@@ -622,6 +622,45 @@ sub evaluate_path_info {
622622 $input_params {' hash_parent' } ||= $parentrefname ;
623623 }
624624 }
625+
626+ # for the snapshot action, we allow URLs in the form
627+ # $project/snapshot/$hash.ext
628+ # where .ext determines the snapshot and gets removed from the
629+ # passed $refname to provide the $hash.
630+ #
631+ # To be able to tell that $refname includes the format extension, we
632+ # require the following two conditions to be satisfied:
633+ # - the hash input parameter MUST have been set from the $refname part
634+ # of the URL (i.e. they must be equal)
635+ # - the snapshot format MUST NOT have been defined already (e.g. from
636+ # CGI parameter sf)
637+ # It's also useless to try any matching unless $refname has a dot,
638+ # so we check for that too
639+ if (defined $input_params {' action' } &&
640+ $input_params {' action' } eq ' snapshot' &&
641+ defined $refname && index ($refname , ' .' ) != -1 &&
642+ $refname eq $input_params {' hash' } &&
643+ !defined $input_params {' snapshot_format' }) {
644+ # We loop over the known snapshot formats, checking for
645+ # extensions. Allowed extensions are both the defined suffix
646+ # (which includes the initial dot already) and the snapshot
647+ # format key itself, with a prepended dot
648+ while (my ($fmt , %opt ) = each %known_snapshot_formats ) {
649+ my $hash = $refname ;
650+ my $sfx ;
651+ $hash =~ s / (\Q $opt{'suffix'}\E |\Q .$fmt\E )$// ;
652+ next unless $sfx = $1 ;
653+ # a valid suffix was found, so set the snapshot format
654+ # and reset the hash parameter
655+ $input_params {' snapshot_format' } = $fmt ;
656+ $input_params {' hash' } = $hash ;
657+ # we also set the format suffix to the one requested
658+ # in the URL: this way a request for e.g. .tgz returns
659+ # a .tgz instead of a .tar.gz
660+ $known_snapshot_formats {$fmt }{' suffix' } = $sfx ;
661+ last ;
662+ }
663+ }
625664}
626665evaluate_path_info();
627666
@@ -727,6 +766,10 @@ sub evaluate_path_info {
727766our $git_dir ;
728767$git_dir = " $projectroot /$project " if $project ;
729768
769+ # list of supported snapshot formats
770+ our @snapshot_fmts = gitweb_check_feature(' snapshot' );
771+ @snapshot_fmts = filter_snapshot_fmts(@snapshot_fmts );
772+
730773# dispatch
731774if (!defined $action ) {
732775 if (defined $hash ) {
774817 # - action
775818 # - hash_parent or hash_parent_base:/file_parent
776819 # - hash or hash_base:/filename
820+ # - the snapshot_format as an appropriate suffix
777821
778822 # When the script is the root DirectoryIndex for the domain,
779823 # $href here would be something like http://gitweb.example.com/
785829 $href .= " /" .esc_url($params {' project' }) if defined $params {' project' };
786830 delete $params {' project' };
787831
832+ # since we destructively absorb parameters, we keep this
833+ # boolean that remembers if we're handling a snapshot
834+ my $is_snapshot = $params {' action' } eq ' snapshot' ;
835+
788836 # Summary just uses the project path URL, any other action is
789837 # added to the URL
790838 if (defined $params {' action' }) {
824872 $href .= esc_url($params {' hash' });
825873 delete $params {' hash' };
826874 }
875+
876+ # If the action was a snapshot, we can absorb the
877+ # snapshot_format parameter too
878+ if ($is_snapshot ) {
879+ my $fmt = $params {' snapshot_format' };
880+ # snapshot_format should always be defined when href()
881+ # is called, but just in case some code forgets, we
882+ # fall back to the default
883+ $fmt ||= $snapshot_fmts [0];
884+ $href .= $known_snapshot_formats {$fmt }{' suffix' };
885+ delete $params {' snapshot_format' };
886+ }
827887 }
828888
829889 # now encode the parameters explicitly
@@ -1652,8 +1712,6 @@ sub format_diff_line {
16521712# linked. Pass the hash of the tree/commit to snapshot.
16531713sub format_snapshot_links {
16541714 my ($hash ) = @_ ;
1655- my @snapshot_fmts = gitweb_check_feature(' snapshot' );
1656- @snapshot_fmts = filter_snapshot_fmts(@snapshot_fmts );
16571715 my $num_fmts = @snapshot_fmts ;
16581716 if ($num_fmts > 1) {
16591717 # A parenthesized list of links bearing format names.
@@ -4855,20 +4913,17 @@ sub git_tree {
48554913}
48564914
48574915sub git_snapshot {
4858- my @supported_fmts = gitweb_check_feature(' snapshot' );
4859- @supported_fmts = filter_snapshot_fmts(@supported_fmts );
4860-
48614916 my $format = $input_params {' snapshot_format' };
4862- if (!@supported_fmts ) {
4917+ if (!@snapshot_fmts ) {
48634918 die_error(403, " Snapshots not allowed" );
48644919 }
48654920 # default to first supported snapshot format
4866- $format ||= $supported_fmts [0];
4921+ $format ||= $snapshot_fmts [0];
48674922 if ($format !~ m / ^[a-z0-9] +$ / ) {
48684923 die_error(400, " Invalid snapshot format parameter" );
48694924 } elsif (!exists ($known_snapshot_formats {$format })) {
48704925 die_error(400, " Unknown snapshot format" );
4871- } elsif (!grep ($_ eq $format , @supported_fmts )) {
4926+ } elsif (!grep ($_ eq $format , @snapshot_fmts )) {
48724927 die_error(403, " Unsupported snapshot format" );
48734928 }
48744929
0 commit comments