]> git.deb.at Git - deb/packages.git/blob - cgi-bin/show_package.pl
Begin implementing virtual package support.
[deb/packages.git] / cgi-bin / show_package.pl
1 #!/usr/bin/perl -wT
2 # $Id$
3 # show_package.pl -- CGI interface to show info about a package
4 #
5 # Copyright (C) 1998 James Treacy
6 # Copyright (C) 2000, 2001 Josip Rodin
7 # Copyright (C) 2001 Adam Heath
8 # Copyright (C) 2004 Martin Schulze
9 # Copyright (C) 2004-2006 Frank Lichtenheld
10 # Copyright (C) 2006 Jeroen van Wolffelaar
11 #
12 # use is allowed under the terms of the GNU Public License (GPL)                              
13 # see http://www.fsf.org/copyleft/gpl.html for a copy of the license
14
15 use strict;
16 use lib '../lib';
17 use CGI qw( -oldstyle_urls );
18 use CGI::Carp qw( fatalsToBrowser );
19 use POSIX;
20 use URI::Escape;
21 use HTML::Entities;
22 use DB_File;
23 use Benchmark;
24
25 use Deb::Versions;
26 use Packages::Config qw( $DBDIR $ROOT @SUITES @ARCHIVES @SECTIONS
27                          @ARCHITECTURES %FTP_SITES );
28 use Packages::CGI;
29 use Packages::DB;
30 use Packages::Search qw( :all );
31 use Packages::HTML;
32 use Packages::Page ();
33 use Packages::SrcPage ();
34
35 &Packages::CGI::reset;
36
37 $ENV{PATH} = "/bin:/usr/bin";
38
39 # Read in all the variables set by the form
40 my $input;
41 if ($ARGV[0] && ($ARGV[0] eq 'php')) {
42         $input = new CGI(\*STDIN);
43 } else {
44         $input = new CGI;
45 }
46
47 my $pet0 = new Benchmark;
48 my $tet0 = new Benchmark;
49 # use this to disable debugging in production mode completly
50 my $debug_allowed = 1;
51 my $debug = $debug_allowed && $input->param("debug");
52 $debug = 0 if !defined($debug) || $debug !~ /^\d+$/o;
53 $Packages::CGI::debug = $debug;
54
55 &Packages::Config::init( '../' );
56 &Packages::DB::init();
57
58 if (my $path = $input->param('path')) {
59     my @components = map { lc $_ } split /\//, $path;
60
61     my %SUITES = map { $_ => 1 } @SUITES;
62     my %SECTIONS = map { $_ => 1 } @SECTIONS;
63     my %ARCHIVES = map { $_ => 1 } @ARCHIVES;
64     my %ARCHITECTURES = map { $_ => 1 } @ARCHITECTURES;
65
66     foreach (@components) {
67         if ($SUITES{$_}) {
68             $input->param('suite', $_);
69         } elsif ($SECTIONS{$_}) {
70             $input->param('section', $_);
71         } elsif ($ARCHIVES{$_}) {
72             $input->param('archive', $_);
73         } elsif ($ARCHITECTURES{$_}) {
74             $input->param('arch', $_);
75         } elsif ($_ eq 'source') {
76             $input->param('source', 1);
77         }
78     }
79 }
80
81 my ( $pkg, $suite, @sections, @archs, @archives, $format );
82 my %params_def = ( package => { default => undef, match => '^([a-z0-9.+-]+)$',
83                                 var => \$pkg },
84                    suite => { default => undef, match => '^(\w+)$',
85                               var => \$suite },
86                    archive => { default => 'all', match => '^(\w+)$',
87                                 array => ',', var => \@archives,
88                                 replace => { all => [qw(us security non-US)] } },
89                    section => { default => 'all', match => '^(\w+)$',
90                                 array => ',', var => \@sections,
91                                 replace => { all => \@SECTIONS } },
92                    arch => { default => 'any', match => '^(\w+)$',
93                              array => ',', var => \@archs,
94                              replace => { any => \@ARCHITECTURES } },
95                    format => { default => 'html', match => '^(\w+)$',
96                                var => \$format },
97                    source => { default => 0, match => '^(\d+)$' },
98                    );
99 my %opts;
100 my %params = Packages::Search::parse_params( $input, \%params_def, \%opts );
101
102 #XXX: Don't use alternative output formats yet
103 $format = 'html';
104 if ($format eq 'html') {
105     print $input->header;
106 }
107
108 if ($params{errors}{package}) {
109     fatal_error( "package not valid or not specified" );
110     $pkg = '';
111 }
112 if ($params{errors}{suite}) {
113     fatal_error( "suite not valid or not specified" );
114     $suite = '';
115 }
116
117 $opts{h_suites} =   { $suite => 1 };
118 $opts{h_archs} =    { map { $_ => 1 } @archs };
119 $opts{h_sections} = { map { $_ => 1 } @sections };
120 $opts{h_archives} = { map { $_ => 1 } @archives };;
121
122 my $DL_URL = "$pkg/download";
123 my $FILELIST_URL = "$pkg/files";
124
125 our (%packages_all, %sources_all);
126 my (@results, @non_results);
127 my $page = $opts{source} ?
128     new Packages::SrcPage( $pkg ) :
129     new Packages::Page( $pkg );
130 my $package_page = "";
131 my ($short_desc, $version, $archive, $section, $subsection) = ("")x5;
132
133 sub gettext { return $_[0]; };
134
135 my $st0 = new Benchmark;
136 unless (@Packages::CGI::fatal_errors) {
137     tie %packages_all, 'DB_File', "$DBDIR/packages_all_$suite.db",
138     O_RDONLY, 0666, $DB_BTREE
139         or die "couldn't tie DB $DBDIR/packages_all_$suite.db: $!";
140     tie %sources_all, 'DB_File', "$DBDIR/sources_all_$suite.db",
141     O_RDONLY, 0666, $DB_BTREE
142         or die "couldn't tie DB $DBDIR/sources_all_$suite.db: $!";
143
144     unless ($opts{source}) {
145         read_entry_all( \%packages, $pkg, \@results, \@non_results, \%opts );
146
147         unless (@results || @non_results ) {
148             fatal_error( "No such package".
149                          "{insert link to search page with substring search}" );
150         } else {
151             unless (@results) {
152                 fatal_error( "Package not available in this suite" );
153             } else {
154                 for my $entry (@results) {
155                     debug( join(":", @$entry), 1 );
156                     my (undef, $archive, undef, $arch, $section, $subsection,
157                         $priority, $version, @provided_by) = @$entry;
158                     
159                     if ($arch ne 'virtual') {
160                         my %data = split /\000/, $packages_all{"$pkg $arch $version"};
161                         $data{package} = $pkg;
162                         $data{architecture} = $arch;
163                         $data{version} = $version;
164                         $page->merge_package(\%data) or debug( "Merging $pkg $arch $version FAILED", 2 );
165                     } else {
166                         $page->add_provided_by(\@provided_by);
167                     }
168                 }
169
170                 $version = $page->{newest};
171                 my $source = $page->get_newest( 'source' );
172                 $archive = $page->get_newest( 'archive' );
173                 debug( "find source package: source=$source", 1);
174                 my $src_data = $sources_all{"$archive $suite $source"};
175                 $page->add_src_data( $source, $src_data )
176                     if $src_data;
177
178                 my $st1 = new Benchmark;
179                 my $std = timediff($st1, $st0);
180                 debug( "Data search and merging took ".timestr($std) );
181
182                 my $encodedpkg = uri_escape( $pkg );
183                 my ($v_str, $v_str_arch, $v_str_arr) = $page->get_version_string();
184                 my $did = $page->get_newest( 'description' );
185                 $section = $page->get_newest( 'section' );
186                 $subsection = $page->get_newest( 'subsection' );
187                 my $filenames = $page->get_arch_field( 'filename' );
188                 my $file_md5sums = $page->get_arch_field( 'md5sum' );
189                 my $archives = $page->get_arch_field( 'archive' );
190                 my $sizes_inst = $page->get_arch_field( 'installed-size' );
191                 my $sizes_deb = $page->get_arch_field( 'size' );
192                 my @archs = sort $page->get_architectures;
193
194                 # process description
195                 #
196                 my $desc = $descriptions{$did};
197                 $short_desc = encode_entities( $1, "<>&\"" )
198                     if $desc =~ s/^(.*)$//m;
199                 my $long_desc = encode_entities( $desc, "<>&\"" );
200                 
201                 $long_desc =~ s,((ftp|http|https)://[\S~-]+?/?)((\&gt\;)?[)]?[']?[:.\,]?(\s|$)),<a href=\"$1\">$1</a>$3,go; # syntax highlighting -> '];
202                 $long_desc =~ s/\A //o;
203                 $long_desc =~ s/\n /\n/sgo;
204                 $long_desc =~ s/\n.\n/\n<p>\n/go;
205                 $long_desc =~ s/(((\n|\A) [^\n]*)+)/\n<pre>$1\n<\/pre>/sgo;
206 #           $long_desc = conv_desc( $lang, $long_desc );
207 #           $short_desc = conv_desc( $lang, $short_desc );
208
209                 my %all_suites;
210                 foreach (@results, @non_results) {
211                     my $a = $_->[1];
212                     my $s = $_->[2];
213                     if ($a =~ /^(?:us|security|non-US)$/o) {
214                         $all_suites{$s}++;
215                     } else {
216                         $all_suites{"$s/$a"}++;
217                     }
218                 }
219                 foreach (suites_sort(keys %all_suites)) {
220                     if (("$suite/$archive" eq $_)
221                         || (!$all_suites{"$suite/$archive"} && ($suite eq $_))) {
222                         $package_page .= "[ <strong>$_</strong> ] ";
223                     } else {
224                         $package_page .=
225                             "[ <a href=\"$ROOT/$_/".uri_escape($pkg)."\">$_</a> ] ";
226                     }
227                 }
228                 $package_page .= '<br>';
229
230                 $package_page .= simple_menu( [ gettext( "Distribution:" ),
231                                                 gettext( "Overview over this suite" ),
232                                                 "$ROOT/$suite/",
233                                                 $suite ],
234                                               [ gettext( "Section:" ),
235                                                 gettext( "All packages in this section" ),
236                                                 "$ROOT/$suite/$subsection/",
237                                                 $subsection ],
238                                               );
239
240                 my $title .= sprintf( gettext( "Package: %s (%s)" ), $pkg, $v_str );
241                 $title .=  " ".marker( $archive ) if $archive ne 'us';
242                 $title .=  " ".marker( $subsection ) if $subsection eq 'non-US'
243                     and $archive ne 'non-US'; # non-US/security
244                 $title .=  " ".marker( $section ) if $section ne 'main';
245                 $package_page .= title( $title );
246                 
247                 $package_page .= "<h2>".gettext( "Versions:" )." $v_str_arch</h2>\n" 
248                     unless $version eq $v_str;
249                 
250                 if ($suite eq "experimental") {
251                     $package_page .= note( gettext( "Experimental package"),
252                                            gettext( "Warning: This package is from the <span class=\"pred\">experimental</span> distribution. That means it is likely unstable or buggy, and it may even cause data loss. If you ignore this warning and install it nevertheless, you do it on your own risk.")."</p><p>".
253                                            gettext( "Users of experimental packages are encouraged to contact the package maintainers directly in case of problems." )
254                                            );
255                 }
256                 if ($subsection eq "debian-installer") {
257                     note( gettext( "debian-installer udeb package"),
258                           gettext( "Warning: This package is intended for the use in building <a href=\"http://www.debian.org/devel/debian-installer\">debian-installer</a> images only. Do not install it on a normal Debian system." )
259                           );
260                 }
261                 $package_page .= pdesc( $short_desc, $long_desc );
262
263                 #
264                 # display dependencies
265                 #
266                 my $dep_list;
267                 $dep_list = print_deps( \%packages, \%opts, $pkg,
268                                         $page->get_dep_field('depends'),
269                                         'depends' );
270                 $dep_list .= print_deps( \%packages, \%opts, $pkg,
271                                          $page->get_dep_field('recommends'),
272                                          'recommends' );
273                 $dep_list .= print_deps( \%packages, \%opts, $pkg,
274                                          $page->get_dep_field('suggests'),
275                                          'suggests' );
276
277                 if ( $dep_list ) {
278                     $package_page .= "<div id=\"pdeps\">\n";
279                     $package_page .= sprintf( "<h2>".gettext( "Other Packages Related to %s" )."</h2>\n", $pkg );
280                     if ($suite eq "experimental") {
281                         note( gettext( "Note that the \"<span class=\"pred\">experimental</span>\" distribution is not self-contained; missing dependencies are likely found in the \"<a href=\"/unstable/\">unstable</a>\" distribution." ) );
282                     }
283                     
284                     $package_page .= pdeplegend( [ 'dep',  gettext( 'depends' ) ],
285                                                  [ 'rec',  gettext( 'recommends' ) ],
286                                                  [ 'sug',  gettext( 'suggests' ) ], );
287                     
288                     $package_page .= $dep_list;
289                     $package_page .= "</div> <!-- end pdeps -->\n";
290                 }
291
292                 #
293                 # Download package
294                 #
295                 my $encodedpack = uri_escape( $pkg );
296                 $package_page .= "<div id=\"pdownload\">";
297                 $package_page .= sprintf( "<h2>".gettext( "Download %s\n" )."</h2>",
298                                           $pkg ) ;
299                 $package_page .= "<table border=\"1\" summary=\"".gettext("The download table links to the download of the package and a file overview. In addition it gives information about the package size and the installed size.")."\">\n";
300                 $package_page .= "<caption class=\"hidecss\">".gettext("Download for all available architectures")."</caption>\n";
301                 $package_page .= "<tr>\n";
302                 $package_page .= "<th>".gettext("Architecture")."</th><th>".gettext("Files")."</th><th>".gettext( "Package Size")."</th><th>".gettext("Installed Size")."</th></tr>\n";
303                 foreach my $a ( @archs ) {
304                     $package_page .= "<tr>\n";
305                     $package_page .=  "<th><a href=\"$ROOT/$suite/$encodedpkg/$a/download";
306                     $package_page .=  "\">$a</a></th>\n";
307                     $package_page .= "<td>";
308                     if ( $suite ne "experimental" ) {
309                         $package_page .= sprintf( "[<a href=\"%s\">".gettext( "list of files" )."</a>]\n",
310                             "$ROOT/$suite/$encodedpkg/$a/filelist", $pkg );
311                     } else {
312                         $package_page .= gettext( "no current information" );
313                     }
314                     $package_page .= "</td>\n<td align=right>"; #FIXME: css
315                     $package_page .=  floor(($sizes_deb->{$a}/102.4)+0.5)/10 . "&nbsp;kB";
316                     $package_page .= "</td>\n<td align=right>"; #FIXME: css
317                     $package_page .=  $sizes_inst->{$a} . "&nbsp;kB";
318                     $package_page .= "</td>\n</tr>";
319                 }
320                 $package_page .= "</table>\n";
321                 $package_page .= "</div> <!-- end pdownload -->\n";
322                 
323                 #
324                 # more information
325                 #
326                 $package_page .= pmoreinfo( name => $pkg, data => $page,
327                                             opts => \%opts,
328                                             env => \%FTP_SITES,
329                                             bugreports => 1, sourcedownload => 1,
330                                             changesandcopy => 1, maintainers => 1,
331                                             search => 1 );
332             }
333         }
334     } else {
335         read_src_entry_all( \%sources, $pkg, \@results, \@non_results, \%opts );
336
337         unless (@results || @non_results ) {
338             fatal_error( "No such package".
339                          "{insert link to search page with substring search}" );
340         } else {
341             unless (@results) {
342                 fatal_error( "Package not available in this suite" );
343             } else {
344                 for my $entry (@results) {
345                     debug( join(":", @$entry), 1 );
346                     my (undef, $archive, undef, $section, $subsection,
347                         $priority, $version) = @$entry;
348                     
349                     my $data = $sources_all{"$archive $suite $pkg"};
350                     $page->merge_data($pkg, $suite, $archive, $data) or debug( "Merging $pkg $version FAILED", 2 );
351                 }
352                 $version = $page->{version};
353
354                 my $st1 = new Benchmark;
355                 my $std = timediff($st1, $st0);
356                 debug( "Data search and merging took ".timestr($std) );
357
358                 my $encodedpkg = uri_escape( $pkg );
359                 my ($v_str, $v_str_arr) = $page->get_version_string();
360                 $archive = $page->get_newest( 'archive' );
361                 $section = $page->get_newest( 'section' );
362                 $subsection = $page->get_newest( 'subsection' );
363
364                 my %all_suites;
365                 foreach (@results, @non_results) {
366                     my $a = $_->[1];
367                     my $s = $_->[2];
368                     if ($a =~ /^(?:us|security|non-US)$/o) {
369                         $all_suites{$s}++;
370                     } else {
371                         $all_suites{"$s/$a"}++;
372                     }
373                 }
374                 foreach (suites_sort(keys %all_suites)) {
375                     if (("$suite/$archive" eq $_)
376                         || (!$all_suites{"$suite/$archive"} && ($suite eq $_))) {
377                         $package_page .= "[ <strong>$_</strong> ] ";
378                     } else {
379                         $package_page .=
380                             "[ <a href=\"$ROOT/$_/source/".uri_escape($pkg)."\">$_</a> ] ";
381                     }
382                 }
383                 $package_page .= '<br>';
384
385                 $package_page .= simple_menu( [ gettext( "Distribution:" ),
386                                                 gettext( "Overview over this suite" ),
387                                                 "/$suite/",
388                                                 $suite ],
389                                               [ gettext( "Section:" ),
390                                                 gettext( "All packages in this section" ),
391                                                 "/$suite/$subsection/",
392                                                 $subsection ],
393                                               );
394
395                 my $title .= sprintf( gettext( "Source Package: %s (%s)" ),
396                                       $pkg, $v_str );
397                 $title .=  " ".marker( $archive ) if $archive ne 'us';
398                 $title .=  " ".marker( $subsection ) if $subsection eq 'non-US'
399                     and $archive ne 'non-US'; # non-US/security
400                 $title .=  " ".marker( $section ) if $section ne 'main';
401                 $package_page .= title( $title );
402                 
403                 if ($suite eq "experimental") {
404                     $package_page .= note( gettext( "Experimental package"),
405                                            gettext( "Warning: This package is from the <span class=\"pred\">experimental</span> distribution. That means it is likely unstable or buggy, and it may even cause data loss. If you ignore this warning and install it nevertheless, you do it on your own risk.")."</p><p>".
406                                            gettext( "Users of experimental packages are encouraged to contact the package maintainers directly in case of problems." )
407                                            );
408                 }
409                 if ($subsection eq "debian-installer") {
410                     note( gettext( "debian-installer udeb package"),
411                           gettext( "Warning: This package is intended for the use in building <a href=\"http://www.debian.org/devel/debian-installer\">debian-installer</a> images only. Do not install it on a normal Debian system." )
412                           );
413                 }
414
415                 my $binaries = find_binaries( $pkg, $archive, $suite, \%src2bin );
416                 if ($binaries && @$binaries) {
417                     $package_page .= '<div class="pdesc">';
418                     $package_page .= gettext( "The following binary packages are built from this source package:" );
419                     $package_page .= pkg_list( \%packages, \%opts, $binaries, 'en' );
420                     $package_page .= '</div> <!-- end pdesc -->';
421                 }
422                 
423                 #
424                 # display dependencies
425                 #
426                 my $dep_list;
427                 $dep_list = print_src_deps( \%packages, \%opts, $pkg,
428                                             $page->get_dep_field('build-depends'),
429                                             'build-depends' );
430                 $dep_list .= print_src_deps( \%packages, \%opts, $pkg,
431                                              $page->get_dep_field('build-depends-indep'),
432                                              'build-depends-indep' );
433
434                 if ( $dep_list ) {
435                     $package_page .= "<div id=\"pdeps\">\n";
436                     $package_page .= sprintf( "<h2>".gettext( "Other Packages Related to %s" )."</h2>\n", $pkg );
437                     if ($suite eq "experimental") {
438                         note( gettext( "Note that the \"<span class=\"pred\">experimental</span>\" distribution is not self-contained; missing dependencies are likely found in the \"<a href=\"/unstable/\">unstable</a>\" distribution." ) );
439                     }
440                     
441                     $package_page .= pdeplegend( [ 'adep',  gettext( 'build-depends' ) ],
442                                                  [ 'idep',  gettext( 'build-depends-indep' ) ],
443                                                  );
444                     
445                     $package_page .= $dep_list;
446                     $package_page .= "</div> <!-- end pdeps -->\n";
447                 }
448
449                 #
450                 # Source package download
451                 #
452                 $package_page .= "<div id=\"pdownload\">\n";
453                 my $encodedpack = uri_escape( $pkg );
454                 $package_page .= sprintf( "<h2>".gettext( "Download %s" )."</h2>\n",
455                                           $pkg ) ;
456
457                 my $source_files = $page->get_src( 'files' );
458                 my $source_dir = $page->get_src( 'directory' );
459
460                 $package_page .= sprintf( "<table cellspacing=\"0\" cellpadding=\"2\" summary=\"Download information for the files of this source package\">\n"
461                                           ."<tr><th>%s</th><th>%s</th><th>%s</th>",
462                                           gettext("File"),
463                                           gettext("Size (in kB)"),
464                                           gettext("md5sum") );
465                 foreach( @$source_files ) {
466                     my ($src_file_md5, $src_file_size, $src_file_name)
467                         = split /\s+/, $_;
468                     my $src_url;
469                     for ($archive) {
470                         /security/o &&  do {
471                             $src_url = $FTP_SITES{security}; last };
472                         /volatile/o &&  do {
473                             $src_url = $FTP_SITES{volatile}; last };
474                         /backports/o &&  do {
475                             $src_url = $FTP_SITES{backports}; last };
476                         /non-us/io  &&  do {
477                             $src_url = $FTP_SITES{'non-US'}; last };
478                         $src_url = $FTP_SITES{us};
479                     }
480                     $src_url .= "/$source_dir/$src_file_name";
481                     
482                     $package_page .= "<tr><td><a href=\"$src_url\">$src_file_name</a></td>\n"
483                         ."<td class=\"dotalign\">".sprintf("%.1f", (floor(($src_file_size/102.4)+0.5)/10))."</td>\n"
484                         ."<td>$src_file_md5</td></tr>";
485                 }
486                 $package_page .= "</table>\n";
487                 $package_page .= "</div> <!-- end pdownload -->\n";
488
489                 #
490                 # more information
491                 #
492                 $package_page .= pmoreinfo( name => $pkg, data => $page,
493                                             opts => \%opts,
494                                             env => \%FTP_SITES,
495                                             bugreports => 1,
496                                             changesandcopy => 1, maintainers => 1,
497                                             search => 1, is_source => 1 );
498             }
499         }
500     }
501 }
502
503 use Data::Dumper;
504 debug( "Final page object:\n".Dumper($page), 3 );
505
506 my $title = $opts{source} ?
507     "Details of source package <em>$pkg</em> in $suite"  :
508     "Details of package <em>$pkg</em> in $suite" ;
509 my $title_tag = $opts{source} ?
510     "Details of source package $pkg in $suite"  :
511     "Details of package $pkg in $suite" ;
512 print Packages::HTML::header( title => $title ,
513                               lang => 'en',
514                               desc => $short_desc,
515                               keywords => "$suite, $archive, $section, $subsection, $version",
516                               title_tag => "Details of package $pkg in $suite",
517                               );
518
519 print_errors();
520 print_hints();
521 print_msgs();
522 print_debug();
523 print_notes();
524
525 unless (@Packages::CGI::fatal_errors) {
526     print $package_page;
527 }
528 my $tet1 = new Benchmark;
529 my $tetd = timediff($tet1, $tet0);
530 print "Total page evaluation took ".timestr($tetd)."<br>"
531     if $debug_allowed;
532
533 my $trailer = Packages::HTML::trailer( $ROOT );
534 $trailer =~ s/LAST_MODIFIED_DATE/gmtime()/e; #FIXME
535 print $trailer;
536
537 # vim: ts=8 sw=4