Merge branch 'jc/for-each-ref' into jc/ref-locking
[gitweb.git] / gitweb / gitweb.perl
index f75fea5623f5e19a69653ecb0629634934b1728e..c77270c7cd7ef478f708dbbc6165ac420891ca5a 100755 (executable)
 # source of projects list
 our $projects_list = "++GITWEB_LIST++";
 
+# show repository only if this file exists
+# (only effective if this variable evaluates to true)
+our $export_ok = "++GITWEB_EXPORT_OK++";
+
+# only allow viewing of repositories also shown on the overview page
+our $strict_export = "++GITWEB_STRICT_EXPORT++";
+
 # list of git base URLs used for URL to where fetch project from,
 # i.e. full URL is "$git_base_url/$project"
 our @git_base_url_list = ("++GITWEB_BASE_URL++");
@@ -182,9 +189,6 @@ sub feature_pickaxe {
 # version of the core git binary
 our $git_version = qx($GIT --version) =~ m/git version (.*)$/ ? $1 : "unknown";
 
-# path to the current git repository
-our $git_dir;
-
 $projects_list ||= $projectroot;
 
 # ======================================================================
@@ -196,23 +200,16 @@ sub feature_pickaxe {
        }
 }
 
-our $project = ($cgi->param('p') || $ENV{'PATH_INFO'});
+our $project = $cgi->param('p');
 if (defined $project) {
-       $project =~ s|^/||;
-       $project =~ s|/$||;
-       $project = undef unless $project;
-}
-if (defined $project) {
-       if (!validate_input($project)) {
-               die_error(undef, "Invalid project parameter");
-       }
-       if (!(-d "$projectroot/$project")) {
-               die_error(undef, "No such directory");
-       }
-       if (!(-e "$projectroot/$project/HEAD")) {
+       if (!validate_input($project) ||
+           !(-d "$projectroot/$project") ||
+           !(-e "$projectroot/$project/HEAD") ||
+           ($export_ok && !(-e "$projectroot/$project/$export_ok")) ||
+           ($strict_export && !project_in_list($project))) {
+               undef $project;
                die_error(undef, "No such project");
        }
-       $git_dir = "$projectroot/$project";
 }
 
 our $file_name = $cgi->param('f');
@@ -259,7 +256,7 @@ sub feature_pickaxe {
 
 our $page = $cgi->param('pg');
 if (defined $page) {
-       if ($page =~ m/[^0-9]$/) {
+       if ($page =~ m/[^0-9]/) {
                die_error(undef, "Invalid page parameter");
        }
 }
@@ -272,6 +269,43 @@ sub feature_pickaxe {
        $searchtext = quotemeta $searchtext;
 }
 
+# now read PATH_INFO and use it as alternative to parameters
+sub evaluate_path_info {
+       return if defined $project;
+       my $path_info = $ENV{"PATH_INFO"};
+       return if !$path_info;
+       $path_info =~ s,(^/|/$),,gs;
+       $path_info = validate_input($path_info);
+       return if !$path_info;
+       $project = $path_info;
+       while ($project && !-e "$projectroot/$project/HEAD") {
+               $project =~ s,/*[^/]*$,,;
+       }
+       if (!$project ||
+           ($export_ok && !-e "$projectroot/$project/$export_ok") ||
+           ($strict_export && !project_in_list($project))) {
+               undef $project;
+               return;
+       }
+       # do not change any parameters if an action is given using the query string
+       return if $action;
+       if ($path_info =~ m,^$project/([^/]+)/(.+)$,) {
+               # we got "project.git/branch/filename"
+               $action    ||= "blob_plain";
+               $hash_base ||= validate_input($1);
+               $file_name ||= validate_input($2);
+       } elsif ($path_info =~ m,^$project/([^/]+)$,) {
+               # we got "project.git/branch"
+               $action ||= "shortlog";
+               $hash   ||= validate_input($1);
+       }
+}
+evaluate_path_info();
+
+# path to the current git repository
+our $git_dir;
+$git_dir = "$projectroot/$project" if $project;
+
 # dispatch
 my %actions = (
        "blame" => \&git_blame2,
@@ -405,6 +439,12 @@ sub untabify {
        return $line;
 }
 
+sub project_in_list {
+       my $project = shift;
+       my @list = git_get_projects_list();
+       return @list && scalar(grep { $_->{'path'} eq $project } @list);
+}
+
 ## ----------------------------------------------------------------------
 ## HTML aware string manipulation
 
@@ -717,7 +757,8 @@ sub git_get_projects_list {
 
                                my $subdir = substr($File::Find::name, $pfxlen + 1);
                                # we check related file in $projectroot
-                               if (-e "$projectroot/$subdir/HEAD") {
+                               if (-e "$projectroot/$subdir/HEAD" && (!$export_ok ||
+                                   -e "$projectroot/$subdir/$export_ok")) {
                                        push @list, { path => $subdir };
                                        $File::Find::prune = 1;
                                }
@@ -738,7 +779,8 @@ sub git_get_projects_list {
                        if (!defined $path) {
                                next;
                        }
-                       if (-e "$projectroot/$path/HEAD") {
+                       if (-e "$projectroot/$path/HEAD" && (!$export_ok ||
+                           -e "$projectroot/$path/$export_ok")) {
                                my $pr = {
                                        path => $path,
                                        owner => decode("utf8", $owner, Encode::FB_DEFAULT),
@@ -1255,6 +1297,13 @@ sub git_header_html {
                printf('<link rel="alternate" title="%s log" '.
                       'href="%s" type="application/rss+xml"/>'."\n",
                       esc_param($project), href(action=>"rss"));
+       } else {
+               printf('<link rel="alternate" title="%s projects list" '.
+                      'href="%s" type="text/plain; charset=utf-8"/>'."\n",
+                      $site_name, href(project=>undef, action=>"project_index"));
+               printf('<link rel="alternate" title="%s projects logs" '.
+                      'href="%s" type="text/x-opml"/>'."\n",
+                      $site_name, href(project=>undef, action=>"opml"));
        }
        if (defined $favicon) {
                print qq(<link rel="shortcut icon" href="$favicon" type="image/png"/>\n);
@@ -1309,7 +1358,9 @@ sub git_footer_html {
                              -class => "rss_logo"}, "RSS") . "\n";
        } else {
                print $cgi->a({-href => href(project=>undef, action=>"opml"),
-                             -class => "rss_logo"}, "OPML") . "\n";
+                             -class => "rss_logo"}, "OPML") . " ";
+               print $cgi->a({-href => href(project=>undef, action=>"project_index"),
+                             -class => "rss_logo"}, "TXT") . "\n";
        }
        print "</div>\n" .
              "</body>\n" .
@@ -2518,11 +2569,7 @@ sub git_heads {
 }
 
 sub git_blob_plain {
-       # blobs defined by non-textual hash id's can be cached
        my $expires;
-       if ($hash =~ m/^[0-9a-fA-F]{40}$/) {
-               $expires = "+1d";
-       }
 
        if (!defined $hash) {
                if (defined $file_name) {
@@ -2532,7 +2579,11 @@ sub git_blob_plain {
                } else {
                        die_error(undef, "No file name defined");
                }
+       } elsif ($hash =~ m/^[0-9a-fA-F]{40}$/) {
+               # blobs defined by non-textual hash id's can be cached
+               $expires = "+1d";
        }
+
        my $type = shift;
        open my $fd, "-|", git_cmd(), "cat-file", "blob", $hash
                or die_error(undef, "Couldn't cat $file_name, $hash");
@@ -2560,11 +2611,7 @@ sub git_blob_plain {
 }
 
 sub git_blob {
-       # blobs defined by non-textual hash id's can be cached
        my $expires;
-       if ($hash =~ m/^[0-9a-fA-F]{40}$/) {
-               $expires = "+1d";
-       }
 
        if (!defined $hash) {
                if (defined $file_name) {
@@ -2574,7 +2621,11 @@ sub git_blob {
                } else {
                        die_error(undef, "No file name defined");
                }
+       } elsif ($hash =~ m/^[0-9a-fA-F]{40}$/) {
+               # blobs defined by non-textual hash id's can be cached
+               $expires = "+1d";
        }
+
        my ($have_blame) = gitweb_check_feature('blame');
        open my $fd, "-|", git_cmd(), "cat-file", "blob", $hash
                or die_error(undef, "Couldn't cat $file_name, $hash");