Merge branch 'gb/gitweb-snapshot-pathinfo'
authorJunio C Hamano <gitster@pobox.com>
Thu, 13 Nov 2008 06:27:49 +0000 (22:27 -0800)
committerJunio C Hamano <gitster@pobox.com>
Thu, 13 Nov 2008 06:27:49 +0000 (22:27 -0800)
* gb/gitweb-snapshot-pathinfo:
gitweb: embed snapshot format parameter in PATH_INFO
gitweb: retrieve snapshot format from PATH_INFO
gitweb: make the supported snapshot formats array global

1  2 
gitweb/gitweb.perl
diff --combined gitweb/gitweb.perl
index 06da30c50186eaaaba59dde91b8b8a34f6b89840,e2ed1ccab34abfb3e28568c43419648067de5830..d4c500b3b3668e388a9bd0fdbd4129e292268ee2
@@@ -30,7 -30,7 +30,7 @@@ our $my_uri = $cgi->url(-absolute => 1)
  # if we're called with PATH_INFO, we have to strip that
  # from the URL to find our real URL
  # we make $path_info global because it's also used later on
 -my $path_info = $ENV{"PATH_INFO"};
 +our $path_info = $ENV{"PATH_INFO"};
  if ($path_info) {
        $my_url =~ s,\Q$path_info\E$,,;
        $my_uri =~ s,\Q$path_info\E$,,;
@@@ -95,11 -95,6 +95,11 @@@ our $default_projects_order = "project"
  # (only effective if this variable evaluates to true)
  our $export_ok = "++GITWEB_EXPORT_OK++";
  
 +# show repository only if this subroutine returns true
 +# when given the path to the project, for example:
 +#    sub { return -e "$_[0]/git-daemon-export-ok"; }
 +our $export_auth_hook = undef;
 +
  # only allow viewing of repositories also shown on the overview page
  our $strict_export = "++GITWEB_STRICT_EXPORT++";
  
@@@ -405,8 -400,7 +405,8 @@@ sub check_head_link 
  sub check_export_ok {
        my ($dir) = @_;
        return (check_head_link($dir) &&
 -              (!$export_ok || -e "$dir/$export_ok"));
 +              (!$export_ok || -e "$dir/$export_ok") &&
 +              (!$export_auth_hook || $export_auth_hook->($dir)));
  }
  
  # process alternate names for backward compatibility
