Merge branch 'jn/maint-gitweb-pathinfo-fix' into maint
authorJunio C Hamano <gitster@pobox.com>
Wed, 19 Jan 2011 16:26:04 +0000 (08:26 -0800)
committerJunio C Hamano <gitster@pobox.com>
Wed, 19 Jan 2011 16:26:04 +0000 (08:26 -0800)
* jn/maint-gitweb-pathinfo-fix:
gitweb: Fix handling of whitespace in generated links

1  2 
gitweb/gitweb.perl
diff --combined gitweb/gitweb.perl
index 59b2e08b980b968e3ba5e282fcb237b1af5bb579,75cef245a26f24d4aefd19a7b9b6edea9676b2ba..fda9d30f37ce40b6da55b9558eee8848b20ebef9
@@@ -1186,7 -1186,7 +1186,7 @@@ sub href 
                $href =~ s,/$,,;
  
                # Then add the project name, if present
-               $href .= "/".esc_url($params{'project'});
+               $href .= "/".esc_path_info($params{'project'});
                delete $params{'project'};
  
                # since we destructively absorb parameters, we keep this
                # Summary just uses the project path URL, any other action is
                # added to the URL
                if (defined $params{'action'}) {
-                       $href .= "/".esc_url($params{'action'}) unless $params{'action'} eq 'summary';
+                       $href .= "/".esc_path_info($params{'action'})
+                               unless $params{'action'} eq 'summary';
                        delete $params{'action'};
                }
  
                        || $params{'hash_parent'} || $params{'hash'});
                if (defined $params{'hash_base'}) {
                        if (defined $params{'hash_parent_base'}) {
-                               $href .= esc_url($params{'hash_parent_base'});
+                               $href .= esc_path_info($params{'hash_parent_base'});
                                # skip the file_parent if it's the same as the file_name
                                if (defined $params{'file_parent'}) {
                                        if (defined $params{'file_name'} && $params{'file_parent'} eq $params{'file_name'}) {
                                                delete $params{'file_parent'};
                                        } elsif ($params{'file_parent'} !~ /\.\./) {
-                                               $href .= ":/".esc_url($params{'file_parent'});
+                                               $href .= ":/".esc_path_info($params{'file_parent'});
                                                delete $params{'file_parent'};
                                        }
                                }
                                delete $params{'hash_parent'};
                                delete $params{'hash_parent_base'};
                        } elsif (defined $params{'hash_parent'}) {
-                               $href .= esc_url($params{'hash_parent'}). "..";
+                               $href .= esc_path_info($params{'hash_parent'}). "..";
                                delete $params{'hash_parent'};
                        }
  
-                       $href .= esc_url($params{'hash_base'});
+                       $href .= esc_path_info($params{'hash_base'});
                        if (defined $params{'file_name'} && $params{'file_name'} !~ /\.\./) {
-                               $href .= ":/".esc_url($params{'file_name'});
+                               $href .= ":/".esc_path_info($params{'file_name'});
                                delete $params{'file_name'};
                        }
                        delete $params{'hash'};
                        delete $params{'hash_base'};
                } elsif (defined $params{'hash'}) {
-                       $href .= esc_url($params{'hash'});
+                       $href .= esc_path_info($params{'hash'});
                        delete $params{'hash'};
                }
  
        }
        $href .= "?" . join(';', @result) if scalar @result;
  
+       # final transformation: trailing spaces must be escaped (URI-encoded)
+       $href =~ s/(\s+)$/CGI::escape($1)/e;
        return $href;
  }
  
@@@ -1347,6 -1351,17 +1351,17 @@@ sub esc_param 
        return $str;
  }
  
+ # the quoting rules for path_info fragment are slightly different
+ sub esc_path_info {
+       my $str = shift;
+       return undef unless defined $str;
+       # path_info doesn't treat '+' as space (specially), but '?' must be escaped
+       $str =~ s/([^A-Za-z0-9\-_.~();\/;:@&= +]+)/CGI::escape($1)/eg;
+       return $str;
+ }
  # quote unsafe chars in whole URL, so some characters cannot be quoted
  sub esc_url {
        my $str = shift;
        return $str;
  }
  
 +# quote unsafe characters in HTML attributes
 +sub esc_attr {
 +
 +      # for XHTML conformance escaping '"' to '&quot;' is not enough
 +      return esc_html(@_);
 +}
 +
  # replace invalid utf8 character with SUBSTITUTION sequence
  sub esc_html {
        my $str = shift;
@@@ -1768,7 -1776,7 +1783,7 @@@ sub format_ref_marker 
                                        hash=>$dest
                                )}, $name);
  
 -                      $markers .= " <span class=\"$class\" title=\"$ref\">" .
 +                      $markers .= " <span class=\"".esc_attr($class)."\" title=\"".esc_attr($ref)."\">" .
                                $link . "</span>";
                }
        }
