X-Git-Url: https://git.ladys.computer/Gitweb/blobdiff_plain/069b78426769040f09cf16fb7188a8e02b0fc415643a5b60a5d092703ee13132..a341a1048ae5adee5899af18efc76cb217f080043b5b3b221100063da3b4eec7:/gitweb.perl diff --git a/gitweb.perl b/gitweb.perl index 986c9ed..87c50e6 100755 --- a/gitweb.perl +++ b/gitweb.perl @@ -27,6 +27,13 @@ our $version = "++GIT_VERSION++"; our $my_url = $cgi->url(); 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 +if (my $path_info = $ENV{"PATH_INFO"}) { + $my_url =~ s,\Q$path_info\E$,,; + $my_uri =~ s,\Q$path_info\E$,,; +} + # core git executable to use # this can just be "git" if your webserver has a sensible PATH our $GIT = "++GIT_BINDIR++/git"; @@ -378,7 +385,7 @@ if (-e $GITWEB_CONFIG) { } # version of the core git binary -our $git_version = qx($GIT --version) =~ m/git version (.*)$/ ? $1 : "unknown"; +our $git_version = qx("$GIT" --version) =~ m/git version (.*)$/ ? $1 : "unknown"; $projects_list ||= $projectroot; @@ -387,7 +394,7 @@ $projects_list ||= $projectroot; our $action = $cgi->param('a'); if (defined $action) { if ($action =~ m/[^0-9a-zA-Z\.\-_]/) { - die_error(undef, "Invalid action parameter"); + die_error(400, "Invalid action parameter"); } } @@ -400,21 +407,21 @@ if (defined $project) { ($export_ok && !(-e "$projectroot/$project/$export_ok")) || ($strict_export && !project_in_list($project))) { undef $project; - die_error(undef, "No such project"); + die_error(404, "No such project"); } } our $file_name = $cgi->param('f'); if (defined $file_name) { if (!validate_pathname($file_name)) { - die_error(undef, "Invalid file parameter"); + die_error(400, "Invalid file parameter"); } } our $file_parent = $cgi->param('fp'); if (defined $file_parent) { if (!validate_pathname($file_parent)) { - die_error(undef, "Invalid file parent parameter"); + die_error(400, "Invalid file parent parameter"); } } @@ -422,21 +429,21 @@ if (defined $file_parent) { our $hash = $cgi->param('h'); if (defined $hash) { if (!validate_refname($hash)) { - die_error(undef, "Invalid hash parameter"); + die_error(400, "Invalid hash parameter"); } } our $hash_parent = $cgi->param('hp'); if (defined $hash_parent) { if (!validate_refname($hash_parent)) { - die_error(undef, "Invalid hash parent parameter"); + die_error(400, "Invalid hash parent parameter"); } } our $hash_base = $cgi->param('hb'); if (defined $hash_base) { if (!validate_refname($hash_base)) { - die_error(undef, "Invalid hash base parameter"); + die_error(400, "Invalid hash base parameter"); } } @@ -448,10 +455,10 @@ our @extra_options = $cgi->param('opt'); if (defined @extra_options) { foreach my $opt (@extra_options) { if (not exists $allowed_options{$opt}) { - die_error(undef, "Invalid option parameter"); + die_error(400, "Invalid option parameter"); } if (not grep(/^$action$/, @{$allowed_options{$opt}})) { - die_error(undef, "Invalid option parameter for this action"); + die_error(400, "Invalid option parameter for this action"); } } } @@ -459,7 +466,7 @@ if (defined @extra_options) { our $hash_parent_base = $cgi->param('hpb'); if (defined $hash_parent_base) { if (!validate_refname($hash_parent_base)) { - die_error(undef, "Invalid hash parent base parameter"); + die_error(400, "Invalid hash parent base parameter"); } } @@ -467,14 +474,14 @@ if (defined $hash_parent_base) { our $page = $cgi->param('pg'); if (defined $page) { if ($page =~ m/[^0-9]/) { - die_error(undef, "Invalid page parameter"); + die_error(400, "Invalid page parameter"); } } our $searchtype = $cgi->param('st'); if (defined $searchtype) { if ($searchtype =~ m/[^a-z]/) { - die_error(undef, "Invalid searchtype parameter"); + die_error(400, "Invalid searchtype parameter"); } } @@ -484,7 +491,7 @@ our $searchtext = $cgi->param('s'); our $search_regexp; if (defined $searchtext) { if (length($searchtext) < 2) { - die_error(undef, "At least two characters are required for search parameter"); + die_error(403, "At least two characters are required for search parameter"); } $search_regexp = $search_use_regexp ? $searchtext : quotemeta $searchtext; } @@ -540,7 +547,7 @@ $git_dir = "$projectroot/$project" if $project; # dispatch my %actions = ( - "blame" => \&git_blame2, + "blame" => \&git_blame, "blobdiff" => \&git_blobdiff, "blobdiff_plain" => \&git_blobdiff_plain, "blob" => \&git_blob, @@ -581,11 +588,11 @@ if (!defined $action) { } } if (!defined($actions{$action})) { - die_error(undef, "Unknown action"); + die_error(400, "Unknown action"); } if ($action !~ m/^(opml|project_list|project_index)$/ && !$project) { - die_error(undef, "Project needed"); + die_error(400, "Project needed"); } $actions{$action}->(); exit; @@ -776,7 +783,7 @@ sub quot_cec { ); my $chr = ( (exists $es{$cntrl}) ? $es{$cntrl} - : sprintf('\%03o', ord($cntrl)) ); + : sprintf('\%2x', ord($cntrl)) ); if ($opts{-nohtml}) { return $chr; } else { @@ -867,6 +874,10 @@ sub chop_str { my $add_len = shift || 10; my $where = shift || 'right'; # 'left' | 'center' | 'right' + # Make sure perl knows it is utf8 encoded so we don't + # cut in the middle of a utf8 multibyte char. + $str = to_utf8($str); + # 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 # remove chopped character entities entirely @@ -1087,13 +1098,23 @@ sub format_log_line_html { } # format marker of refs pointing to given object + +# the destination action is chosen based on object type and current context: +# - for annotated tags, we choose the tag view unless it's the current view +# already, in which case we go to shortlog view +# - for other refs, we keep the current view if we're in history, shortlog or +# log view, and select shortlog otherwise sub format_ref_marker { my ($refs, $id) = @_; my $markers = ''; if (defined $refs->{$id}) { foreach my $ref (@{$refs->{$id}}) { + # this code exploits the fact that non-lightweight tags are the + # only indirect objects, and that they are the only objects for which + # we want to use tag instead of shortlog as action my ($type, $name) = qw(); + my $indirect = ($ref =~ s/\^\{\}$//); # e.g. tags/v2.6.11 or heads/next if ($ref =~ m!^(.*?)s?/(.*)$!) { $type = $1; @@ -1103,8 +1124,29 @@ sub format_ref_marker { $name = $ref; } - $markers .= " " . - esc_html($name) . ""; + my $class = $type; + $class .= " indirect" if $indirect; + + my $dest_action = "shortlog"; + + if ($indirect) { + $dest_action = "tag" unless $action eq "tag"; + } elsif ($action =~ /^(history|(short)?log)$/) { + $dest_action = $action; + } + + my $dest = ""; + $dest .= "refs/" unless $ref =~ m!^refs/!; + $dest .= $ref; + + my $link = $cgi->a({ + -href => href( + action=>$dest_action, + hash=>$dest + )}, $name); + + $markers .= " " . + $link . ""; } } @@ -1497,9 +1539,13 @@ sub git_cmd { return $GIT, '--git-dir='.$git_dir; } -# returns path to the core git executable and the --git-dir parameter as string -sub git_cmd_str { - return join(' ', git_cmd()); +# quote the given arguments for passing them to the shell +# quote_command("command", "arg 1", "arg with ' and ! characters") +# => "'command' 'arg 1' 'arg with '\'' and '\!' characters'" +# Try to avoid using this function wherever possible. +sub quote_command { + return join(' ', + map( { my $a = $_; $a =~ s/(['!])/'\\$1'/g; "'$a'" } @_ )); } # get HEAD ref of given project as hash @@ -1658,7 +1704,7 @@ sub git_get_hash_by_path { $path =~ s,/+$,,; open my $fd, "-|", git_cmd(), "ls-tree", $base, "--", $path - or die_error(undef, "Open git-ls-tree failed"); + or die_error(500, "Open git-ls-tree failed"); my $line = <$fd>; close $fd or return undef; @@ -1911,7 +1957,7 @@ sub git_get_references { while (my $line = <$fd>) { chomp $line; - if ($line =~ m!^([0-9a-fA-F]{40})\srefs/($type/?[^^]+)!) { + if ($line =~ m!^([0-9a-fA-F]{40})\srefs/($type.*)$!) { if (defined $refs{$1}) { push @{$refs{$1}}, $2; } else { @@ -2085,7 +2131,7 @@ sub parse_commit_text { last; } } - if ($co{'title'} eq "") { + if (! defined $co{'title'} || $co{'title'} eq "") { $co{'title'} = $co{'title_short'} = '(no commit message)'; } # remove added spaces @@ -2120,7 +2166,7 @@ sub parse_commit { "--max-count=1", $commit_id, "--", - or die_error(undef, "Open git-rev-list failed"); + or die_error(500, "Open git-rev-list failed"); %co = parse_commit_text(<$fd>, 1); close $fd; @@ -2145,7 +2191,7 @@ sub parse_commits { $commit_id, "--", ($filename ? ($filename) : ()) - or die_error(undef, "Open git-rev-list failed"); + or die_error(500, "Open git-rev-list failed"); while (my $line = <$fd>) { my %co = parse_commit_text($line); push @cos, \%co; @@ -2155,49 +2201,6 @@ sub parse_commits { return wantarray ? @cos : \@cos; } -# parse ref from ref_file, given by ref_id, with given type -sub parse_ref { - my $ref_file = shift; - my $ref_id = shift; - my $type = shift || git_get_type($ref_id); - my %ref_item; - - $ref_item{'type'} = $type; - $ref_item{'id'} = $ref_id; - $ref_item{'epoch'} = 0; - $ref_item{'age'} = "unknown"; - if ($type eq "tag") { - my %tag = parse_tag($ref_id); - $ref_item{'comment'} = $tag{'comment'}; - if ($tag{'type'} eq "commit") { - my %co = parse_commit($tag{'object'}); - $ref_item{'epoch'} = $co{'committer_epoch'}; - $ref_item{'age'} = $co{'age_string'}; - } elsif (defined($tag{'epoch'})) { - my $age = time - $tag{'epoch'}; - $ref_item{'epoch'} = $tag{'epoch'}; - $ref_item{'age'} = age_string($age); - } - $ref_item{'reftype'} = $tag{'type'}; - $ref_item{'name'} = $tag{'name'}; - $ref_item{'refid'} = $tag{'object'}; - } elsif ($type eq "commit"){ - my %co = parse_commit($ref_id); - $ref_item{'reftype'} = "commit"; - $ref_item{'name'} = $ref_file; - $ref_item{'title'} = $co{'title'}; - $ref_item{'refid'} = $ref_id; - $ref_item{'epoch'} = $co{'committer_epoch'}; - $ref_item{'age'} = $co{'age_string'}; - } else { - $ref_item{'reftype'} = $type; - $ref_item{'name'} = $ref_file; - $ref_item{'refid'} = $ref_id; - } - - return %ref_item; -} - # parse line of git-diff-tree "raw" output sub parse_difftree_raw_line { my $line = shift; @@ -2478,8 +2481,7 @@ sub blob_mimetype { return $default_blob_plain_mimetype unless $fd; if (-T $fd) { - return 'text/plain' . - ($default_text_plain_charset ? '; charset='.$default_text_plain_charset : ''); + return 'text/plain'; } elsif (! $filename) { return 'application/octet-stream'; } elsif ($filename =~ m/\.png$/i) { @@ -2493,6 +2495,17 @@ sub blob_mimetype { } } +sub blob_contenttype { + my ($fd, $file_name, $type) = @_; + + $type ||= blob_mimetype($fd, $file_name); + if ($type eq 'text/plain' && defined $default_text_plain_charset) { + $type .= "; charset=$default_text_plain_charset"; + } + + return $type; +} + ## ====================================================================== ## functions printing HTML: header, footer, error page @@ -2620,7 +2633,7 @@ EOF print "\n"; my ($have_search) = gitweb_check_feature('search'); - if ((defined $project) && ($have_search)) { + if (defined $project && $have_search) { if (!defined $searchtext) { $searchtext = ""; } @@ -2636,16 +2649,13 @@ EOF my ($use_pathinfo) = gitweb_check_feature('pathinfo'); if ($use_pathinfo) { $action .= "/".esc_url($project); - } else { - $cgi->param("p", $project); } - $cgi->param("a", "search"); - $cgi->param("h", $search_hash); print $cgi->startform(-method => "get", -action => $action) . "