translate reference into hash while reading a commit
[gitweb.git] / gitweb.cgi
index 8d4e4f702204d7a555117ab82ff64130cd4231c4..6a61c6555754817bf11f137875ff4736957337ea 100755 (executable)
@@ -15,14 +15,14 @@ use CGI::Carp qw(fatalsToBrowser);
 use Fcntl ':mode';
 
 my $cgi = new CGI;
-my $version =          "240";
+my $version =          "243";
 my $my_url =           $cgi->url();
 my $my_uri =           $cgi->url(-absolute => 1);
 my $rss_link = "";
 
 # absolute fs-path which will be prepended to the project path
-my $projectroot =      "/pub/scm";
-$projectroot = "/home/kay/public_html/pub/scm";
+#my $projectroot =     "/pub/scm";
+my $projectroot = "/home/kay/public_html/pub/scm";
 
 # location of the git-core binaries
 my $gitbin =           "/usr/bin";
@@ -43,7 +43,7 @@ my $projects_list = "index/index.aux";
 # input validation and dispatch
 my $action = $cgi->param('a');
 if (defined $action) {
-       if ($action =~ m/[^0-9a-zA-Z\.\-_]+/) {
+       if ($action =~ m/[^0-9a-zA-Z\.\-_]/) {
                undef $action;
                die_error(undef, "Invalid action parameter.");
        }
@@ -58,7 +58,7 @@ if (defined $action) {
 
 my $order = $cgi->param('o');
 if (defined $order) {
-       if ($order =~ m/[^a-zA-Z0-9_]/) {
+       if ($order =~ m/[^0-9a-zA-Z_]/) {
                undef $order;
                die_error(undef, "Invalid order parameter.");
        }
@@ -66,13 +66,9 @@ if (defined $order) {
 
 my $project = $cgi->param('p');
 if (defined $project) {
-       if ($project =~ m/(^|\/)(|\.|\.\.)($|\/)/) {
-               undef $project;
-               die_error(undef, "Non-canonical project parameter.");
-       }
-       if ($project =~ m/[^a-zA-Z0-9_\.\/\-\+\#\~]/) {
-               undef $project;
-               die_error(undef, "Invalid character in project parameter.");
+       $project = validate_input($project);
+       if (!defined($project)) {
+               die_error(undef, "Invalid project parameter.");
        }
        if (!(-d "$projectroot/$project")) {
                undef $project;
@@ -91,54 +87,39 @@ if (defined $project) {
 
 my $file_name = $cgi->param('f');
 if (defined $file_name) {
-       if ($file_name =~ m/(^|\/)(|\.|\.\.)($|\/)/) {
-               undef $file_name;
-               die_error(undef, "Non-canonical file parameter.");
-       }
-       if ($file_name =~ m/[^a-zA-Z0-9_\.\/\-\+\#\~\:\!]/) {
-               undef $file_name;
-               die_error(undef, "Invalid character in file parameter.");
+       $file_name = validate_input($file_name);
+       if (!defined($file_name)) {
+               die_error(undef, "Invalid file parameter.");
        }
 }
 
 my $hash = $cgi->param('h');
 if (defined $hash) {
-       if (!($hash =~ m/^[0-9a-fA-F]{40}$/)) {
-               if ($hash =~ m/(^|\/)(|\.|\.\.)($|\/)/) {
-                       undef $hash;
-                       die_error(undef, "Non-canonical hash parameter.");
-               }
-               if ($hash =~ m/[^a-zA-Z0-9_\.\/\-\+\#\~\:\!]/) {
-                       undef $hash;
-                       die_error(undef, "Invalid character in hash parameter.");
-               }
-               # replace branch-name with hash
-               my $branchlist = git_read_refs("refs/heads");
-               foreach my $entry (@$branchlist) {
-                       my %branch = %$entry;
-                       if ($branch{'name'} eq $hash) {
-                               $hash = $branch{'id'};
-                               last;
-                       }
-               }
+       $hash = validate_input($hash);
+       if (!defined($hash)) {
+               die_error(undef, "Invalid hash parameter.");
        }
 }
 
 my $hash_parent = $cgi->param('hp');
-if (defined $hash_parent && !($hash_parent =~ m/^[0-9a-fA-F]{40}$/)) {
-       undef $hash_parent;
-       die_error(undef, "Invalid hash_parent parameter.");
+if (defined $hash_parent) {
+       $hash_parent = validate_input($hash_parent);
+       if (!defined($hash_parent)) {
+               die_error(undef, "Invalid hash parent parameter.");
+       }
 }
 
 my $hash_base = $cgi->param('hb');
-if (defined $hash_base && !($hash_base =~ m/^[0-9a-fA-F]{40}$/)) {
-       undef $hash_base;
-       die_error(undef, "Invalid parent hash parameter.");
+if (defined $hash_base) {
+       $hash_base = validate_input($hash_base);
+       if (!defined($hash_base)) {
+               die_error(undef, "Invalid hash base parameter.");
+       }
 }
 
 my $page = $cgi->param('pg');
 if (defined $page) {
-       if ($page =~ m/^[^0-9]+$/) {
+       if ($page =~ m/[^0-9]$/) {
                undef $page;
                die_error(undef, "Invalid page parameter.");
        }
@@ -153,6 +134,21 @@ if (defined $searchtext) {
        $searchtext = quotemeta $searchtext;
 }
 
+sub validate_input {
+       my $input = shift;
+
+       if ($input =~ m/^[0-9a-fA-F]{40}$/) {
+               return $input;
+       }
+       if ($input =~ m/(^|\/)(|\.|\.\.)($|\/)/) {
+               return undef;
+       }
+       if ($input =~ m/[^a-zA-Z0-9_\.\/\-\+\#\~]/) {
+               return undef;
+       }
+       return $input;
+}
+
 if (!defined $action || $action eq "summary") {
        git_summary();
        exit;
@@ -482,13 +478,22 @@ sub git_read_commit {
        if (!defined $co{'tree'}) {
                return undef
        };
-       $co{'id'} = $commit_id;
+       if (!($commit_id =~ m/^[0-9a-fA-F]{40}$/)) {
+               # lookup hash by textual id
+               open my $fd, "-|", "$gitbin/git-rev-parse --verify $commit_id" or return;
+               my $hash_id = <$fd>;
+               close $fd or return;
+               chomp $hash_id;
+               $co{'id'} = $hash_id
+       } else {
+               $co{'id'} = $commit_id;
+       }
        $co{'parents'} = \@parents;
        $co{'parent'} = $parents[0];
        $co{'comment'} = \@commit_lines;
        foreach my $title (@commit_lines) {
                if ($title ne "") {
-                       $co{'title'} = chop_str($title, 80);
+                       $co{'title'} = chop_str($title, 80, 5);
                        # remove leading stuff of merges to make the interesting part visible
                        if (length($title) > 50) {
                                $title =~ s/^Automatic //;
@@ -506,7 +511,7 @@ sub git_read_commit {
                                        $title =~ s/\/pub\/scm//;
                                }
                        }
-                       $co{'title_short'} = chop_str($title, 50);
+                       $co{'title_short'} = chop_str($title, 50, 5);
                        last;
                }
        }
@@ -621,12 +626,15 @@ sub chop_str {
        my $len = shift;
        my $add_len = shift || 10;
 
-       $str =~ m/^(.{0,$len}[^ \/\-_:\.@]{0,$add_len})/;
-       my $chopped = $1;
-       if ($chopped ne $str) {
-               $chopped .= " ...";
+       # allow only $len chars, but don't cut a word if it would fit in $add_len
+       # if it doesn't fit, cut it if it's still longer than the dots we would add
+       $str =~ m/^(.{0,$len}[^ \/\-_:\.@]{0,$add_len})(.*)/;
+       my $body = $1;
+       my $tail = $2;
+       if (length($tail) > 4) {
+               $tail = " ...";
        }
-       return $chopped;
+       return "$body$tail";
 }
 
 sub file_type {
@@ -977,10 +985,15 @@ sub git_summary {
                if ($i-- > 0) {
                        print "<td><i>$co{'age_string'}</i></td>\n" .
                              "<td><i>" . escapeHTML(chop_str($co{'author_name'}, 10)) . "</i></td>\n" .
-                             "<td>" .
-                             $cgi->a({-href => "$my_uri?p=$project;a=commit;h=$commit", -class => "list"},
-                             "<b>" . escapeHTML($co{'title_short'}) . "</b>") .
-                             "</td>\n" .
+                             "<td>";
+                       if (length($co{'title_short'}) < length($co{'title'})) {
+                               print $cgi->a({-href => "$my_uri?p=$project;a=commit;h=$commit", -class => "list", -title => "$co{'title'}"},
+                                     "<b>" . escapeHTML($co{'title_short'}) . "</b>");
+                       } else {
+                               print $cgi->a({-href => "$my_uri?p=$project;a=commit;h=$commit", -class => "list"},
+                                     "<b>" . escapeHTML($co{'title'}) . "</b>");
+                       }
+                       print "</td>\n" .
                              "<td class=\"link\">" .
                              $cgi->a({-href => "$my_uri?p=$project;a=commit;h=$commit"}, "commit") .
                              " | " . $cgi->a({-href => "$my_uri?p=$project;a=commitdiff;h=$commit"}, "commitdiff") .
@@ -1424,7 +1437,9 @@ sub git_rss {
                      "<title>" .
                      sprintf("%d %s %02d:%02d", $cd{'mday'}, $cd{'month'}, $cd{'hour'}, $cd{'minute'}) . " - " . escapeHTML($co{'title'}) .
                      "</title>\n" .
+                     "<author>" . escapeHTML($co{'author'}) . "</author>\n" .
                      "<pubDate>$cd{'rfc2822'}</pubDate>\n" .
+                     "<guid isPermaLink=\"true\">" . escapeHTML("$my_url?p=$project;a=commit;h=$commit") . "</guid>\n" .
                      "<link>" . escapeHTML("$my_url?p=$project;a=commit;h=$commit") . "</link>\n" .
                      "<description>" . escapeHTML($co{'title'}) . "</description>\n" .
                      "<content:encoded>" .
@@ -1624,7 +1639,7 @@ sub git_commit {
              "</tr>\n";
        print "<tr><td>committer</td><td>" . escapeHTML($co{'committer'}) . "</td></tr>\n";
        print "<tr><td></td><td> $cd{'rfc2822'}" . sprintf(" (%02d:%02d %s)", $cd{'hour_local'}, $cd{'minute_local'}, $cd{'tz_local'}) . "</td></tr>\n";
-       print "<tr><td>commit</td><td style=\"font-family:monospace\">$hash</td></tr>\n";
+       print "<tr><td>commit</td><td style=\"font-family:monospace\">$co{'id'}</td></tr>\n";
        print "<tr>" .
              "<td>tree</td>" .
              "<td style=\"font-family:monospace\">" .
@@ -2227,8 +2242,15 @@ sub git_shortlog {
                $alternate ^= 1;
                print "<td title=\"$co{'age_string_age'}\"><i>$co{'age_string_date'}</i></td>\n" .
                      "<td><i>" . escapeHTML(chop_str($co{'author_name'}, 10)) . "</i></td>\n" .
-                     "<td>" . $cgi->a({-href => "$my_uri?p=$project;a=commit;h=$commit", -class => "list"}, "<b>" .
-                     escapeHTML($co{'title_short'}) . "</b>") . "</td>\n" .
+                     "<td>";
+               if (length($co{'title_short'}) < length($co{'title'})) {
+                       print $cgi->a({-href => "$my_uri?p=$project;a=commit;h=$commit", -class => "list", -title => "$co{'title'}"},
+                             "<b>" . escapeHTML($co{'title_short'}) . "</b>");
+               } else {
+                       print $cgi->a({-href => "$my_uri?p=$project;a=commit;h=$commit", -class => "list"},
+                             "<b>" . escapeHTML($co{'title_short'}) . "</b>");
+               }
+               print "</td>\n" .
                      "<td class=\"link\">" .
                      $cgi->a({-href => "$my_uri?p=$project;a=commit;h=$commit"}, "commit") .
                      " | " . $cgi->a({-href => "$my_uri?p=$project;a=commitdiff;h=$commit"}, "commitdiff") .