@@@ -1852,7 -1860,7 +1867,7 @@@ sub git_get_avatar 
                return $pre_white .
                       "<img width=\"$size\" " .
                            "class=\"avatar\" " .
 -                          "src=\"$url\" " .
 +                          "src=\"".esc_url($url)."\" " .
                            "alt=\"\" " .
                       "/>" . $post_white;
        } else {
@@@ -2563,7 -2571,7 +2578,7 @@@ sub git_show_project_tagcloud 
        } else {
                my @tags = sort { $cloud->{$a}->{count} <=> $cloud->{$b}->{count} } keys %$cloud;
                return '<p align="center">' . join (', ', map {
 -                      "<a href=\"$home_link?by_tag=$_\">$cloud->{$_}->{topname}</a>"
 +                      $cgi->a({-href=>"$home_link?by_tag=$_"}, $cloud->{$_}->{topname})
                } splice(@tags, 0, $count)) . '</p>';
        }
  }
@@@ -3394,51 -3402,6 +3409,51 @@@ sub get_page_title 
        return $title;
  }
  
 +sub print_feed_meta {
 +      if (defined $project) {
 +              my %href_params = get_feed_info();
 +              if (!exists $href_params{'-title'}) {
 +                      $href_params{'-title'} = 'log';
 +              }
 +
 +              foreach my $format qw(RSS Atom) {
 +                      my $type = lc($format);
 +                      my %link_attr = (
 +                              '-rel' => 'alternate',
 +                              '-title' => esc_attr("$project - $href_params{'-title'} - $format feed"),
 +                              '-type' => "application/$type+xml"
 +                      );
 +
 +                      $href_params{'action'} = $type;
 +                      $link_attr{'-href'} = href(%href_params);
 +                      print "<link ".
 +                            "rel=\"$link_attr{'-rel'}\" ".
 +                            "title=\"$link_attr{'-title'}\" ".
 +                            "href=\"$link_attr{'-href'}\" ".
 +                            "type=\"$link_attr{'-type'}\" ".
 +                            "/>\n";
 +
 +                      $href_params{'extra_options'} = '--no-merges';
 +                      $link_attr{'-href'} = href(%href_params);
 +                      $link_attr{'-title'} .= ' (no merges)';
 +                      print "<link ".
 +                            "rel=\"$link_attr{'-rel'}\" ".
 +                            "title=\"$link_attr{'-title'}\" ".
 +                            "href=\"$link_attr{'-href'}\" ".
 +                            "type=\"$link_attr{'-type'}\" ".
 +                            "/>\n";
 +              }
 +
 +      } 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"));
 +              printf('<link rel="alternate" title="%s projects feeds" '.
 +                     'href="%s" type="text/x-opml" />'."\n",
 +                     esc_attr($site_name), href(project=>undef, action=>"opml"));
 +      }
 +}
 +
  sub git_header_html {
        my $status = shift || "200 OK";
        my $expires = shift;
        # print out each stylesheet that exist, providing backwards capability
        # for those people who defined $stylesheet in a config file
        if (defined $stylesheet) {
 -              print '<link rel="stylesheet" type="text/css" href="'.$stylesheet.'"/>'."\n";
 +              print '<link rel="stylesheet" type="text/css" href="'.esc_url($stylesheet).'"/>'."\n";
        } else {
                foreach my $stylesheet (@stylesheets) {
                        next unless $stylesheet;
 -                      print '<link rel="stylesheet" type="text/css" href="'.$stylesheet.'"/>'."\n";
 +                      print '<link rel="stylesheet" type="text/css" href="'.esc_url($stylesheet).'"/>'."\n";
                }
        }
 -      if (defined $project) {
 -              my %href_params = get_feed_info();
 -              if (!exists $href_params{'-title'}) {
 -                      $href_params{'-title'} = 'log';
 -              }
 -
 -              foreach my $format qw(RSS Atom) {
 -                      my $type = lc($format);
 -                      my %link_attr = (
 -                              '-rel' => 'alternate',
 -                              '-title' => "$project - $href_params{'-title'} - $format feed",
 -                              '-type' => "application/$type+xml"
 -                      );
 -
 -                      $href_params{'action'} = $type;
 -                      $link_attr{'-href'} = href(%href_params);
 -                      print "<link ".
 -                            "rel=\"$link_attr{'-rel'}\" ".
 -                            "title=\"$link_attr{'-title'}\" ".
 -                            "href=\"$link_attr{'-href'}\" ".
 -                            "type=\"$link_attr{'-type'}\" ".
 -                            "/>\n";
 -
 -                      $href_params{'extra_options'} = '--no-merges';
 -                      $link_attr{'-href'} = href(%href_params);
 -                      $link_attr{'-title'} .= ' (no merges)';
 -                      print "<link ".
 -                            "rel=\"$link_attr{'-rel'}\" ".
 -                            "title=\"$link_attr{'-title'}\" ".
 -                            "href=\"$link_attr{'-href'}\" ".
 -                            "type=\"$link_attr{'-type'}\" ".
 -                            "/>\n";
 -              }
 -
 -      } 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 feeds" '.
 -                     'href="%s" type="text/x-opml" />'."\n",
 -                     $site_name, href(project=>undef, action=>"opml"));
 -      }
 +      print_feed_meta()
 +              if ($status eq '200 OK');
        if (defined $favicon) {
 -              print qq(<link rel="shortcut icon" href="$favicon" type="image/png" />\n);
 +              print qq(<link rel="shortcut icon" href=").esc_url($favicon).qq(" type="image/png" />\n);
        }
  
        print "</head>\n" .
        print "<div class=\"page_header\">\n" .
              $cgi->a({-href => esc_url($logo_url),
                       -title => $logo_label},
 -                    qq(<img src="$logo" width="72" height="27" alt="git" class="logo"/>));
 +                    qq(<img src=").esc_url($logo).qq(" width="72" height="27" alt="git" class="logo"/>));
        print $cgi->a({-href => esc_url($home_link)}, $home_link_str) . " / ";
        if (defined $project) {
                print $cgi->a({-href => href(action=>"summary")}, esc_html($project));
@@@ -3602,7 -3605,7 +3617,7 @@@ sub git_footer_html 
                insert_file($site_footer);
        }
  
 -      print qq!<script type="text/javascript" src="$javascript"></script>\n!;
 +      print qq!<script type="text/javascript" src="!.esc_url($javascript).qq!"></script>\n!;
        if (defined $action &&
            $action eq 'blame_incremental') {
                print qq!<script type="text/javascript">\n!.
@@@ -5622,14 -5625,14 +5637,14 @@@ sub git_blob 
        } else {
                print "<div class=\"page_nav\">\n" .
                      "<br/><br/></div>\n" .
 -                    "<div class=\"title\">$hash</div>\n";
 +                    "<div class=\"title\">".esc_html($hash)."</div>\n";
        }
        git_print_page_path($file_name, "blob", $hash_base);
        print "<div class=\"page_body\">\n";
        if ($mimetype =~ m!^image/!) {
 -              print qq!<img type="$mimetype"!;
 +              print qq!<img type="!.esc_attr($mimetype).qq!"!;
                if ($file_name) {
 -                      print qq! alt="$file_name" title="$file_name"!;
 +                      print qq! alt="!.esc_attr($file_name).qq!" title="!.esc_attr($file_name).qq!"!;
                }
                print qq! src="! .
                      href(action=>"blob_plain", hash=>$hash,
                        $nr++;
                        $line = untabify($line);
                        printf qq!<div class="pre"><a id="l%i" href="%s#l%i" class="linenr">%4i</a> %s</div>\n!,
 -                             $nr, href(-replay => 1), $nr, $nr, $syntax ? $line : esc_html($line, -nbsp=>1);
 +                             $nr, esc_attr(href(-replay => 1)), $nr, $nr, $syntax ? $line : esc_html($line, -nbsp=>1);
                }
        }
        close $fd
