3 # show_package.pl -- CGI interface to show info about a package
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
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
17 use CGI qw( -oldstyle_urls );
18 use CGI::Carp qw( fatalsToBrowser );
26 use Packages::Config qw( $DBDIR $ROOT @SUITES @ARCHIVES @SECTIONS
27 @ARCHITECTURES %FTP_SITES );
30 use Packages::Search qw( :all );
32 use Packages::Page ();
33 use Packages::SrcPage ();
35 &Packages::CGI::reset;
37 $ENV{PATH} = "/bin:/usr/bin";
39 # Read in all the variables set by the form
41 if ($ARGV[0] && ($ARGV[0] eq 'php')) {
42 $input = new CGI(\*STDIN);
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;
55 &Packages::Config::init( '../' );
56 &Packages::DB::init();
58 if (my $path = $input->param('path')) {
59 my @components = map { lc $_ } split /\//, $path;
61 my %SUITES = map { $_ => 1 } @SUITES;
62 my %SECTIONS = map { $_ => 1 } @SECTIONS;
63 my %ARCHIVES = map { $_ => 1 } @ARCHIVES;
64 my %ARCHITECTURES = map { $_ => 1 } @ARCHITECTURES;
66 foreach (@components) {
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);
81 my ( $pkg, $suite, @sections, @archs, @archives, $format );
82 my %params_def = ( package => { default => undef, match => '^([a-z0-9.+-]+)$',
84 suite => { default => undef, match => '^(\w+)$',
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+)$',
97 source => { default => 0, match => '^(\d+)$' },
100 my %params = Packages::Search::parse_params( $input, \%params_def, \%opts );
102 #XXX: Don't use alternative output formats yet
104 if ($format eq 'html') {
105 print $input->header;
108 if ($params{errors}{package}) {
109 fatal_error( "package not valid or not specified" );
112 if ($params{errors}{suite}) {
113 fatal_error( "suite not valid or not specified" );
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 };;
122 my $DL_URL = "$pkg/download";
123 my $FILELIST_URL = "$pkg/files";
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;
133 sub gettext { return $_[0]; };
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: $!";
144 unless ($opts{source}) {
145 read_entry_all( \%packages, $pkg, \@results, \@non_results, \%opts );
147 unless (@results || @non_results ) {
148 fatal_error( "No such package".
149 "{insert link to search page with substring search}" );
152 fatal_error( "Package not available in this suite" );
154 for my $entry (@results) {
155 debug( join(":", @$entry), 1 );
156 my (undef, $archive, undef, $arch, $section, $subsection,
157 $priority, $version) = @$entry;
159 my $data = $packages_all{"$pkg $arch $version"};
160 $page->merge_data($pkg, $version, $arch, $data) or debug( "Merging $pkg $arch $version FAILED", 2 );
163 $version = $page->{newest};
164 my $source = $page->get_newest( 'source' );
165 my $source_version = $page->get_newest( 'source-version' )
167 debug( "find source package: source=$source (=$source_version)", 1);
168 my $src_data = $sources_all{"$source $source_version"};
169 unless ($src_data) { #fucking binNMUs
170 my $versions = $page->get_versions;
171 my $sources = $page->get_arch_field( 'source' );
172 my $source_versions = $page->get_arch_field( 'source-version' );
173 foreach (version_sort keys %$versions) {
174 $source = $sources->{$versions->{$_}[0]};
175 $source = $source_versions->{$versions->{$_}[0]}
177 $src_data = $sources_all{"$source $source_version"};
180 error( "couldn't find source package" ) unless $src_data;
182 $page->add_src_data( $source, $source_version, $src_data )
185 my $st1 = new Benchmark;
186 my $std = timediff($st1, $st0);
187 debug( "Data search and merging took ".timestr($std) );
189 my $encodedpkg = uri_escape( $pkg );
190 my ($v_str, $v_str_arch, $v_str_arr) = $page->get_version_string();
191 my $did = $page->get_newest( 'description' );
192 $archive = $page->get_newest( 'archive' );
193 $section = $page->get_newest( 'section' );
194 $subsection = $page->get_newest( 'subsection' );
195 my $filenames = $page->get_arch_field( 'filename' );
196 my $file_md5sums = $page->get_arch_field( 'md5sum' );
197 my $archives = $page->get_arch_field( 'archive' );
198 my $sizes_inst = $page->get_arch_field( 'installed-size' );
199 my $sizes_deb = $page->get_arch_field( 'size' );
200 my @archs = sort $page->get_architectures;
202 # process description
204 my $desc = $descriptions{$did};
205 $short_desc = encode_entities( $1, "<>&\"" )
206 if $desc =~ s/^(.*)$//m;
207 my $long_desc = encode_entities( $desc, "<>&\"" );
209 $long_desc =~ s,((ftp|http|https)://[\S~-]+?/?)((\>\;)?[)]?[']?[:.\,]?(\s|$)),<a href=\"$1\">$1</a>$3,go; # syntax highlighting -> '];
210 $long_desc =~ s/\A //o;
211 $long_desc =~ s/\n /\n/sgo;
212 $long_desc =~ s/\n.\n/\n<p>\n/go;
213 $long_desc =~ s/(((\n|\A) [^\n]*)+)/\n<pre>$1\n<\/pre>/sgo;
214 # $long_desc = conv_desc( $lang, $long_desc );
215 # $short_desc = conv_desc( $lang, $short_desc );
218 foreach (@results, @non_results) {
221 if ($a =~ /^(?:us|security|non-US)$/o) {
224 $all_suites{"$s/$a"}++;
227 foreach (suites_sort(keys %all_suites)) {
228 if (("$suite/$archive" eq $_)
229 || (!$all_suites{"$suite/$archive"} && ($suite eq $_))) {
230 $package_page .= "[ <strong>$_</strong> ] ";
233 "[ <a href=\"$ROOT/$_/".uri_escape($pkg)."\">$_</a> ] ";
236 $package_page .= '<br>';
238 $package_page .= simple_menu( [ gettext( "Distribution:" ),
239 gettext( "Overview over this suite" ),
242 [ gettext( "Section:" ),
243 gettext( "All packages in this section" ),
244 "/$suite/$subsection/",
248 my $title .= sprintf( gettext( "Package: %s (%s)" ), $pkg, $v_str );
249 $title .= " ".marker( $archive ) if $archive ne 'us';
250 $title .= " ".marker( $subsection ) if $subsection eq 'non-US'
251 and $archive ne 'non-US'; # non-US/security
252 $title .= " ".marker( $section ) if $section ne 'main';
253 $package_page .= title( $title );
255 $package_page .= "<h2>".gettext( "Versions:" )." $v_str_arch</h2>\n"
256 unless $version eq $v_str;
258 if ($suite eq "experimental") {
259 $package_page .= note( gettext( "Experimental package"),
260 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>".
261 gettext( "Users of experimental packages are encouraged to contact the package maintainers directly in case of problems." )
264 if ($subsection eq "debian-installer") {
265 note( gettext( "debian-installer udeb package"),
266 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." )
269 $package_page .= pdesc( $short_desc, $long_desc );
272 # display dependencies
275 $dep_list = print_deps( \%packages, \%opts, $pkg,
276 $page->get_dep_field('depends'),
278 $dep_list .= print_deps( \%packages, \%opts, $pkg,
279 $page->get_dep_field('recommends'),
281 $dep_list .= print_deps( \%packages, \%opts, $pkg,
282 $page->get_dep_field('suggests'),
286 $package_page .= "<div id=\"pdeps\">\n";
287 $package_page .= sprintf( "<h2>".gettext( "Other Packages Related to %s" )."</h2>\n", $pkg );
288 if ($suite eq "experimental") {
289 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." ) );
292 $package_page .= pdeplegend( [ 'dep', gettext( 'depends' ) ],
293 [ 'rec', gettext( 'recommends' ) ],
294 [ 'sug', gettext( 'suggests' ) ], );
296 $package_page .= $dep_list;
297 $package_page .= "</div> <!-- end pdeps -->\n";
303 my $encodedpack = uri_escape( $pkg );
304 $package_page .= "<div id=\"pdownload\">";
305 $package_page .= sprintf( "<h2>".gettext( "Download %s\n" )."</h2>",
307 $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";
308 $package_page .= "<caption class=\"hidecss\">".gettext("Download for all available architectures")."</caption>\n";
309 $package_page .= "<tr>\n";
310 $package_page .= "<th>".gettext("Architecture")."</th><th>".gettext("Files")."</th><th>".gettext( "Package Size")."</th><th>".gettext("Installed Size")."</th></tr>\n";
311 foreach my $a ( @archs ) {
312 $package_page .= "<tr>\n";
313 $package_page .= "<th><a href=\"$DL_URL?arch=$a";
314 $package_page .= "&file=".uri_escape($filenames->{$a});
315 $package_page .= "&md5sum=$file_md5sums->{$a}";
316 $package_page .= "&arch=$a";
317 for ($archives->{$a}) {
319 $package_page .= "&type=security"; last };
321 $package_page .= "&type=volatile"; last };
323 $package_page .= "&type=backports"; last };
325 $package_page .= "&type=nonus"; last };
326 $package_page .= "&type=main";
328 $package_page .= "\">$a</a></th>\n";
329 $package_page .= "<td>";
330 if ( $suite ne "experimental" ) {
331 $package_page .= sprintf( "[<a href=\"%s\">".gettext( "list of files" )."</a>]\n", "$FILELIST_URL$encodedpkg&version=$suite&arch=$a", $pkg );
333 $package_page .= gettext( "no current information" );
335 $package_page .= "</td>\n<td>";
336 $package_page .= floor(($sizes_deb->{$a}/102.4)+0.5)/10;
337 $package_page .= "</td>\n<td>";
338 $package_page .= $sizes_inst->{$a};
339 $package_page .= "</td>\n</tr>";
341 $package_page .= "</table><p>".gettext ( "Size is measured in kBytes." )."</p>\n";
342 $package_page .= "</div> <!-- end pdownload -->\n";
347 $package_page .= pmoreinfo( name => $pkg, data => $page,
350 bugreports => 1, sourcedownload => 1,
351 changesandcopy => 1, maintainers => 1,
356 read_src_entry_all( \%sources, $pkg, \@results, \@non_results, \%opts );
358 unless (@results || @non_results ) {
359 fatal_error( "No such package".
360 "{insert link to search page with substring search}" );
363 fatal_error( "Package not available in this suite" );
365 for my $entry (@results) {
366 debug( join(":", @$entry), 1 );
367 my (undef, $archive, undef, $section, $subsection,
368 $priority, $version) = @$entry;
370 my $data = $sources_all{"$pkg $version"};
371 $page->merge_data($pkg, $version, $data) or debug( "Merging $pkg $version FAILED", 2 );
373 $version = $page->{version};
375 my $st1 = new Benchmark;
376 my $std = timediff($st1, $st0);
377 debug( "Data search and merging took ".timestr($std) );
379 my $encodedpkg = uri_escape( $pkg );
380 my ($v_str, $v_str_arr) = $page->get_version_string();
381 $archive = $page->get_newest( 'archive' );
382 $section = $page->get_newest( 'section' );
383 $subsection = $page->get_newest( 'subsection' );
386 foreach (@results, @non_results) {
389 if ($a =~ /^(?:us|security|non-US)$/o) {
392 $all_suites{"$s/$a"}++;
395 foreach (suites_sort(keys %all_suites)) {
396 if (("$suite/$archive" eq $_)
397 || (!$all_suites{"$suite/$archive"} && ($suite eq $_))) {
398 $package_page .= "[ <strong>$_</strong> ] ";
401 "[ <a href=\"$ROOT/$_/source/".uri_escape($pkg)."\">$_</a> ] ";
404 $package_page .= '<br>';
406 $package_page .= simple_menu( [ gettext( "Distribution:" ),
407 gettext( "Overview over this suite" ),
410 [ gettext( "Section:" ),
411 gettext( "All packages in this section" ),
412 "/$suite/$subsection/",
416 my $title .= sprintf( gettext( "Source Package: %s (%s)" ),
418 $title .= " ".marker( $archive ) if $archive ne 'us';
419 $title .= " ".marker( $subsection ) if $subsection eq 'non-US'
420 and $archive ne 'non-US'; # non-US/security
421 $title .= " ".marker( $section ) if $section ne 'main';
422 $package_page .= title( $title );
424 if ($suite eq "experimental") {
425 $package_page .= note( gettext( "Experimental package"),
426 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>".
427 gettext( "Users of experimental packages are encouraged to contact the package maintainers directly in case of problems." )
430 if ($subsection eq "debian-installer") {
431 note( gettext( "debian-installer udeb package"),
432 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." )
436 my $binaries = find_binaries( $pkg, $archive, $suite, \%src2bin );
437 if ($binaries && @$binaries) {
438 $package_page .= '<div class="pdesc">';
439 $package_page .= gettext( "The following binary packages are built from this source package:" );
440 $package_page .= pkg_list( \%packages, \%opts, $binaries, 'en' );
441 $package_page .= '</div> <!-- end pdesc -->';
445 # display dependencies
448 $dep_list = print_src_deps( \%packages, \%opts, $pkg,
449 $page->get_dep_field('build-depends'),
451 $dep_list .= print_src_deps( \%packages, \%opts, $pkg,
452 $page->get_dep_field('build-depends-indep'),
453 'build-depends-indep' );
456 $package_page .= "<div id=\"pdeps\">\n";
457 $package_page .= sprintf( "<h2>".gettext( "Other Packages Related to %s" )."</h2>\n", $pkg );
458 if ($suite eq "experimental") {
459 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." ) );
462 $package_page .= pdeplegend( [ 'adep', gettext( 'build-depends' ) ],
463 [ 'idep', gettext( 'build-depends-indep' ) ],
466 $package_page .= $dep_list;
467 $package_page .= "</div> <!-- end pdeps -->\n";
471 # Source package download
473 $package_page .= "<div id=\"pdownload\">\n";
474 my $encodedpack = uri_escape( $pkg );
475 $package_page .= sprintf( "<h2>".gettext( "Download %s" )."</h2>\n",
478 my $source_files = $page->get_src( 'files' );
479 my $source_dir = $page->get_src( 'directory' );
481 $package_page .= sprintf( "<table cellspacing=\"0\" cellpadding=\"2\" summary=\"Download information for the files of this source package\">\n"
482 ."<tr><th>%s</th><th>%s</th><th>%s</th>",
484 gettext("Size (in kB)"),
486 foreach( @$source_files ) {
487 my ($src_file_md5, $src_file_size, $src_file_name) = @$_;
491 $src_url = $FTP_SITES{security}; last };
493 $src_url = $FTP_SITES{volatile}; last };
495 $src_url = $FTP_SITES{backports}; last };
497 $src_url = $FTP_SITES{'non-US'}; last };
498 $src_url = $FTP_SITES{us};
500 $src_url .= "/$source_dir/$src_file_name";
502 $package_page .= "<tr><td><a href=\"$src_url\">$src_file_name</a></td>\n"
503 ."<td class=\"dotalign\">".sprintf("%.1f", (floor(($src_file_size/102.4)+0.5)/10))."</td>\n"
504 ."<td>$src_file_md5</td></tr>";
506 $package_page .= "</table>\n";
507 $package_page .= "</div> <!-- end pdownload -->\n";
512 $package_page .= pmoreinfo( name => $pkg, data => $page,
516 changesandcopy => 1, maintainers => 1,
517 search => 1, is_source => 1 );
524 #debug( "Final page object:\n".Dumper($page), 3 );
526 my $title = $opts{source} ?
527 "Details of source package <em>$pkg</em> in $suite" :
528 "Details of package <em>$pkg</em> in $suite" ;
529 my $title_tag = $opts{source} ?
530 "Details of source package $pkg in $suite" :
531 "Details of package $pkg in $suite" ;
532 print Packages::HTML::header( title => $title ,
535 keywords => "$suite, $archive, $section, $subsection, $version",
536 title_tag => "Details of package $pkg in $suite",
545 unless (@Packages::CGI::fatal_errors) {
548 my $tet1 = new Benchmark;
549 my $tetd = timediff($tet1, $tet0);
550 print "Total page evaluation took ".timestr($tetd)."<br>"
553 my $trailer = Packages::HTML::trailer( $ROOT );
554 $trailer =~ s/LAST_MODIFIED_DATE/gmtime()/e; #FIXME