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