@@@ -5704,7 -5707,7 +5719,7 @@@ sub git_tree 
                undef $hash_base;
                print "<div class=\"page_nav\">\n";
                print "<br/><br/></div>\n";
 -              print "<div class=\"title\">$hash</div>\n";
 +              print "<div class=\"title\">".esc_html($hash)."</div>\n";
        }
        if (defined $file_name) {
                $basedir = $file_name;
@@@ -6172,7 -6175,7 +6187,7 @@@ sub git_blobdiff 
                        git_print_header_div('commit', esc_html($co{'title'}), $hash_base);
                } else {
                        print "<div class=\"page_nav\"><br/>$formats_nav<br/></div>\n";
 -                      print "<div class=\"title\">$hash vs $hash_parent</div>\n";
 +                      print "<div class=\"title\">".esc_html("$hash vs $hash_parent")."</div>\n";
                }
                if (defined $file_name) {
                        git_print_page_path($file_name, "blob", $hash_base);
@@@ -6870,7 -6873,7 +6885,7 @@@ XM
                if (defined $favicon) {
                        print "<icon>" . esc_url($favicon) . "</icon>\n";
                }
 -              if (defined $logo_url) {
 +              if (defined $logo) {
                        # not twice as wide as tall: 72 x 27 pixels
                        print "<logo>" . esc_url($logo) . "</logo>\n";
                }