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