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, @provided_by) = @$entry;
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 );
166 $page->add_provided_by(\@provided_by);
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 )
178 my $st1 = new Benchmark;
179 my $std = timediff($st1, $st0);
180 debug( "Data search and merging took ".timestr($std) );
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;
194 # process description
196 my $desc = $descriptions{$did};
197 $short_desc = encode_entities( $1, "<>&\"" )
198 if $desc =~ s/^(.*)$//m;
199 my $long_desc = encode_entities( $desc, "<>&\"" );
201 $long_desc =~ s,((ftp|http|https)://[\S~-]+?/?)((\>\;)?[)]?[']?[:.\,]?(\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 );
210 foreach (@results, @non_results) {
213 if ($a =~ /^(?:us|security|non-US)$/o) {
216 $all_suites{"$s/$a"}++;
219 foreach (suites_sort(keys %all_suites)) {
220 if (("$suite/$archive" eq $_)
221 || (!$all_suites{"$suite/$archive"} && ($suite eq $_))) {
222 $package_page .= "[ <strong>$_</strong> ] ";
225 "[ <a href=\"$ROOT/$_/".uri_escape($pkg)."\">$_</a> ] ";
228 $package_page .= '<br>';
230 $package_page .= simple_menu( [ gettext( "Distribution:" ),
231 gettext( "Overview over this suite" ),
234 [ gettext( "Section:" ),
235 gettext( "All packages in this section" ),
236 "$ROOT/$suite/$subsection/",
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 );
247 $package_page .= "<h2>".gettext( "Versions:" )." $v_str_arch</h2>\n"
248 unless $version eq $v_str;
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." )
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." )
261 $package_page .= pdesc( $short_desc, $long_desc );
264 # display dependencies
267 $dep_list = print_deps( \%packages, \%opts, $pkg,
268 $page->get_dep_field('depends'),
270 $dep_list .= print_deps( \%packages, \%opts, $pkg,
271 $page->get_dep_field('recommends'),
273 $dep_list .= print_deps( \%packages, \%opts, $pkg,
274 $page->get_dep_field('suggests'),
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." ) );
284 $package_page .= pdeplegend( [ 'dep', gettext( 'depends' ) ],
285 [ 'rec', gettext( 'recommends' ) ],
286 [ 'sug', gettext( 'suggests' ) ], );
288 $package_page .= $dep_list;
289 $package_page .= "</div> <!-- end pdeps -->\n";
295 my $encodedpack = uri_escape( $pkg );
296 $package_page .= "<div id=\"pdownload\">";
297 $package_page .= sprintf( "<h2>".gettext( "Download %s\n" )."</h2>",
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 );
312 $package_page .= gettext( "no current information" );
314 $package_page .= "</td>\n<td align=right>"; #FIXME: css
315 $package_page .= floor(($sizes_deb->{$a}/102.4)+0.5)/10 . " kB";
316 $package_page .= "</td>\n<td align=right>"; #FIXME: css
317 $package_page .= $sizes_inst->{$a} . " kB";
318 $package_page .= "</td>\n</tr>";
320 $package_page .= "</table>\n";
321 $package_page .= "</div> <!-- end pdownload -->\n";
326 $package_page .= pmoreinfo( name => $pkg, data => $page,
329 bugreports => 1, sourcedownload => 1,
330 changesandcopy => 1, maintainers => 1,
335 read_src_entry_all( \%sources, $pkg, \@results, \@non_results, \%opts );
337 unless (@results || @non_results ) {
338 fatal_error( "No such package".
339 "{insert link to search page with substring search}" );
342 fatal_error( "Package not available in this suite" );
344 for my $entry (@results) {
345 debug( join(":", @$entry), 1 );
346 my (undef, $archive, undef, $section, $subsection,
347 $priority, $version) = @$entry;
349 my $data = $sources_all{"$archive $suite $pkg"};
350 $page->merge_data($pkg, $suite, $archive, $data) or debug( "Merging $pkg $version FAILED", 2 );
352 $version = $page->{version};
354 my $st1 = new Benchmark;
355 my $std = timediff($st1, $st0);
356 debug( "Data search and merging took ".timestr($std) );
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' );
365 foreach (@results, @non_results) {
368 if ($a =~ /^(?:us|security|non-US)$/o) {
371 $all_suites{"$s/$a"}++;
374 foreach (suites_sort(keys %all_suites)) {
375 if (("$suite/$archive" eq $_)
376 || (!$all_suites{"$suite/$archive"} && ($suite eq $_))) {
377 $package_page .= "[ <strong>$_</strong> ] ";
380 "[ <a href=\"$ROOT/$_/source/".uri_escape($pkg)."\">$_</a> ] ";
383 $package_page .= '<br>';
385 $package_page .= simple_menu( [ gettext( "Distribution:" ),
386 gettext( "Overview over this suite" ),
389 [ gettext( "Section:" ),
390 gettext( "All packages in this section" ),
391 "/$suite/$subsection/",
395 my $title .= sprintf( gettext( "Source Package: %s (%s)" ),
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 );
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." )
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." )
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 -->';
424 # display dependencies
427 $dep_list = print_src_deps( \%packages, \%opts, $pkg,
428 $page->get_dep_field('build-depends'),
430 $dep_list .= print_src_deps( \%packages, \%opts, $pkg,
431 $page->get_dep_field('build-depends-indep'),
432 'build-depends-indep' );
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." ) );
441 $package_page .= pdeplegend( [ 'adep', gettext( 'build-depends' ) ],
442 [ 'idep', gettext( 'build-depends-indep' ) ],
445 $package_page .= $dep_list;
446 $package_page .= "</div> <!-- end pdeps -->\n";
450 # Source package download
452 $package_page .= "<div id=\"pdownload\">\n";
453 my $encodedpack = uri_escape( $pkg );
454 $package_page .= sprintf( "<h2>".gettext( "Download %s" )."</h2>\n",
457 my $source_files = $page->get_src( 'files' );
458 my $source_dir = $page->get_src( 'directory' );
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>",
463 gettext("Size (in kB)"),
465 foreach( @$source_files ) {
466 my ($src_file_md5, $src_file_size, $src_file_name)
471 $src_url = $FTP_SITES{security}; last };
473 $src_url = $FTP_SITES{volatile}; last };
475 $src_url = $FTP_SITES{backports}; last };
477 $src_url = $FTP_SITES{'non-US'}; last };
478 $src_url = $FTP_SITES{us};
480 $src_url .= "/$source_dir/$src_file_name";
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>";
486 $package_page .= "</table>\n";
487 $package_page .= "</div> <!-- end pdownload -->\n";
492 $package_page .= pmoreinfo( name => $pkg, data => $page,
496 changesandcopy => 1, maintainers => 1,
497 search => 1, is_source => 1 );
504 debug( "Final page object:\n".Dumper($page), 3 );
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 ,
515 keywords => "$suite, $archive, $section, $subsection, $version",
516 title_tag => "Details of package $pkg in $suite",
525 unless (@Packages::CGI::fatal_errors) {
528 my $tet1 = new Benchmark;
529 my $tetd = timediff($tet1, $tet0);
530 print "Total page evaluation took ".timestr($tetd)."<br>"
533 my $trailer = Packages::HTML::trailer( $ROOT );
534 $trailer =~ s/LAST_MODIFIED_DATE/gmtime()/e; #FIXME