]> git.deb.at Git - deb/packages.git/blob - cgi-bin/show_package.pl
* Add source package display
[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) = @$entry;
158                     
159                     my $data = $packages_all{"$pkg $arch $version"};
160                     $page->merge_data($pkg, $version, $arch, $data) or debug( "Merging $pkg $arch $version FAILED", 2 );
161                 }
162
163                 $version = $page->{newest};
164                 my $source = $page->get_newest( 'source' );
165                 my $source_version = $page->get_newest( 'source-version' )
166                     || $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]}
176                         || $version;
177                         $src_data = $sources_all{"$source $source_version"};
178                         last if $src_data;
179                     }
180                     error( "couldn't find source package" ) unless $src_data;
181                 }
182                 $page->add_src_data( $source, $source_version, $src_data )
183                     if $src_data;
184
185                 my $st1 = new Benchmark;
186                 my $std = timediff($st1, $st0);
187                 debug( "Data search and merging took ".timestr($std) );
188
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;
201
202                 # process description
203                 #
204                 my $desc = $descriptions{$did};
205                 $short_desc = encode_entities( $1, "<>&\"" )
206                     if $desc =~ s/^(.*)$//m;
207                 my $long_desc = encode_entities( $desc, "<>&\"" );
208                 
209                 $long_desc =~ s,((ftp|http|https)://[\S~-]+?/?)((\&gt\;)?[)]?[']?[:.\,]?(\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 );
216
217                 my %all_suites;
218                 foreach (@results, @non_results) {
219                     my $a = $_->[1];
220                     my $s = $_->[2];
221                     if ($a =~ /^(?:us|security|non-US)$/o) {
222                         $all_suites{$s}++;
223                     } else {
224                         $all_suites{"$s/$a"}++;
225                     }
226                 }
227                 foreach (suites_sort(keys %all_suites)) {
228                     if (("$suite/$archive" eq $_)
229                         || (!$all_suites{"$suite/$archive"} && ($suite eq $_))) {
230                         $package_page .= "[ <strong>$_</strong> ] ";
231                     } else {
232                         $package_page .=
233                             "[ <a href=\"$ROOT/$_/".uri_escape($pkg)."\">$_</a> ] ";
234                     }
235                 }
236                 $package_page .= '<br>';
237
238                 $package_page .= simple_menu( [ gettext( "Distribution:" ),
239                                                 gettext( "Overview over this suite" ),
240                                                 "/$suite/",
241                                                 $suite ],
242                                               [ gettext( "Section:" ),
243                                                 gettext( "All packages in this section" ),
244                                                 "/$suite/$subsection/",
245                                                 $subsection ],
246                                               );
247
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 );
254                 
255                 $package_page .= "<h2>".gettext( "Versions:" )." $v_str_arch</h2>\n" 
256                     unless $version eq $v_str;
257                 
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." )
262                                            );
263                 }
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." )
267                           );
268                 }
269                 $package_page .= pdesc( $short_desc, $long_desc );
270
271                 #
272                 # display dependencies
273                 #
274                 my $dep_list;
275                 $dep_list = print_deps( \%packages, \%opts, $pkg,
276                                         $page->get_dep_field('depends'),
277                                         'depends' );
278                 $dep_list .= print_deps( \%packages, \%opts, $pkg,
279                                          $page->get_dep_field('recommends'),
280                                          'recommends' );
281                 $dep_list .= print_deps( \%packages, \%opts, $pkg,
282                                          $page->get_dep_field('suggests'),
283                                          'suggests' );
284
285                 if ( $dep_list ) {
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." ) );
290                     }
291                     
292                     $package_page .= pdeplegend( [ 'dep',  gettext( 'depends' ) ],
293                                                  [ 'rec',  gettext( 'recommends' ) ],
294                                                  [ 'sug',  gettext( 'suggests' ) ], );
295                     
296                     $package_page .= $dep_list;
297                     $package_page .= "</div> <!-- end pdeps -->\n";
298                 }
299
300                 #
301                 # Download package
302                 #
303                 my $encodedpack = uri_escape( $pkg );
304                 $package_page .= "<div id=\"pdownload\">";
305                 $package_page .= sprintf( "<h2>".gettext( "Download %s\n" )."</h2>",
306                                           $pkg ) ;
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 .=  "&amp;file=".uri_escape($filenames->{$a});
315                     $package_page .=  "&amp;md5sum=$file_md5sums->{$a}";
316                     $package_page .=  "&amp;arch=$a";
317                     for ($archives->{$a}) {
318                         /security/o &&  do {
319                             $package_page .=  "&amp;type=security"; last };
320                         /volatile/o &&  do {
321                             $package_page .=  "&amp;type=volatile"; last };
322                         /backports/o &&  do {
323                             $package_page .=  "&amp;type=backports"; last };
324                         /non-us/io  &&  do {
325                             $package_page .=  "&amp;type=nonus"; last };
326                         $package_page .=  "&amp;type=main";
327                     }
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&amp;version=$suite&amp;arch=$a", $pkg );
332                     } else {
333                         $package_page .= gettext( "no current information" );
334                     }
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>";
340                 }
341                 $package_page .= "</table><p>".gettext ( "Size is measured in kBytes." )."</p>\n";
342                 $package_page .= "</div> <!-- end pdownload -->\n";
343                 
344                 #
345                 # more information
346                 #
347                 $package_page .= pmoreinfo( name => $pkg, data => $page,
348                                             opts => \%opts,
349                                             env => \%FTP_SITES,
350                                             bugreports => 1, sourcedownload => 1,
351                                             changesandcopy => 1, maintainers => 1,
352                                             search => 1 );
353             }
354         }
355     } else {
356         read_src_entry_all( \%sources, $pkg, \@results, \@non_results, \%opts );
357
358         unless (@results || @non_results ) {
359             fatal_error( "No such package".
360                          "{insert link to search page with substring search}" );
361         } else {
362             unless (@results) {
363                 fatal_error( "Package not available in this suite" );
364             } else {
365                 for my $entry (@results) {
366                     debug( join(":", @$entry), 1 );
367                     my (undef, $archive, undef, $section, $subsection,
368                         $priority, $version) = @$entry;
369                     
370                     my $data = $sources_all{"$pkg $version"};
371                     $page->merge_data($pkg, $version, $data) or debug( "Merging $pkg $version FAILED", 2 );
372                 }
373                 $version = $page->{version};
374
375                 my $st1 = new Benchmark;
376                 my $std = timediff($st1, $st0);
377                 debug( "Data search and merging took ".timestr($std) );
378
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' );
384
385                 my %all_suites;
386                 foreach (@results, @non_results) {
387                     my $a = $_->[1];
388                     my $s = $_->[2];
389                     if ($a =~ /^(?:us|security|non-US)$/o) {
390                         $all_suites{$s}++;
391                     } else {
392                         $all_suites{"$s/$a"}++;
393                     }
394                 }
395                 foreach (suites_sort(keys %all_suites)) {
396                     if (("$suite/$archive" eq $_)
397                         || (!$all_suites{"$suite/$archive"} && ($suite eq $_))) {
398                         $package_page .= "[ <strong>$_</strong> ] ";
399                     } else {
400                         $package_page .=
401                             "[ <a href=\"$ROOT/$_/source/".uri_escape($pkg)."\">$_</a> ] ";
402                     }
403                 }
404                 $package_page .= '<br>';
405
406                 $package_page .= simple_menu( [ gettext( "Distribution:" ),
407                                                 gettext( "Overview over this suite" ),
408                                                 "/$suite/",
409                                                 $suite ],
410                                               [ gettext( "Section:" ),
411                                                 gettext( "All packages in this section" ),
412                                                 "/$suite/$subsection/",
413                                                 $subsection ],
414                                               );
415
416                 my $title .= sprintf( gettext( "Source Package: %s (%s)" ),
417                                       $pkg, $v_str );
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 );
423                 
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." )
428                                            );
429                 }
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." )
433                           );
434                 }
435
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 -->';
442                 }
443                 
444                 #
445                 # display dependencies
446                 #
447                 my $dep_list;
448                 $dep_list = print_src_deps( \%packages, \%opts, $pkg,
449                                             $page->get_dep_field('build-depends'),
450                                             'build-depends' );
451                 $dep_list .= print_src_deps( \%packages, \%opts, $pkg,
452                                              $page->get_dep_field('build-depends-indep'),
453                                              'build-depends-indep' );
454
455                 if ( $dep_list ) {
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." ) );
460                     }
461                     
462                     $package_page .= pdeplegend( [ 'adep',  gettext( 'build-depends' ) ],
463                                                  [ 'idep',  gettext( 'build-depends-indep' ) ],
464                                                  );
465                     
466                     $package_page .= $dep_list;
467                     $package_page .= "</div> <!-- end pdeps -->\n";
468                 }
469
470                 #
471                 # Source package download
472                 #
473                 $package_page .= "<div id=\"pdownload\">\n";
474                 my $encodedpack = uri_escape( $pkg );
475                 $package_page .= sprintf( "<h2>".gettext( "Download %s" )."</h2>\n",
476                                           $pkg ) ;
477
478                 my $source_files = $page->get_src( 'files' );
479                 my $source_dir = $page->get_src( 'directory' );
480
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>",
483                                           gettext("File"),
484                                           gettext("Size (in kB)"),
485                                           gettext("md5sum") );
486                 foreach( @$source_files ) {
487                     my ($src_file_md5, $src_file_size, $src_file_name) = @$_;
488                     my $src_url;
489                     for ($archive) {
490                         /security/o &&  do {
491                             $src_url = $FTP_SITES{security}; last };
492                         /volatile/o &&  do {
493                             $src_url = $FTP_SITES{volatile}; last };
494                         /backports/o &&  do {
495                             $src_url = $FTP_SITES{backports}; last };
496                         /non-us/io  &&  do {
497                             $src_url = $FTP_SITES{'non-US'}; last };
498                         $src_url = $FTP_SITES{us};
499                     }
500                     $src_url .= "/$source_dir/$src_file_name";
501                     
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>";
505                 }
506                 $package_page .= "</table>\n";
507                 $package_page .= "</div> <!-- end pdownload -->\n";
508
509                 #
510                 # more information
511                 #
512                 $package_page .= pmoreinfo( name => $pkg, data => $page,
513                                             opts => \%opts,
514                                             env => \%FTP_SITES,
515                                             bugreports => 1,
516                                             changesandcopy => 1, maintainers => 1,
517                                             search => 1, is_source => 1 );
518             }
519         }
520     }
521 }
522
523 #use Data::Dumper;
524 #debug( "Final page object:\n".Dumper($page), 3 );
525
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 ,
533                               lang => 'en',
534                               desc => $short_desc,
535                               keywords => "$suite, $archive, $section, $subsection, $version",
536                               title_tag => "Details of package $pkg in $suite",
537                               );
538
539 print_errors();
540 print_hints();
541 print_msgs();
542 print_debug();
543 print_notes();
544
545 unless (@Packages::CGI::fatal_errors) {
546     print $package_page;
547 }
548 my $tet1 = new Benchmark;
549 my $tetd = timediff($tet1, $tet0);
550 print "Total page evaluation took ".timestr($tetd)."<br>"
551     if $debug_allowed;
552
553 my $trailer = Packages::HTML::trailer( $ROOT );
554 $trailer =~ s/LAST_MODIFIED_DATE/gmtime()/e; #FIXME
555 print $trailer;
556
557 # vim: ts=8 sw=4