]> git.donarmstrong.com Git - infobot.git/blob - src/Modules/Debian.pl
- Another patch from Morten Brix Pedersen <morten@wtf.dk>:
[infobot.git] / src / Modules / Debian.pl
1 #
2 #   Debian.pl: Frontend to debian contents and packages files
3 #      Author: dms
4 #     Version: v0.8 (20000918)
5 #     Created: 20000106
6 #
7
8 package Debian;
9
10 use strict;
11
12 # format: "alias=real".
13 my $announce    = 0;
14 my $defaultdist = "sid";
15 my $refresh = &::getChanConfDefault("debianRefreshInterval",7)
16                         * 60 * 60 * 24;
17 my $debug       = 0;
18 my $debian_dir  = "$::bot_state_dir/debian";
19
20 ### ... old
21 #my %dists      = (
22 #       "sid"           => "unstable",
23 #       "woody"         => "testing",   # new since 20001219.
24 #       "potato"        => "stable",
25 #       "incoming"      => "incoming",
26 #);
27
28 ### new... the right way.
29 my %dists       = (
30         "unstable"      => "sid",
31         "testing"       => "woody",     # new since 20001219.
32         "stable"        => "potato",
33         "incoming"      => "incoming",
34 );
35
36 my %urlcontents = (
37         "Contents-##DIST-i386.gz" =>
38                 "ftp://ftp.us.debian.org".
39                 "/debian/dists/##DIST/Contents-i386.gz",
40         "Contents-##DIST-i386-non-US.gz" =>
41                 "ftp://non-us.debian.org".
42                 "/debian-non-US/dists/##DIST/non-US/Contents-i386.gz",
43 );
44
45 my %urlpackages = (
46         "Packages-##DIST-main-i386.gz" =>
47                 "ftp://ftp.us.debian.org".
48                 "/debian/dists/##DIST/main/binary-i386/Packages.gz",
49         "Packages-##DIST-contrib-i386.gz" =>
50                 "ftp://ftp.us.debian.org".
51                 "/debian/dists/##DIST/contrib/binary-i386/Packages.gz",
52         "Packages-##DIST-non-free-i386.gz" =>
53                 "ftp://ftp.us.debian.org".
54                 "/debian/dists/##DIST/non-free/binary-i386/Packages.gz",
55
56         "Packages-##DIST-non-US-main-i386.gz" =>
57                 "ftp://non-us.debian.org".
58                 "/debian-non-US/dists/##DIST/non-US/main/binary-i386/Packages.gz",
59         "Packages-##DIST-non-US-contrib-i386.gz" =>
60                 "ftp://non-us.debian.org".
61                 "/debian-non-US/dists/##DIST/non-US/contrib/binary-i386/Packages.gz",
62         "Packages-##DIST-non-US-non-free-i386.gz" =>
63                 "ftp://non-us.debian.org".
64                 "/debian-non-US/dists/##DIST/non-US/non-free/binary-i386/Packages.gz",
65 );
66
67 #####################
68 ### COMMON FUNCTION....
69 #######################
70
71 ####
72 # Usage: &DebianDownload($dist, %hash);
73 sub DebianDownload {
74     my ($dist, %urls)   = @_;
75     my $bad     = 0;
76     my $good    = 0;
77
78     if (! -d $debian_dir) {
79         &::status("Debian: creating debian dir.");
80         mkdir($debian_dir, 0755);
81     }
82
83     # fe dists.
84     # Download the files.
85     my $file;
86     foreach $file (keys %urls) {
87         my $url = $urls{$file};
88         $url  =~ s/##DIST/$dist/g;
89         $file =~ s/##DIST/$dist/g;
90         my $update = 0;
91
92         if ( -f $file ) {
93             my $last_refresh = (stat $file)[9];
94             $update++ if (time() - $last_refresh > $refresh);
95         } else {
96             $update++;
97         }
98
99         next unless ($update);
100
101         &::DEBUG("announce == $announce.") if ($debug);
102         if ($good + $bad == 0 and !$announce) {
103             &::status("Debian: Downloading files for '$dist'.");
104             &::msg($::who, "Updating debian files... please wait.");
105             $announce++;
106         }
107
108         if (exists $::debian{$url}) {
109             &::DEBUG("2: ".(time - $::debian{$url})." <= $refresh") if ($debug);
110             next if (time() - $::debian{$url} <= $refresh);
111             &::DEBUG("stale for url $url; updating!") if ($debug);
112         }
113
114         if ($url =~ /^ftp:\/\/(.*?)\/(\S+)\/(\S+)$/) {
115             my ($host,$path,$thisfile) = ($1,$2,$3);
116
117 ### HACK 1
118 #           if ($file =~ /Contents-woody-i386-non-US/) {
119 #               &::DEBUG("Skipping Contents-woody-i386-non-US.");
120 #               $file =~ s/woody/potato/;
121 #               $path =~ s/woody/potato/;
122 #               next;
123 #           }
124
125             if (!&::ftpGet($host,$path,$thisfile,$file)) {
126                 &::WARN("deb: down: $file == BAD.");
127                 $bad++;
128                 next;
129             }
130
131             if (! -f $file) {
132                 &::WARN("deb: down: ftpGet: !file");
133                 $bad++;
134                 next;
135             }
136
137 ### HACK2
138 #           if ($file =~ /Contents-potato-i386-non-US/) {
139 #               &::DEBUG("hack: using potato's non-US contents for woody.");
140 #               system("cp debian/Contents-potato-i386-non-US.gz debian/Contents-woody-i386-non-US.gz");
141 #           }
142
143             &::DEBUG("deb: download: good.") if ($debug);
144             $good++;
145         } else {
146             &::ERROR("Debian: invalid format of url => ($url).");
147             $bad++;
148             next;
149         }
150     }
151
152     # ok... lets just run this.
153     &::miscCheck();
154
155     if ($good) {
156         &generateIndex($dist);
157         return 1;
158     } else {
159         return -1 unless ($bad);        # no download.
160         &::DEBUG("DD: !good and bad($bad). :(");
161         return 0;
162     }
163 }
164
165 ###########################
166 # DEBIAN CONTENTS SEARCH FUNCTIONS.
167 ########
168
169 ####
170 # Usage: &searchContents($query);
171 sub searchContents {
172     my ($dist, $query)  = &getDistroFromStr($_[0]);
173     &::status("Debian: Contents search for '$query' in '$dist'.");
174     my $dccsend = 0;
175
176     $dccsend++          if ($query =~ s/^dcc\s+//i);
177
178     $query =~ s/\\([\^\$])/$1/g;        # hrm?
179     $query =~ s/^\s+|\s+$//g;
180
181     if (!&::validExec($query)) {
182         &::msg($::who, "search string looks fuzzy.");
183         return;
184     }
185
186     if ($dist eq "incoming") {          # nothing yet.
187         &::DEBUG("sC: dist = 'incoming'. no contents yet.");
188         return;
189     } else {
190         my %urls = &fixDist($dist, %urlcontents);
191         # download contents file.
192         &::DEBUG("deb: download 1.") if ($debug);
193         if (!&DebianDownload($dist, %urls)) {
194             &::WARN("Debian: could not download files.");
195         }
196     }
197
198     # start of search.
199     my $start_time = &::timeget();
200
201     my $found   = 0;
202     my $front   = 0;
203     my %contents;
204     my $grepRE;
205     ### TODO: search properly if /usr/bin/blah is done.
206     if ($query =~ s/\$$//) {
207         &::DEBUG("deb: search-regex found.") if ($debug);
208         $grepRE = "$query\[ \t]";
209     } elsif ($query =~ s/^\^//) {
210         &::DEBUG("deb: front marker regex found.") if ($debug);
211         $front = 1;
212         $grepRE = $query;
213     } else {
214         $grepRE = "$query*\[ \t]";
215     }
216
217     # fix up grepRE for "*".
218     $grepRE =~ s/\*/.*/g;
219
220     my @files;
221     foreach (keys %urlcontents) {
222         s/##DIST/$dist/g;
223
224         next unless ( -f "$debian_dir/$_" );
225         push(@files, "$debian_dir/$_");
226     }
227
228     if (!scalar @files) {
229         &::ERROR("sC: no files?");
230         &::msg($::who, "failed.");
231         return;
232     }
233
234     my $files = join(' ', @files);
235
236     my $regex   = $query;
237     $regex      =~ s/\./\\./g;
238     $regex      =~ s/\*/\\S*/g;
239     $regex      =~ s/\?/./g;
240
241     open(IN,"zegrep -h '$grepRE' $files |");
242     # wonderful abuse of last and next and unless ;)
243     while (<IN>) {
244         last if ($found > 100);
245
246         next unless if (/^\.?\/?(.*?)[\t\s]+(\S+)\n$/);
247
248         my ($file,$package) = ("/".$1,$2);
249         if ($query =~ /[\/\*\\]/) {
250             next unless (eval { $file =~ /$regex/ });
251             return unless &checkEval($@);
252         } else {
253             my ($basename) = $file =~ /^.*\/(.*)$/;
254             next unless (eval { $basename =~ /$regex/ });
255             return unless &checkEval($@);
256         }
257         next if ($query !~ /\.\d\.gz/ and $file =~ /\/man\//);
258         next if ($front and eval { $file !~ /^\/$query/ });
259         return unless &checkEval($@);
260
261         $contents{$package}{$file} = 1;
262         $found++;
263     }
264     close IN;
265
266     my $pkg;
267
268     ### send results with dcc.
269     if ($dccsend) {
270         if (exists $::dcc{'SEND'}{$::who}) {
271             &::msg($::who, "DCC already active!");
272             return;
273         }
274
275         if (!scalar %contents) {
276             &::msg($::who,"search returned no results.");
277             return;
278         }
279
280         my $file = "$::param{tempDir}/$::who.txt";
281         if (!open OUT, ">$file") {
282             &::ERROR("Debian: cannot write file for dcc send.");
283             return;
284         }
285
286         foreach $pkg (keys %contents) {
287             foreach (keys %{ $contents{$pkg} }) {
288                 # TODO: correct padding.
289                 print OUT "$_\t\t\t$pkg\n";
290             }
291         }
292         close OUT;
293
294         &::shmWrite($::shm, "DCC SEND $::who $file");
295
296         return;
297     }
298
299     &::status("Debian: $found contents results found.");
300
301     my @list;
302     foreach $pkg (keys %contents) {
303         my @tmplist = &::fixFileList(keys %{ $contents{$pkg} });
304         my @sublist = sort { length $a <=> length $b } @tmplist;
305
306         pop @sublist while (scalar @sublist > 3);
307
308         $pkg =~ s/\,/\037\,\037/g;      # underline ','.
309         push(@list, "(". join(', ',@sublist) .") in $pkg");
310     }
311     # sort the total list from shortest to longest...
312     @list = sort { length $a <=> length $b } @list;
313
314     # show how long it took.
315     my $delta_time = &::timedelta($start_time);
316     &::status(sprintf("Debian: %.02f sec to complete query.", $delta_time)) if ($delta_time > 0);
317
318     my $prefix = "Debian Search of '$query' ";
319     if (scalar @list) { # @list.
320         &::pSReply( &::formListReply(0, $prefix, @list) );
321
322     } else {            # !@list.
323         &::DEBUG("deb: ok, !\@list, searching desc for '$query'.") if ($debug);
324         my @list = &searchDesc($query);
325
326         if (!scalar @list) {
327             my $prefix = "Debian Package/File/Desc Search of '$query' ";
328             &::pSReply( &::formListReply(0, $prefix, ) );
329
330         } elsif (scalar @list == 1) {   # list = 1.
331             &::DEBUG("deb: list == 1; showing package info of '$list[0]'.");
332             &infoPackages("info", $list[0]);
333
334         } else {                                # list > 1.
335             my $prefix = "Debian Desc Search of '$query' ";
336             &::pSReply( &::formListReply(0, $prefix, @list) );
337         }
338     }
339 }
340
341 ####
342 # Usage: &searchAuthor($query);
343 sub searchAuthor {
344     my ($dist, $query)  = &getDistroFromStr($_[0]);
345     &::DEBUG("deb: searchAuthor: dist => '$dist', query => '$query'.") if ($debug);
346     $query =~ s/^\s+|\s+$//g;
347
348     # start of search.
349     my $start_time = &::timeget();
350     &::status("Debian: starting author search.");
351
352     my $files;
353     my ($bad,$good) = (0,0);
354     my %urls = %urlpackages;
355
356     foreach (keys %urlpackages) {
357         s/##DIST/$dist/g;
358
359         if (! -f "$debian_dir/$_" ) {
360             $bad++;
361             next;
362         }
363
364         $good++;
365         $files .= " ".$_;
366     }
367
368     &::DEBUG("deb: good = $good, bad = $bad...") if ($debug);
369
370     if ($good == 0 and $bad != 0) {
371         my %urls = &fixDist($dist, %urlpackages);
372         &::DEBUG("deb: download 2.");
373
374         if (!&DebianDownload($dist, %urls)) {
375             &::ERROR("Debian(sA): could not download files.");
376             return;
377         }
378     }
379
380     my (%maint, %pkg, $package);
381     open(IN,"zegrep -h '^Package|^Maintainer' $files |");
382     while (<IN>) {
383         if (/^Package: (\S+)$/) {
384             $package = $1;
385
386         } elsif (/^Maintainer: (.*) \<(\S+)\>$/) {
387             my($name,$email) = ($1,$2);
388             if ($package eq "") {
389                 &::DEBUG("deb: sA: package == NULL.");
390                 next;
391             }
392             $maint{$name}{$email} = 1;
393             $pkg{$name}{$package} = 1;
394             $package = "";
395
396         } else {
397             &::WARN("debian: invalid line: '$_' (1).");
398         }
399     }
400     close IN;
401
402     my %hash;
403     # TODO: can we use 'map' here?
404     foreach (grep /\Q$query\E/i, keys %maint) {
405         $hash{$_} = 1;
406     }
407
408     # TODO: should we only search email if '@' is used?
409     if (scalar keys %hash < 15) {
410         my $name;
411
412         foreach $name (keys %maint) {
413             my $email;
414
415             foreach $email (keys %{ $maint{$name} }) {
416                 next unless ($email =~ /\Q$query\E/i);
417                 next if (exists $hash{$name});
418                 $hash{$name} = 1;
419             }
420         }
421     }
422
423     my @list = keys %hash;
424     if (scalar @list != 1) {
425         my $prefix = "Debian Author Search of '$query' ";
426         &::pSReply( &::formListReply(0, $prefix, @list) );
427         return 1;
428     }
429
430     &::DEBUG("deb: showing all packages by '$list[0]'...") if ($debug);
431
432     my @pkg = sort keys %{ $pkg{$list[0]} };
433
434     # show how long it took.
435     my $delta_time = &::timedelta($start_time);
436     &::status(sprintf("Debian: %.02f sec to complete query.", $delta_time)) if ($delta_time > 0);
437
438     my $email   = join(', ', keys %{ $maint{$list[0]} });
439     my $prefix  = "Debian Packages by $list[0] \002<\002$email\002>\002 ";
440     &::pSReply( &::formListReply(0, $prefix, @pkg) );
441 }
442
443 ####
444 # Usage: &searchDesc($query);
445 sub searchDesc {
446     my ($dist, $query)  = &getDistroFromStr($_[0]);
447     &::DEBUG("deb: searchDesc: dist => '$dist', query => '$query'.") if ($debug);
448     $query =~ s/^\s+|\s+$//g;
449
450     # start of search.
451     my $start_time = &::timeget();
452     &::status("Debian: starting desc search.");
453
454     my $files;
455     my ($bad,$good) = (0,0);
456     my %urls = %urlpackages;
457
458     foreach (keys %urlpackages) {
459         s/##DIST/$dist/g;
460
461         if (! -f "$debian_dir/$_" ) {
462             $bad++;
463             next;
464         }
465
466         $good++;
467         $files .= " $debian_dir/$_";
468     }
469
470     &::DEBUG("deb(2): good = $good, bad = $bad...") if ($debug);
471
472     if ($good == 0 and $bad != 0) {
473         my %urls = &fixDist($dist, %urlpackages);
474         &::DEBUG("deb: download 2c.") if ($debug);
475
476         if (!&DebianDownload($dist, %urls)) {
477             &::ERROR("deb: sD: could not download files.");
478             return;
479         }
480     }
481
482     my $regex   = $query;
483     $regex      =~ s/\./\\./g;
484     $regex      =~ s/\*/\\S*/g;
485     $regex      =~ s/\?/./g;
486
487     my (%desc, $package);
488     open(IN,"zegrep -h '^Package|^Description' $files |");
489     while (<IN>) {
490         if (/^Package: (\S+)$/) {
491             $package = $1;
492         } elsif (/^Description: (.*)$/) {
493             my $desc = $1;
494             next unless (eval { $desc =~ /$regex/i });
495             return unless &checkEval($@);
496
497             if ($package eq "") {
498                 &::WARN("sD: package == NULL?");
499                 next;
500             }
501
502             $desc{$package} = $desc;
503             $package = "";
504
505         } else {
506             &::WARN("debian: invalid line: '$_'. (2)");
507         }
508     }
509     close IN;
510
511     # show how long it took.
512     my $delta_time = &::timedelta($start_time);
513     &::status(sprintf("Debian: %.02f sec to complete query.", $delta_time)) if ($delta_time > 0);
514
515     return keys %desc;
516 }
517
518 ####
519 # Usage: &generateIncoming();
520 sub generateIncoming {
521     my $pkgfile  = $debian_dir."/Packages-incoming";
522     my $idxfile  = $pkgfile.".idx";
523     my $stale    = 0;
524     $stale++ if (&::isStale($pkgfile.".gz", $refresh));
525     $stale++ if (&::isStale($idxfile, $refresh));
526     &::DEBUG("deb: gI: stale => '$stale'.") if ($debug);
527     return 0 unless ($stale);
528
529     ### STATIC URL.
530     my %ftp = &::ftpList("llug.sep.bnl.gov", "/pub/debian/Incoming/");
531
532     if (!open PKG, ">$pkgfile") {
533         &::ERROR("cannot write to pkg $pkgfile.");
534         return 0;
535     }
536     if (!open IDX, ">$idxfile") {
537         &::ERROR("cannot write to idx $idxfile.");
538         return 0;
539     }
540
541     print IDX "*$pkgfile.gz\n";
542     my $file;
543     foreach $file (sort keys %ftp) {
544         next unless ($file =~ /deb$/);
545
546         if ($file =~ /^(\S+)\_(\S+)\_(\S+)\.deb$/) {
547             print IDX "$1\n";
548             print PKG "Package: $1\n";
549             print PKG "Version: $2\n";
550             print PKG "Architecture: ", (defined $4) ? $4 : "all", "\n";
551         }
552         print PKG "Filename: $file\n";
553         print PKG "Size: $ftp{$file}\n";
554         print PKG "\n";
555     }
556     close IDX;
557     close PKG;
558
559     system("gzip -9fv $pkgfile");       # lame fix.
560
561     &::status("Debian: generateIncoming() complete.");
562 }
563
564
565 ##############################
566 # DEBIAN PACKAGE INFO FUNCTIONS.
567 #########
568
569 # Usage: &getPackageInfo($query,$file);
570 sub getPackageInfo {
571     my ($package, $file) = @_;
572
573     if (! -f $file) {
574         &::status("gPI: file $file does not exist?");
575         return 'NULL';
576     }
577
578     my $found = 0;
579     my (%pkg, $pkg);
580
581     open(IN, "zcat $file 2>&1 |");
582
583     my $done = 0;
584     while (!eof IN) {
585         $_ = <IN>;
586
587         next if (/^ \S+/);      # package long description.
588
589         # package line.
590         if (/^Package: (.*)\n$/) {
591             $pkg = $1;
592             if ($pkg =~ /^$package$/i) {
593                 $found++;       # we can use pkg{'package'} instead.
594                 $pkg{'package'} = $pkg;
595             }
596
597             next;
598         }
599
600         if ($found) {
601             chop;
602
603             if (/^Version: (.*)$/) {
604                 $pkg{'version'}         = $1;
605             } elsif (/^Priority: (.*)$/) {
606                 $pkg{'priority'}        = $1;
607             } elsif (/^Section: (.*)$/) {
608                 $pkg{'section'}         = $1;
609             } elsif (/^Size: (.*)$/) {
610                 $pkg{'size'}            = $1;
611             } elsif (/^Installed-Size: (.*)$/i) {
612                 $pkg{'installed'}       = $1;
613             } elsif (/^Description: (.*)$/) {
614                 $pkg{'desc'}            = $1;
615             } elsif (/^Filename: (.*)$/) {
616                 $pkg{'find'}            = $1;
617             } elsif (/^Pre-Depends: (.*)$/) {
618                 $pkg{'depends'}         = "pre-depends on $1";
619             } elsif (/^Depends: (.*)$/) {
620                 if (exists $pkg{'depends'}) {
621                     $pkg{'depends'} .= "; depends on $1";
622                 } else {
623                     $pkg{'depends'} = "depends on $1";
624                 }
625             } elsif (/^Maintainer: (.*)$/) {
626                 $pkg{'maint'} = $1;
627             } elsif (/^Provides: (.*)$/) {
628                 $pkg{'provides'} = $1;
629             } elsif (/^Suggests: (.*)$/) {
630                 $pkg{'suggests'} = $1;
631             } elsif (/^Conflicts: (.*)$/) {
632                 $pkg{'conflicts'} = $1;
633             }
634
635 ###         &::DEBUG("=> '$_'.");
636         }
637
638         # blank line.
639         if (/^$/) {
640             undef $pkg;
641             last if ($found);
642             next;
643         }
644
645         next if (defined $pkg);
646     }
647
648     close IN;
649
650     %pkg;
651 }
652
653 # Usage: &infoPackages($query,$package);
654 sub infoPackages {
655     my ($query,$dist,$package) = ($_[0], &getDistroFromStr($_[1]));
656
657     &::status("Debian: Searching for package '$package' in '$dist'.");
658
659     # download packages file.
660     # hrm...
661     my %urls = &fixDist($dist, %urlpackages);
662     if ($dist ne "incoming") {
663         &::DEBUG("deb: download 3.") if ($debug);
664
665         if (!&DebianDownload($dist, %urls)) {   # no good download.
666             &::WARN("Debian(iP): could not download ANY files.");
667         }
668     }
669
670     # check if the package is valid.
671     my $incoming = 0;
672     my @files = &validPackage($package, $dist);
673     if (!scalar @files) {
674         &::status("Debian: no valid package found; checking incoming.");
675         @files = &validPackage($package, "incoming");
676         if (scalar @files) {
677             &::status("Debian: cool, it exists in incoming.");
678             $incoming++;
679         } else {
680             &::msg($::who, "Package '$package' does not exist.");
681             return 0;
682         }
683     }
684
685     if (scalar @files > 1) {
686         &::WARN("same package in more than one file; random.");
687         &::DEBUG("THIS SHOULD BE FIXED SOMEHOW!!!");
688         $files[0] = &::getRandom(@files);
689     }
690
691     if (! -f $files[0]) {
692         &::WARN("files[0] ($files[0]) doesn't exist.");
693         &::msg($::who, "WARNING: $files[0] does not exist? FIXME");
694         return 'NULL';
695     }
696
697     ### TODO: if specific package is requested, note down that a version
698     ###         exists in incoming.
699
700     my $found = 0;
701     my $file = $files[0];
702     my ($pkg);
703
704     ### TODO: use fe, dump to a hash. if only one version of the package
705     ###         exists. do as normal otherwise list all versions.
706     if (! -f $file) {
707         &::ERROR("D:iP: file '$file' DOES NOT EXIST!!! should never happen.");
708         return 0;
709     }
710     my %pkg = &getPackageInfo($package, $file);
711
712     # 'fm'-like output.
713     if ($query eq "info") {
714         if (scalar keys %pkg > 5) {
715             $pkg{'info'}  = "\002(\002". $pkg{'desc'} ."\002)\002";
716             $pkg{'info'} .= ", section ".$pkg{'section'};
717             $pkg{'info'} .= ", is ".$pkg{'priority'};
718 #           $pkg{'info'} .= ". Version: \002$pkg{'version'}\002";
719             $pkg{'info'} .= ". Version: \002$pkg{'version'}\002 ($dist)";
720             $pkg{'info'} .= ", Packaged size: \002". int($pkg{'size'}/1024) ."\002 kB";
721             $pkg{'info'} .= ", Installed size: \002$pkg{'installed'}\002 kB";
722
723             if ($incoming) {
724                 &::status("iP: info requested and pkg is in incoming, too.");
725                 my %incpkg = &getPackageInfo($query, $debian_dir ."/Packages-incoming");
726
727                 if (scalar keys %incpkg) {
728                    $pkg{'info'} .= ". Is in incoming ($incpkg{'file'}).";
729                 } else {
730                     &::ERROR("iP: pkg $query is in incoming but we couldn't get any info?");
731                 }
732             }
733         } else {
734             &::DEBUG("deb: running debianCheck() due to problems (".scalar(keys %pkg).").");
735             &debianCheck();
736             &::DEBUG("deb: end of debianCheck()");
737
738             &::msg($::who,"Debian: Package appears to exist but I could not retrieve info about it...");
739             return;
740         }
741     } 
742
743     if ($dist eq "incoming") {
744         $pkg{'info'} .= "Version: \002$pkg{'version'}\002";
745         $pkg{'info'} .= ", Packaged size: \002". int($pkg{'size'}/1024) ."\002 kB";
746         $pkg{'info'} .= ", is in incoming!!!";
747     }
748
749     if (!exists $pkg{$query}) {
750         if ($query eq "suggests") {
751             $pkg{$query} = "has no suggestions";
752         } elsif ($query eq "conflicts") {
753             $pkg{$query} = "does not conflict with any other package";
754         } elsif ($query eq "depends") {
755             $pkg{$query} = "does not depend on anything";
756         } elsif ($query eq "maint") {
757             $pkg{$query} = "has no maintainer";
758         } else {
759             $pkg{$query} = "has nothing about $query";
760         }
761     }
762
763     &::pSReply("$package: $pkg{$query}");
764 }
765
766 # Usage: &infoStats($dist);
767 sub infoStats {
768     my ($dist)  = @_;
769     $dist       = &getDistro($dist);
770     return unless (defined $dist);
771
772     &::DEBUG("deb: infoS: dist => '$dist'.");
773
774     # download packages file if needed.
775     my %urls = &fixDist($dist, %urlpackages);
776     &::DEBUG("deb: download 4.");
777     if (!&DebianDownload($dist, %urls)) {
778         &::WARN("Debian(iS): could not download ANY files.");
779         &::msg($::who, "Debian(iS): internal error.");
780         return;
781     }
782
783     my %stats;
784     my %total;
785     my $file;
786     foreach $file (keys %urlpackages) {
787         $file =~ s/##DIST/$dist/g;      # won't work for incoming.
788         &::DEBUG("deb: file => '$file'.");
789         if (exists $stats{$file}{'count'}) {
790             &::DEBUG("deb: hrm... duplicate open with $file???");
791             next;
792         }
793
794         open(IN, "zcat $file 2>&1 |");
795
796         if (! -e $file) {
797             &::DEBUG("deb: iS: $file does not exist.");
798             next;
799         }
800
801         while (!eof IN) {
802             $_ = <IN>;
803
804             next if (/^ \S+/);  # package long description.
805
806             if (/^Package: (.*)\n$/) {          # counter.
807                 $stats{$file}{'count'}++;
808                 $total{'count'}++;
809             } elsif (/^Maintainer: .* <(\S+)>$/) {
810                 $stats{$file}{'maint'}{$1}++;
811                 $total{'maint'}{$1}++;
812             } elsif (/^Size: (.*)$/) {          # compressed size.
813                 $stats{$file}{'csize'}  += $1;
814                 $total{'csize'}         += $1;
815             } elsif (/^i.*size: (.*)$/i) {      # installed size.
816                 $stats{$file}{'isize'}  += $1;
817                 $total{'isize'}         += $1;
818             }
819
820 ###         &::DEBUG("=> '$_'.");
821         }
822         close IN;
823     }
824
825     ### TODO: don't count ppl with multiple email addresses.
826
827     &::pSReply(
828         "Debian Distro Stats on $dist... ".
829         "\002$total{'count'}\002 packages, ".
830         "\002".scalar(keys %{ $total{'maint'} })."\002 maintainers, ".
831         "\002". int($total{'isize'}/1024)."\002 MB installed size, ".
832         "\002". int($total{'csize'}/1024/1024)."\002 MB compressed size."
833     );
834
835 ### TODO: do individual stats? if so, we need _another_ arg.
836 #    foreach $file (keys %stats) {
837 #       foreach (keys %{ $stats{$file} }) {
838 #           &::DEBUG("  '$file' '$_' '$stats{$file}{$_}'.");
839 #       }
840 #    }
841
842     return;
843 }
844
845 ###
846 # HELPER FUNCTIONS FOR INFOPACKAGES...
847 ###
848
849 # Usage: &generateIndex();
850 sub generateIndex {
851     my (@dists) = @_;
852     &::DEBUG("D: generateIndex($dists[0]) called!");
853     if (!scalar @dists or $dists[0] eq '') {
854         &::ERROR("gI: no dists to generate index.");
855         return 1;
856     }
857
858     foreach (@dists) {
859         my $dist = &getDistro($_); # incase the alias is returned, possible?
860         my $idx  = $debian_dir."/Packages-$dist.idx";
861
862         # TODO: check if any of the Packages file have been updated then
863         #       regenerate it, even if it's not stale.
864         # TODO: also, regenerate the index if the packages file is newer
865         #       than the index.
866         next unless (&::isStale($idx, $refresh));
867
868         if (/^incoming$/i) {
869             &::DEBUG("deb: gIndex: calling generateIncoming()!");
870             &generateIncoming();
871             next;
872         }
873
874         if (/^woody$/i) {
875             &::DEBUG("deb: Copying old index of woody to -old");
876             system("cp $idx $idx-old");
877         }
878
879         &::DEBUG("deb: gIndex: calling DebianDownload($dist, ...).") if ($debug);
880         &DebianDownload($dist, &fixDist($dist, %urlpackages) );
881
882         &::status("Debian: generating index for '$dist'.");
883         if (!open OUT, ">$idx") {
884             &::ERROR("cannot write to $idx.");
885             return 0;
886         }
887
888         my $packages;
889         foreach $packages (keys %urlpackages) {
890             $packages =~ s/##DIST/$dist/;
891             $packages =  "$debian_dir/$packages";
892
893             if (! -e $packages) {
894                 &::ERROR("gIndex: '$packages' does not exist?");
895                 next;
896             }
897
898             print OUT "*$packages\n";
899             open(IN,"zcat $packages |");
900
901             while (<IN>) {
902                 next unless (/^Package: (.*)\n$/);
903                 print OUT $1."\n";
904             }
905             close IN;
906         }
907         close OUT;
908     }
909
910     return 1;
911 }
912
913 # Usage: &validPackage($package, $dist);
914 sub validPackage {
915     my ($package,$dist) = @_;
916     my @files;
917     my $file;
918
919     ### this majorly sucks, we need some standard in place.
920     # why is this needed... need to investigate later.
921     my $olddist = $dist;
922     $dist = &getDistro($dist);
923
924     &::DEBUG("deb: validPackage($package, $dist) called.") if ($debug);
925
926     my $error = 0;
927     while (!open IN, $debian_dir."/Packages-$dist.idx") {
928         if ($error) {
929             &::ERROR("Packages-$dist.idx does not exist (#1).");
930             return;
931         }
932
933         &generateIndex($dist);
934
935         $error++;
936     }
937
938     my $count = 0;
939     while (<IN>) {
940         if (/^\*(.*)\n$/) {
941             $file = $1;
942             next;
943         }
944
945         if (/^\Q$package\E\n$/) {
946             push(@files,$file);
947         }
948         $count++;
949     }
950     close IN;
951
952     &::VERB("vP: scanned $count items in index.",2);
953
954     return @files;
955 }
956
957 sub searchPackage {
958     my ($dist, $query) = &getDistroFromStr($_[0]);
959     my $file = $debian_dir."/Packages-$dist.idx";
960     my @files;
961     my $error   = 0;
962     my $warn    = ($query =~ tr/A-Z/a-z/) ? 1 : 0;
963
964     &::status("Debian: Search package matching '$query' in '$dist'.");
965     unlink $file if ( -z $file );
966
967     while (!open IN, $file) {
968         if ($dist eq "incoming") {
969             &::DEBUG("deb: sP: dist == incoming; calling gI().");
970             &generateIncoming();
971         }
972
973         if ($error) {
974             &::ERROR("could not generate index ($file)!");
975             return;
976         }
977
978         $error++;
979         &::DEBUG("deb: should we be doing this?");
980         &generateIndex(($dist));
981     }
982
983     while (<IN>) {
984         chop;
985
986         if (/^\*(.*)$/) {
987             $file = $1;
988
989             if (&::isStale($file, $refresh)) {
990                 &::DEBUG("deb: STALE $file! regen.") if ($debug);
991                 &generateIndex(($dist));
992 ###             @files = searchPackage("$query $dist");
993                 &::DEBUG("deb: EVIL HACK HACK HACK.") if ($debug);
994                 last;
995             }
996
997             next;
998         }
999
1000         if (/\Q$query\E/) {
1001             push(@files,$_);
1002         }
1003     }
1004     close IN;
1005
1006     if (scalar @files and $warn) {
1007         &::msg($::who, "searching for package name should be fully lowercase!");
1008     }
1009
1010     return @files;
1011 }
1012
1013 sub getDistro {
1014     my $dist = $_[0];
1015
1016     if (!defined $dist or $dist eq "") {
1017         &::DEBUG("deb: gD: dist == NULL; dist = defaultdist.");
1018         $dist = $defaultdist;
1019     }
1020
1021     if ($dist =~ /^(slink|hamm|rex|bo)$/i) {
1022         &::DEBUG("deb: deprecated version ($dist).");
1023         &::msg($::who, "Debian: deprecated distribution version.");
1024         return;
1025     }
1026
1027     if (exists $dists{$dist}) {
1028         &::VERB("gD: returning dists{$dist} ($dists{$dist})",2);
1029         return $dists{$dist};
1030
1031     } else {
1032         if (!grep /^\Q$dist\E$/i, %dists) {
1033             &::msg($::who, "invalid dist '$dist'.");
1034             return;
1035         }
1036
1037         &::VERB("gD: returning $dist (no change or conversion)",2);
1038         return $dist;
1039     }
1040 }
1041
1042 sub getDistroFromStr {
1043     my ($str) = @_;
1044     my $dists   = join '|', %dists;
1045     my $dist    = $defaultdist;
1046
1047     if ($str =~ s/\s+($dists)$//i) {
1048         $dist = &getDistro(lc $1);
1049         $str =~ s/\\+$//;
1050     }
1051     $str =~ s/\\([\$\^])/$1/g;
1052
1053     return($dist,$str);
1054 }
1055
1056 sub fixDist {
1057     my ($dist, %urls) = @_;
1058     my %new;
1059     my ($key,$val);
1060
1061     while (($key,$val) = each %urls) {
1062         $key =~ s/##DIST/$dist/;
1063         $val =~ s/##DIST/$dist/;
1064         ### TODO: what should we do if the sar wasn't done.
1065         $new{$debian_dir."/".$key} = $val;
1066     }
1067
1068     return %new;
1069 }
1070
1071 sub DebianFind {
1072     # HACK! HACK! HACK!
1073     my ($str) = @_;
1074     my ($dist, $query) = &getDistroFromStr($str);
1075     my @results = sort &searchPackage($str);
1076
1077     if (!scalar @results) {
1078         &::Forker("debian", sub { &searchContents($str); } );
1079     } elsif (scalar @results == 1) {
1080         &::status("searchPackage returned one result; getting info of package instead!");
1081         &::Forker("debian", sub { &infoPackages("info", "$results[0] $dist"); } );
1082     } else {
1083         my $prefix = "Debian Package Listing of '$query' ";
1084         &::pSReply( &::formListReply(0, $prefix, @results) );
1085     }
1086 }
1087
1088 sub debianCheck {
1089     my $error   = 0;
1090
1091     &::status("debianCheck() called.");
1092
1093     ### TODO: remove the following loop (check if dir exists before)
1094     while (1) {
1095         last if (opendir(DEBIAN, $debian_dir));
1096
1097         if ($error) {
1098             &::ERROR("dC: cannot opendir debian.");
1099             return;
1100         }
1101
1102         mkdir $debian_dir, 0755;
1103         $error++;
1104     }
1105
1106     my $retval = 0;
1107     my $file;
1108     while (defined($file = readdir DEBIAN)) {
1109         next unless ($file =~ /(gz|bz2)$/);
1110
1111         my $exit = system("gzip -t '$debian_dir/$file'");
1112         next unless ($exit);
1113         &::DEBUG("deb: hmr... => ".(time() - (stat($file))[8])."'.");
1114         next unless (time() - (stat($file))[8] > 3600);
1115
1116         &::DEBUG("deb: dC: exit => '$exit'.");
1117         &::WARN("dC: '$debian_dir/$file' corrupted? deleting!");
1118         unlink $debian_dir."/".$file;
1119         $retval++;
1120     }
1121
1122     return $retval;
1123 }
1124
1125 sub checkEval {
1126     my($str)    = @_;
1127
1128     if ($str) {
1129         &::WARN("cE: $str");
1130         return 0;
1131     } else {
1132         return 1;
1133     }
1134 }
1135
1136 sub searchDescFE {
1137 #    &::DEBUG("deb: FE called for searchDesc");
1138     my ($query) = @_;
1139     my @list = &searchDesc($query);
1140
1141     if (!scalar @list) {
1142         my $prefix = "Debian Desc Search of '$query' ";
1143         &::pSReply( &::formListReply(0, $prefix, ) );
1144     } elsif (scalar @list == 1) {       # list = 1.
1145         &::DEBUG("deb: list == 1; showing package info of '$list[0]'.");
1146         &infoPackages("info", $list[0]);
1147     } else {                            # list > 1.
1148         my $prefix = "Debian Desc Search of '$query' ";
1149         &::pSReply( &::formListReply(0, $prefix, @list) );
1150     }
1151 }
1152
1153 1;