@@ -114,6 +114,49 @@ BEGIN
114114# - one might want to include '-B' option, e.g. '-B', '-M'
115115our @diff_opts = (' -M' ); # taken from git_commit
116116
117+ # information about snapshot formats that gitweb is capable of serving
118+ our %known_snapshot_formats = (
119+ # name => {
120+ # 'display' => display name,
121+ # 'type' => mime type,
122+ # 'suffix' => filename suffix,
123+ # 'format' => --format for git-archive,
124+ # 'compressor' => [compressor command and arguments]
125+ # (array reference, optional)}
126+ #
127+ ' tgz' => {
128+ ' display' => ' tar.gz' ,
129+ ' type' => ' application/x-gzip' ,
130+ ' suffix' => ' .tar.gz' ,
131+ ' format' => ' tar' ,
132+ ' compressor' => [' gzip' ]},
133+
134+ ' tbz2' => {
135+ ' display' => ' tar.bz2' ,
136+ ' type' => ' application/x-bzip2' ,
137+ ' suffix' => ' .tar.bz2' ,
138+ ' format' => ' tar' ,
139+ ' compressor' => [' bzip2' ]},
140+
141+ ' zip' => {
142+ ' display' => ' zip' ,
143+ ' type' => ' application/x-zip' ,
144+ ' suffix' => ' .zip' ,
145+ ' format' => ' zip' },
146+ );
147+
148+ # Aliases so we understand old gitweb.snapshot values in repository
149+ # configuration.
150+ our %known_snapshot_format_aliases = (
151+ ' gzip' => ' tgz' ,
152+ ' bzip2' => ' tbz2' ,
153+
154+ # backward compatibility: legacy gitweb config support
155+ ' x-gzip' => undef , ' gz' => undef ,
156+ ' x-bzip2' => undef , ' bz2' => undef ,
157+ ' x-zip' => undef , ' ' => undef ,
158+ );
159+
117160# You define site-wide feature defaults here; override them with
118161# $GITWEB_CONFIG as necessary.
119162our %feature = (
@@ -144,20 +187,22 @@ BEGIN
144187 ' override' => 0,
145188 ' default' => [0]},
146189
147- # Enable the 'snapshot' link, providing a compressed tarball of any
190+ # Enable the 'snapshot' link, providing a compressed archive of any
148191 # tree. This can potentially generate high traffic if you have large
149192 # project.
150193
194+ # Value is a list of formats defined in %known_snapshot_formats that
195+ # you wish to offer.
151196 # To disable system wide have in $GITWEB_CONFIG
152- # $feature{'snapshot'}{'default'} = [undef ];
197+ # $feature{'snapshot'}{'default'} = [];
153198 # To have project specific config enable override in $GITWEB_CONFIG
154199 # $feature{'snapshot'}{'override'} = 1;
155- # and in project config gitweb.snapshot = none|gzip|bzip2|zip;
200+ # and in project config, a comma-separated list of formats or "none"
201+ # to disable. Example: gitweb.snapshot = tbz2,zip;
156202 ' snapshot' => {
157203 ' sub' => \&feature_snapshot,
158204 ' override' => 0,
159- # => [content-encoding, suffix, program]
160- ' default' => [' x-gzip' , ' gz' , ' gzip' ]},
205+ ' default' => [' tgz' ]},
161206
162207 # Enable text search, which will list the commits which match author,
163208 # committer or commit text to a given string. Enabled by default.
@@ -256,28 +301,19 @@ sub feature_blame {
256301}
257302
258303sub feature_snapshot {
259- my ($ctype , $suffix , $command ) = @_ ;
304+ my (@fmts ) = @_ ;
260305
261306 my ($val ) = git_get_project_config(' snapshot' );
262307
263- if ($val eq ' gzip' ) {
264- return (' x-gzip' , ' gz' , ' gzip' );
265- } elsif ($val eq ' bzip2' ) {
266- return (' x-bzip2' , ' bz2' , ' bzip2' );
267- } elsif ($val eq ' zip' ) {
268- return (' x-zip' , ' zip' , ' ' );
269- } elsif ($val eq ' none' ) {
270- return ();
308+ if ($val ) {
309+ @fmts = ($val eq ' none' ? () : split /\s *[,\s ]\s */, $val );
310+ @fmts = grep { defined } map {
311+ exists $known_snapshot_format_aliases {$_ } ?
312+ $known_snapshot_format_aliases {$_ } : $_ } @fmts ;
313+ @fmts = grep (exists $known_snapshot_formats {$_ }, @fmts );
271314 }
272315
273- return ($ctype , $suffix , $command );
274- }
275-
276- sub gitweb_have_snapshot {
277- my ($ctype , $suffix , $command ) = gitweb_check_feature(' snapshot' );
278- my $have_snapshot = (defined $ctype && defined $suffix );
279-
280- return $have_snapshot ;
316+ return @fmts ;
281317}
282318
283319sub feature_grep {
563599 order => " o" ,
564600 searchtext => " s" ,
565601 searchtype => " st" ,
602+ snapshot_format => " sf" ,
566603 );
567604 my %mapping = @mapping ;
568605
@@ -1257,6 +1294,39 @@ sub format_diff_line {
12571294 return " <div class=\" diff$diff_class \" >" . esc_html($line , -nbsp => 1) . " </div>\n " ;
12581295}
12591296
1297+ # Generates undef or something like "_snapshot_" or "snapshot (_tbz2_ _zip_)",
1298+ # linked. Pass the hash of the tree/commit to snapshot.
1299+ sub format_snapshot_links {
1300+ my ($hash ) = @_ ;
1301+ my @snapshot_fmts = gitweb_check_feature(' snapshot' );
1302+ my $num_fmts = @snapshot_fmts ;
1303+ if ($num_fmts > 1) {
1304+ # A parenthesized list of links bearing format names.
1305+ return " snapshot (" . join (' ' , map
1306+ $cgi -> a({
1307+ -href => href(
1308+ action => " snapshot" ,
1309+ hash => $hash ,
1310+ snapshot_format => $_
1311+ )
1312+ }, $known_snapshot_formats {$_ }{' display' })
1313+ , @snapshot_fmts ) . " )" ;
1314+ } elsif ($num_fmts == 1) {
1315+ # A single "snapshot" link whose tooltip bears the format name.
1316+ my ($fmt ) = @snapshot_fmts ;
1317+ return $cgi -> a({
1318+ -href => href(
1319+ action => " snapshot" ,
1320+ hash => $hash ,
1321+ snapshot_format => $fmt
1322+ ),
1323+ -title => " in format: $known_snapshot_formats {$fmt }{'display'}"
1324+ }, " snapshot" );
1325+ } else { # $num_fmts == 0
1326+ return undef ;
1327+ }
1328+ }
1329+
12601330# # ----------------------------------------------------------------------
12611331# # git utility subroutines, invoking git commands
12621332
@@ -3321,8 +3391,6 @@ sub git_shortlog_body {
33213391 # uses global variable $project
33223392 my ($commitlist , $from , $to , $refs , $extra ) = @_ ;
33233393
3324- my $have_snapshot = gitweb_have_snapshot();
3325-
33263394 $from = 0 unless defined $from ;
33273395 $to = $# {$commitlist } if (!defined $to || $# {$commitlist } < $to );
33283396
@@ -3349,8 +3417,9 @@ sub git_shortlog_body {
33493417 $cgi -> a({-href => href(action => " commit" , hash => $commit )}, " commit" ) . " | " .
33503418 $cgi -> a({-href => href(action => " commitdiff" , hash => $commit )}, " commitdiff" ) . " | " .
33513419 $cgi -> a({-href => href(action => " tree" , hash => $commit , hash_base => $commit )}, " tree" );
3352- if ($have_snapshot ) {
3353- print " | " . $cgi -> a({-href => href(action => " snapshot" , hash => $commit )}, " snapshot" );
3420+ my $snapshot_links = format_snapshot_links($commit );
3421+ if (defined $snapshot_links ) {
3422+ print " | " . $snapshot_links ;
33543423 }
33553424 print " </td>\n " .
33563425 " </tr>\n " ;
@@ -4132,8 +4201,6 @@ sub git_blob {
41324201}
41334202
41344203sub git_tree {
4135- my $have_snapshot = gitweb_have_snapshot();
4136-
41374204 if (!defined $hash_base ) {
41384205 $hash_base = " HEAD" ;
41394206 }
@@ -4167,11 +4234,10 @@ sub git_tree {
41674234 hash_base => " HEAD" , file_name => $file_name )},
41684235 " HEAD" ),
41694236 }
4170- if ($have_snapshot ) {
4237+ my $snapshot_links = format_snapshot_links($hash );
4238+ if (defined $snapshot_links ) {
41714239 # FIXME: Should be available when we have no hash base as well.
4172- push @views_nav ,
4173- $cgi -> a({-href => href(action => " snapshot" , hash => $hash )},
4174- " snapshot" );
4240+ push @views_nav , $snapshot_links ;
41754241 }
41764242 git_print_page_nav(' tree' ,' ' , $hash_base , undef , undef , join (' | ' , @views_nav ));
41774243 git_print_header_div(' commit' , esc_html($co {' title' }) . $ref , $hash_base );
@@ -4235,33 +4301,36 @@ sub git_tree {
42354301}
42364302
42374303sub git_snapshot {
4238- my ($ctype , $suffix , $command ) = gitweb_check_feature(' snapshot' );
4239- my $have_snapshot = (defined $ctype && defined $suffix );
4240- if (!$have_snapshot ) {
4241- die_error(' 403 Permission denied' , " Permission denied" );
4304+ my @supported_fmts = gitweb_check_feature(' snapshot' );
4305+
4306+ my $format = $cgi -> param(' sf' );
4307+ unless ($format =~ m /[a-z0-9] +/
4308+ && exists ($known_snapshot_formats {$format })
4309+ && grep ($_ eq $format , @supported_fmts )) {
4310+ die_error(undef , " Unsupported snapshot format" );
42424311 }
42434312
42444313 if (!defined $hash ) {
42454314 $hash = git_get_head_hash($project );
42464315 }
42474316
4248- my $git = git_cmd_str();
4317+ my $git_command = git_cmd_str();
42494318 my $name = $project ;
42504319 $name =~ s ,([^/])/*\.git$, $1 ,;
42514320 $name = basename($name );
42524321 my $filename = to_utf8($name );
42534322 $name =~ s /\047 / \047\\\047\047 / g ;
42544323 my $cmd ;
4255- if ( $suffix eq ' zip ' ) {
4256- $filename . = " - $hash . $suffix " ;
4257- $cmd = " $git archive --format=zip --prefix= \' $name \' / $hash " ;
4258- } else {
4259- $filename .= " - $hash .tar. $suffix " ;
4260- $cmd = " $git archive --format=tar --prefix= \' $name \' / $hash | $command " ;
4324+ $filename .= " - $hash$known_snapshot_formats { $format }{'suffix'} " ;
4325+ $cmd = " $git_command archive " .
4326+ " --format=$known_snapshot_formats { $format }{'format'} " .
4327+ " --prefix= \' $name \' / $hash " ;
4328+ if ( exists $known_snapshot_formats { $format }{ ' compressor ' }) {
4329+ $cmd .= ' | ' . join ' ' , @{ $known_snapshot_formats { $format }{ ' compressor ' }} ;
42614330 }
42624331
42634332 print $cgi -> header(
4264- -type => " application/ $ctype " ,
4333+ -type => $known_snapshot_formats { $format }{ ' type ' } ,
42654334 -content_disposition => ' inline; filename="' . " $filename " . ' "' ,
42664335 -status => ' 200 OK' );
42674336
@@ -4271,7 +4340,6 @@ sub git_snapshot {
42714340 print <$fd >;
42724341 binmode STDOUT , ' :utf8' ; # as set at the beginning of gitweb.cgi
42734342 close $fd ;
4274-
42754343}
42764344
42774345sub git_log {
@@ -4390,8 +4458,6 @@ sub git_commit {
43904458 my $refs = git_get_references();
43914459 my $ref = format_ref_marker($refs , $co {' id' });
43924460
4393- my $have_snapshot = gitweb_have_snapshot();
4394-
43954461 git_header_html(undef , $expires );
43964462 git_print_page_nav(' commit' , ' ' ,
43974463 $hash , $co {' tree' }, $hash ,
@@ -4430,9 +4496,9 @@ sub git_commit {
44304496 " <td class=\" link\" >" .
44314497 $cgi -> a({-href => href(action => " tree" , hash => $co {' tree' }, hash_base => $hash )},
44324498 " tree" );
4433- if ( $have_snapshot ) {
4434- print " | " .
4435- $cgi -> a({- href => href( action => " snapshot " , hash => $hash )}, " snapshot " ) ;
4499+ my $snapshot_links = format_snapshot_links( $hash );
4500+ if ( defined $snapshot_links ) {
4501+ print " | " . $snapshot_links ;
44364502 }
44374503 print " </td>" .
44384504 " </tr>\n " ;
0 commit comments