@@@ -442,7 -436,7 +442,7 @@@ $projects_list ||= $projectroot
  # together during validation: this allows subsequent uses (e.g. href()) to be
  # agnostic of the parameter origin
  
 -my %input_params = ();
 +our %input_params = ();
  
  # input parameters are stored with the long parameter name as key. This will
  # also be used in the href subroutine to convert parameters to their CGI
  # XXX: Warning: If you touch this, check the search form for updating,
  # too.
  
 -my @cgi_param_mapping = (
 +our @cgi_param_mapping = (
        project => "p",
        action => "a",
        file_name => "f",
        extra_options => "opt",
        search_use_regexp => "sr",
  );
 -my %cgi_param_mapping = @cgi_param_mapping;
 +our %cgi_param_mapping = @cgi_param_mapping;
  
  # we will also need to know the possible actions, for validation
 -my %actions = (
 +our %actions = (
        "blame" => \&git_blame,
        "blobdiff" => \&git_blobdiff,
        "blobdiff_plain" => \&git_blobdiff_plain,
  
  # finally, we have the hash of allowed extra_options for the commands that
  # allow them
 -my %allowed_options = (
 +our %allowed_options = (
        "--no-merges" => [ qw(rss atom log shortlog history) ],
  );
  
@@@ -622,6 -616,45 +622,45 @@@ sub evaluate_path_info 
                        $input_params{'hash_parent'} ||= $parentrefname;
                }
        }
+       # for the snapshot action, we allow URLs in the form
+       # $project/snapshot/$hash.ext
+       # where .ext determines the snapshot and gets removed from the
+       # passed $refname to provide the $hash.
+       #
+       # To be able to tell that $refname includes the format extension, we
+       # require the following two conditions to be satisfied:
+       # - the hash input parameter MUST have been set from the $refname part
+       #   of the URL (i.e. they must be equal)
+       # - the snapshot format MUST NOT have been defined already (e.g. from
+       #   CGI parameter sf)
+       # It's also useless to try any matching unless $refname has a dot,
+       # so we check for that too
+       if (defined $input_params{'action'} &&
+               $input_params{'action'} eq 'snapshot' &&
+               defined $refname && index($refname, '.') != -1 &&
+               $refname eq $input_params{'hash'} &&
+               !defined $input_params{'snapshot_format'}) {
+               # We loop over the known snapshot formats, checking for
+               # extensions. Allowed extensions are both the defined suffix
+               # (which includes the initial dot already) and the snapshot
+               # format key itself, with a prepended dot
+               while (my ($fmt, %opt) = each %known_snapshot_formats) {
+                       my $hash = $refname;
+                       my $sfx;
+                       $hash =~ s/(\Q$opt{'suffix'}\E|\Q.$fmt\E)$//;
+                       next unless $sfx = $1;
+                       # a valid suffix was found, so set the snapshot format
+                       # and reset the hash parameter
+                       $input_params{'snapshot_format'} = $fmt;
+                       $input_params{'hash'} = $hash;
+                       # we also set the format suffix to the one requested
+                       # in the URL: this way a request for e.g. .tgz returns
+                       # a .tgz instead of a .tar.gz
+                       $known_snapshot_formats{$fmt}{'suffix'} = $sfx;
+                       last;
+               }
+       }
  }
  evaluate_path_info();
  
@@@ -727,6 -760,10 +766,10 @@@ if (defined $searchtext) 
  our $git_dir;
  $git_dir = "$projectroot/$project" if $project;
  
+ # list of supported snapshot formats
+ our @snapshot_fmts = gitweb_check_feature('snapshot');
+ @snapshot_fmts = filter_snapshot_fmts(@snapshot_fmts);
  # dispatch
  if (!defined $action) {
        if (defined $hash) {
@@@ -774,6 -811,7 +817,7 @@@ sub href (%) 
                #   - action
                #   - hash_parent or hash_parent_base:/file_parent
                #   - hash or hash_base:/filename
+               #   - the snapshot_format as an appropriate suffix
  
                # When the script is the root DirectoryIndex for the domain,
                # $href here would be something like http://gitweb.example.com/
                $href .= "/".esc_url($params{'project'}) if defined $params{'project'};
                delete $params{'project'};
  
+               # since we destructively absorb parameters, we keep this
+               # boolean that remembers if we're handling a snapshot
+               my $is_snapshot = $params{'action'} eq 'snapshot';
                # Summary just uses the project path URL, any other action is
                # added to the URL
                if (defined $params{'action'}) {
                        $href .= esc_url($params{'hash'});
                        delete $params{'hash'};
                }
+               # If the action was a snapshot, we can absorb the
+               # snapshot_format parameter too
+               if ($is_snapshot) {
+                       my $fmt = $params{'snapshot_format'};
+                       # snapshot_format should always be defined when href()
+                       # is called, but just in case some code forgets, we
+                       # fall back to the default
+                       $fmt ||= $snapshot_fmts[0];
+                       $href .= $known_snapshot_formats{$fmt}{'suffix'};
+                       delete $params{'snapshot_format'};
+               }
        }
  
        # now encode the parameters explicitly
@@@ -859,7 -913,8 +919,7 @@@ sub validate_project 
        my $input = shift || return undef;
        if (!validate_pathname($input) ||
                !(-d "$projectroot/$input") ||
 -              !check_head_link("$projectroot/$input") ||
 -              ($export_ok && !(-e "$projectroot/$input/$export_ok")) ||
 +              !check_export_ok("$projectroot/$input") ||
                ($strict_export && !project_in_list($input))) {
                return undef;
        } else {
@@@ -1652,8 -1707,6 +1712,6 @@@ sub format_diff_line 
  # linked.  Pass the hash of the tree/commit to snapshot.
  sub format_snapshot_links {
        my ($hash) = @_;
-       my @snapshot_fmts = gitweb_check_feature('snapshot');
-       @snapshot_fmts = filter_snapshot_fmts(@snapshot_fmts);
        my $num_fmts = @snapshot_fmts;
        if ($num_fmts > 1) {
                # A parenthesized list of links bearing format names.
@@@ -1962,10 -2015,7 +2020,10 @@@ sub git_get_project_ctags 
        my $ctags = {};
  
        $git_dir = "$projectroot/$path";
 -      foreach (<$git_dir/ctags/*>) {
 +      unless (opendir D, "$git_dir/ctags") {
 +              return $ctags;
 +      }
 +      foreach (grep { -f $_ } map { "$git_dir/ctags/$_" } readdir(D)) {
                open CT, $_ or next;
                my $val = <CT>;
                chomp $val;
                my $ctag = $_; $ctag =~ s#.*/##;
                $ctags->{$ctag} = $val;
        }
 +      closedir D;
        $ctags;
  }
  
@@@ -4855,20 -4904,17 +4913,17 @@@ sub git_tree 
  }
  
  sub git_snapshot {
-       my @supported_fmts = gitweb_check_feature('snapshot');
-       @supported_fmts = filter_snapshot_fmts(@supported_fmts);
        my $format = $input_params{'snapshot_format'};
-       if (!@supported_fmts) {
+       if (!@snapshot_fmts) {
                die_error(403, "Snapshots not allowed");
        }
        # default to first supported snapshot format
-       $format ||= $supported_fmts[0];
+       $format ||= $snapshot_fmts[0];
        if ($format !~ m/^[a-z0-9]+$/) {
                die_error(400, "Invalid snapshot format parameter");
        } elsif (!exists($known_snapshot_formats{$format})) {
                die_error(400, "Unknown snapshot format");
-       } elsif (!grep($_ eq $format, @supported_fmts)) {
+       } elsif (!grep($_ eq $format, @snapshot_fmts)) {
                die_error(403, "Unsupported snapshot format");
        }