]> Lady’s Gitweb - Gitweb/blobdiff - gitweb.perl
gitweb: remove title shortening heuristics
[Gitweb] / gitweb.perl
index 87c8dc000518f3bccda21db160d175e305014f1da82383fe2930e2be3a5042c4..8c96a1dd0ab78384a91c40b8cde1a09478a2b6fd58f6127f3bd05e3657c4b807 100755 (executable)
@@ -570,6 +570,15 @@ our %feature = (
                'sub' => \&feature_extra_branch_refs,
                'override' => 0,
                'default' => []},
+
+       # Redact e-mail addresses.
+
+       # To enable system wide have in $GITWEB_CONFIG
+       # $feature{'email-privacy'}{'default'} = [1];
+       'email-privacy' => {
+               'sub' => sub { feature_bool('email-privacy', @_) },
+               'override' => 1,
+               'default' => [0]},
 );
 
 sub gitweb_get_feature {
@@ -742,7 +751,7 @@ sub evaluate_gitweb_config {
        $GITWEB_CONFIG_SYSTEM = "" if ($GITWEB_CONFIG_SYSTEM eq $GITWEB_CONFIG_COMMON);
 
        # Common system-wide settings for convenience.
-       # Those settings can be ovverriden by GITWEB_CONFIG or GITWEB_CONFIG_SYSTEM.
+       # Those settings can be overridden by GITWEB_CONFIG or GITWEB_CONFIG_SYSTEM.
        read_config_file($GITWEB_CONFIG_COMMON);
 
        # Use first config file that exists.  This means use the per-instance
@@ -1292,9 +1301,23 @@ our $is_last_request = sub { 1 };
 our ($pre_dispatch_hook, $post_dispatch_hook, $pre_listen_hook);
 our $CGI = 'CGI';
 our $cgi;
+our $FCGI_Stream_PRINT_raw = \&FCGI::Stream::PRINT;
 sub configure_as_fcgi {
        require CGI::Fast;
        our $CGI = 'CGI::Fast';
+       # FCGI is not Unicode aware hence the UTF-8 encoding must be done manually.
+       # However no encoding must be done within git_blob_plain() and git_snapshot()
+       # which must still output in raw binary mode.
+       no warnings 'redefine';
+       my $enc = Encode::find_encoding('UTF-8');
+       *FCGI::Stream::PRINT = sub {
+               my @OUTPUT = @_;
+               for (my $i = 1; $i < @_; $i++) {
+                       $OUTPUT[$i] = $enc->encode($_[$i], Encode::FB_CROAK|Encode::LEAVE_SRC);
+               }
+               @_ = @OUTPUT;
+               goto $FCGI_Stream_PRINT_raw;
+       };
 
        my $request_number = 0;
        # let each child service 100 requests
@@ -3436,6 +3459,13 @@ sub parse_date {
        return %date;
 }
 
+sub hide_mailaddrs_if_private {
+       my $line = shift;
+       return $line unless gitweb_check_feature('email-privacy');
+       $line =~ s/<[^@>]+@[^>]+>/<redacted>/g;
+       return $line;
+}
+
 sub parse_tag {
        my $tag_id = shift;
        my %tag;
@@ -3452,7 +3482,7 @@ sub parse_tag {
                } elsif ($line =~ m/^tag (.+)$/) {
                        $tag{'name'} = $1;
                } elsif ($line =~ m/^tagger (.*) ([0-9]+) (.*)$/) {
-                       $tag{'author'} = $1;
+                       $tag{'author'} = hide_mailaddrs_if_private($1);
                        $tag{'author_epoch'} = $2;
                        $tag{'author_tz'} = $3;
                        if ($tag{'author'} =~ m/^([^<]+) <([^>]*)>/) {
@@ -3500,7 +3530,7 @@ sub parse_commit_text {
                } elsif ((!defined $withparents) && ($line =~ m/^parent ($oid_regex)$/)) {
                        push @parents, $1;
                } elsif ($line =~ m/^author (.*) ([0-9]+) (.*)$/) {
-                       $co{'author'} = to_utf8($1);
+                       $co{'author'} = hide_mailaddrs_if_private(to_utf8($1));
                        $co{'author_epoch'} = $2;
                        $co{'author_tz'} = $3;
                        if ($co{'author'} =~ m/^([^<]+) <([^>]*)>/) {
@@ -3510,7 +3540,7 @@ sub parse_commit_text {
                                $co{'author_name'} = $co{'author'};
                        }
                } elsif ($line =~ m/^committer (.*) ([0-9]+) (.*)$/) {
-                       $co{'committer'} = to_utf8($1);
+                       $co{'committer'} = hide_mailaddrs_if_private(to_utf8($1));
                        $co{'committer_epoch'} = $2;
                        $co{'committer_tz'} = $3;
                        if ($co{'committer'} =~ m/^([^<]+) <([^>]*)>/) {
@@ -3531,23 +3561,6 @@ sub parse_commit_text {
                $title =~ s/^    //;
                if ($title ne "") {
                        $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 //;
-                               $title =~ s/^merge (of|with) /Merge ... /i;
-                               if (length($title) > 50) {
-                                       $title =~ s/(http|rsync):\/\///;
-                               }
-                               if (length($title) > 50) {
-                                       $title =~ s/(master|www|rsync)\.//;
-                               }
-                               if (length($title) > 50) {
-                                       $title =~ s/kernel.org:?//;
-                               }
-                               if (length($title) > 50) {
-                                       $title =~ s/\/pub\/scm//;
-                               }
-                       }
                        $co{'title_short'} = chop_str($title, 50, 5);
                        last;
                }
@@ -3555,9 +3568,10 @@ sub parse_commit_text {
        if (! defined $co{'title'} || $co{'title'} eq "") {
                $co{'title'} = $co{'title_short'} = '(no commit message)';
        }
-       # remove added spaces
+       # remove added spaces, redact e-mail addresses if applicable.
        foreach my $line (@commit_lines) {
                $line =~ s/^    //;
+               $line = hide_mailaddrs_if_private($line);
        }
        $co{'comment'} = \@commit_lines;
 
@@ -3766,7 +3780,8 @@ sub git_get_heads_list {
        my @headslist;
 
        open my $fd, '-|', git_cmd(), 'for-each-ref',
-               ($limit ? '--count='.($limit+1) : ()), '--sort=-committerdate',
+               ($limit ? '--count='.($limit+1) : ()),
+               '--sort=-HEAD', '--sort=-committerdate',
                '--format=%(objectname) %(refname) %(subject)%00%(committer)',
                @patterns
                or return;
@@ -4049,7 +4064,7 @@ sub print_feed_meta {
 
                        $href_params{'extra_options'} = undef;
                        $href_params{'action'} = $type;
-                       $link_attr{'-href'} = href(%href_params);
+                       $link_attr{'-href'} = esc_attr(href(%href_params));
                        print "<link ".
                              "rel=\"$link_attr{'-rel'}\" ".
                              "title=\"$link_attr{'-title'}\" ".
@@ -4058,7 +4073,7 @@ sub print_feed_meta {
                              "/>\n";
 
                        $href_params{'extra_options'} = '--no-merges';
-                       $link_attr{'-href'} = href(%href_params);
+                       $link_attr{'-href'} = esc_attr(href(%href_params));
                        $link_attr{'-title'} .= ' (no merges)';
                        print "<link ".
                              "rel=\"$link_attr{'-rel'}\" ".
@@ -4071,10 +4086,12 @@ sub print_feed_meta {
        } else {
                printf('<link rel="alternate" title="%s projects list" '.
                       'href="%s" type="text/plain; charset=utf-8" />'."\n",
-                      esc_attr($site_name), href(project=>undef, action=>"project_index"));
+                      esc_attr($site_name),
+                      esc_attr(href(project=>undef, action=>"project_index")));
                printf('<link rel="alternate" title="%s projects feeds" '.
                       'href="%s" type="text/x-opml" />'."\n",
-                      esc_attr($site_name), href(project=>undef, action=>"opml"));
+                      esc_attr($site_name),
+                      esc_attr(href(project=>undef, action=>"opml")));
        }
 }
 
@@ -4180,19 +4197,20 @@ sub git_header_html {
        my %opts = @_;
 
        my $title = get_page_title();
-       my $content_type = get_content_type_html();
-       print $cgi->header(-type=>$content_type, -charset => 'utf-8',
+       print $cgi->header(-type=>get_content_type_html(), -charset => 'utf-8',
                           -status=> $status, -expires => $expires)
                unless ($opts{'-no_http_header'});
        my $mod_perl_version = $ENV{'MOD_PERL'} ? " $ENV{'MOD_PERL'}" : '';
        print <<EOF;
 <?xml version="1.0" encoding="utf-8"?>
-<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<!DOCTYPE html [
+       <!ENTITY nbsp "&#xA0;">
+       <!ENTITY sdot "&#x22C5;">
+]>
 <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en-US" lang="en-US">
 <!-- git web interface version $version, (C) 2005-2006, Kay Sievers <kay.sievers\@vrfy.org>, Christian Gierke -->
 <!-- git core binaries version $git_version -->
 <head>
-<meta http-equiv="content-type" content="$content_type; charset=utf-8"/>
 <meta name="generator" content="gitweb/$version git/$git_version$mod_perl_version"/>
 <meta name="robots" content="index, nofollow"/>
 <title>$title</title>
@@ -4288,8 +4306,8 @@ sub git_footer_html {
        if (defined $action &&
            $action eq 'blame_incremental') {
                print qq!<script type="text/javascript">\n!.
-                     qq!startBlame("!. href(action=>"blame_data", -replay=>1) .qq!",\n!.
-                     qq!           "!. href() .qq!");\n!.
+                     qq!startBlame("!. esc_attr(href(action=>"blame_data", -replay=>1)) .qq!",\n!.
+                     qq!           "!. esc_attr(href()) .qq!");\n!.
                      qq!</script>\n!;
        } else {
                my ($jstimezone, $tz_cookie, $datetime_class) =
@@ -4626,7 +4644,7 @@ sub git_print_log {
        # print log
        my $skip_blank_line = 0;
        foreach my $line (@$log) {
-               if ($line =~ m/^\s*([A-Z][-A-Za-z]*-[Bb]y|C[Cc]): /) {
+               if ($line =~ m/^\s*([A-Z][-A-Za-z]*-([Bb]y|[Tt]o)|C[Cc]|(Clos|Fix)es): /) {
                        if (! $opts{'-remove_signoff'}) {
                                print "<span class=\"signoff\">" . esc_html($line) . "</span><br/>\n";
                                $skip_blank_line = 1;
@@ -5284,7 +5302,7 @@ sub format_ctx_rem_add_lines {
                #    + c
                #   +  d
                #
-               # Otherwise the highlightling would be confusing.
+               # Otherwise the highlighting would be confusing.
                if ($is_combined) {
                        for (my $i = 0; $i < @$add; $i++) {
                                my $prefix_rem = substr($rem->[$i], 0, $num_parents);
@@ -7078,6 +7096,7 @@ sub git_blob_plain {
                        ($sandbox ? 'attachment' : 'inline')
                        . '; filename="' . $save_as . '"');
        local $/ = undef;
+       local *FCGI::Stream::PRINT = $FCGI_Stream_PRINT_raw;
        binmode STDOUT, ':raw';
        print <$fd>;
        binmode STDOUT, ':utf8'; # as set at the beginning of gitweb.cgi
@@ -7156,8 +7175,8 @@ sub git_blob {
                        print qq! alt="!.esc_attr($file_name).qq!" title="!.esc_attr($file_name).qq!"!;
                }
                print qq! src="! .
-                     href(action=>"blob_plain", hash=>$hash,
-                          hash_base=>$hash_base, file_name=>$file_name) .
+                     esc_attr(href(action=>"blob_plain", hash=>$hash,
+                          hash_base=>$hash_base, file_name=>$file_name)) .
                      qq!" />\n!;
        } else {
                my $nr;
@@ -7416,6 +7435,7 @@ sub git_snapshot {
 
        open my $fd, "-|", $cmd
                or die_error(500, "Execute git-archive failed");
+       local *FCGI::Stream::PRINT = $FCGI_Stream_PRINT_raw;
        binmode STDOUT, ':raw';
        print <$fd>;
        binmode STDOUT, ':utf8'; # as set at the beginning of gitweb.cgi
@@ -7472,7 +7492,8 @@ sub git_log_generic {
                                 -accesskey => "n", -title => "Alt-n"}, "next");
        }
        my $patch_max = gitweb_get_feature('patches');
-       if ($patch_max && !defined $file_name) {
+       if ($patch_max && !defined $file_name &&
+               !gitweb_check_feature('email-privacy')) {
                if ($patch_max < 0 || @commitlist <= $patch_max) {
                        $paging_nav .= " &sdot; " .
                                $cgi->a({-href => href(action=>"patches", -replay=>1)},
@@ -7533,7 +7554,8 @@ sub git_commit {
                        } @$parents ) .
                        ')';
        }
-       if (gitweb_check_feature('patches') && @$parents <= 1) {
+       if (gitweb_check_feature('patches') && @$parents <= 1 &&
+               !gitweb_check_feature('email-privacy')) {
                $formats_nav .= " | " .
                        $cgi->a({-href => href(action=>"patch", -replay=>1)},
                                "patch");
@@ -7846,7 +7868,8 @@ sub git_commitdiff {
                $formats_nav =
                        $cgi->a({-href => href(action=>"commitdiff_plain", -replay=>1)},
                                "raw");
-               if ($patch_max && @{$co{'parents'}} <= 1) {
+               if ($patch_max && @{$co{'parents'}} <= 1 &&
+                       !gitweb_check_feature('email-privacy')) {
                        $formats_nav .= " | " .
                                $cgi->a({-href => href(action=>"patch", -replay=>1)},
                                        "patch");
@@ -8240,6 +8263,7 @@ sub git_feed {
        } else {
                $alt_url = href(-full=>1, action=>"summary");
        }
+       $alt_url = esc_attr($alt_url);
        print qq!<?xml version="1.0" encoding="utf-8"?>\n!;
        if ($format eq 'rss') {
                print <<XML;
@@ -8277,7 +8301,7 @@ XML
                      $alt_url . '" />' . "\n" .
                      '<link rel="self" type="' . $content_type . '" href="' .
                      $cgi->self_url() . '" />' . "\n" .
-                     "<id>" . href(-full=>1) . "</id>\n" .
+                     "<id>" . esc_url(href(-full=>1)) . "</id>\n" .
                      # use project owner for feed author
                      "<author><name>$owner</name></author>\n";
                if (defined $favicon) {
@@ -8323,7 +8347,7 @@ XML
                              "<author>" . esc_html($co{'author'}) . "</author>\n" .
                              "<pubDate>$cd{'rfc2822'}</pubDate>\n" .
                              "<guid isPermaLink=\"true\">$co_url</guid>\n" .
-                             "<link>$co_url</link>\n" .
+                             "<link>" . esc_html($co_url) . "</link>\n" .
                              "<description>" . esc_html($co{'title'}) . "</description>\n" .
                              "<content:encoded>" .
                              "<![CDATA[\n";
@@ -8345,8 +8369,8 @@ XML
                        }
                        print "</contributor>\n" .
                              "<published>$cd{'iso-8601'}</published>\n" .
-                             "<link rel=\"alternate\" type=\"text/html\" href=\"$co_url\" />\n" .
-                             "<id>$co_url</id>\n" .
+                             "<link rel=\"alternate\" type=\"text/html\" href=\"" . esc_attr($co_url) . "\" />\n" .
+                             "<id>" . esc_html($co_url) . "</id>\n" .
                              "<content type=\"xhtml\" xml:base=\"" . esc_url($my_url) . "\">\n" .
                              "<div xmlns=\"http://www.w3.org/1999/xhtml\">\n";
                }
@@ -8453,8 +8477,8 @@ XML
                }
 
                my $path = esc_html(chop_str($proj{'path'}, 25, 5));
-               my $rss  = href('project' => $proj{'path'}, 'action' => 'rss', -full => 1);
-               my $html = href('project' => $proj{'path'}, 'action' => 'summary', -full => 1);
+               my $rss  = esc_attr(href('project' => $proj{'path'}, 'action' => 'rss', -full => 1));
+               my $html = esc_attr(href('project' => $proj{'path'}, 'action' => 'summary', -full => 1));
                print "<outline type=\"rss\" text=\"$path\" title=\"$path\" xmlUrl=\"$rss\" htmlUrl=\"$html\"/>\n";
        }
        print <<XML;
This page took 0.201241 seconds and 4 git commands to generate.