]> Lady’s Gitweb - Gitweb/blobdiff - gitweb.perl
gitweb: Return 1 on validation success instead of passed input
[Gitweb] / gitweb.perl
index bfa2d79cb1a2a1dd7fdbeb6d4d25e873c9ea9440fe2cf40abcedd123b4b07276..4d5b9957bcbc451512bceef1e5aadccb7378d920123530fadd9692b0ada6d3d2 100755 (executable)
@@ -54,6 +54,11 @@ sub evaluate_uri {
        # to build the base URL ourselves:
        our $path_info = decode_utf8($ENV{"PATH_INFO"});
        if ($path_info) {
+               # $path_info has already been URL-decoded by the web server, but
+               # $my_url and $my_uri have not. URL-decode them so we can properly
+               # strip $path_info.
+               $my_url = unescape($my_url);
+               $my_uri = unescape($my_uri);
                if ($my_url =~ s,\Q$path_info\E$,, &&
                    $my_uri =~ s,\Q$path_info\E$,, &&
                    defined $ENV{'SCRIPT_NAME'}) {
@@ -80,6 +85,9 @@ our $project_maxdepth = "++GITWEB_PROJECT_MAXDEPTH++";
 # string of the home link on top of all pages
 our $home_link_str = "++GITWEB_HOME_LINK_STR++";
 
+# extra breadcrumbs preceding the home link
+our @extra_breadcrumbs = ();
+
 # name of your site or organization to appear in page titles
 # replace this with something more descriptive for clearer bookmarks
 our $site_name = "++GITWEB_SITENAME++"
@@ -266,16 +274,15 @@ our %highlight_basename = (
 our %highlight_ext = (
        # main extensions, defining name of syntax;
        # see files in /usr/share/highlight/langDefs/ directory
-       map { $_ => $_ }
-               qw(py c cpp rb java css php sh pl js tex bib xml awk bat ini spec tcl sql make),
+       (map { $_ => $_ } qw(py rb java css js tex bib xml awk bat ini spec tcl sql)),
        # alternate extensions, see /etc/highlight/filetypes.conf
-       'h' => 'c',
-       map { $_ => 'sh'  } qw(bash zsh ksh),
-       map { $_ => 'cpp' } qw(cxx c++ cc),
-       map { $_ => 'php' } qw(php3 php4 php5 phps),
-       map { $_ => 'pl'  } qw(perl pm), # perhaps also 'cgi'
-       map { $_ => 'make'} qw(mak mk),
-       map { $_ => 'xml' } qw(xhtml html htm),
+       (map { $_ => 'c'   } qw(c h)),
+       (map { $_ => 'sh'  } qw(sh bash zsh ksh)),
+       (map { $_ => 'cpp' } qw(cpp cxx c++ cc)),
+       (map { $_ => 'php' } qw(php php3 php4 php5 phps)),
+       (map { $_ => 'pl'  } qw(pl perl pm)), # perhaps also 'cgi'
+       (map { $_ => 'make'} qw(make mak mk)),
+       (map { $_ => 'xml' } qw(xml xhtml html htm)),
 );
 
 # You define site-wide feature defaults here; override them with
@@ -537,7 +544,7 @@ our %feature = (
        # $feature{'remote_heads'}{'default'} = [1];
        # To have project specific config enable override in $GITWEB_CONFIG
        # $feature{'remote_heads'}{'override'} = 1;
-       # and in project config gitweb.remote_heads = 0|1;
+       # and in project config gitweb.remoteheads = 0|1;
        'remote_heads' => {
                'sub' => sub { feature_bool('remote_heads', @_) },
                'override' => 0,
@@ -680,7 +687,7 @@ sub evaluate_gitweb_config {
        our $GITWEB_CONFIG_SYSTEM = $ENV{'GITWEB_CONFIG_SYSTEM'} || "++GITWEB_CONFIG_SYSTEM++";
        our $GITWEB_CONFIG_COMMON = $ENV{'GITWEB_CONFIG_COMMON'} || "++GITWEB_CONFIG_COMMON++";
 
-       # Protect agains duplications of file names, to not read config twice.
+       # Protect against duplications of file names, to not read config twice.
        # Only one of $GITWEB_CONFIG and $GITWEB_CONFIG_SYSTEM is used, so
        # there possibility of duplication of filename there doesn't matter.
        $GITWEB_CONFIG = ""        if ($GITWEB_CONFIG eq $GITWEB_CONFIG_COMMON);
@@ -988,7 +995,7 @@ our ($action, $project, $file_name, $file_parent, $hash, $hash_parent, $hash_bas
 sub evaluate_and_validate_params {
        our $action = $input_params{'action'};
        if (defined $action) {
-               if (!validate_action($action)) {
+               if (!is_valid_action($action)) {
                        die_error(400, "Invalid action parameter");
                }
        }
@@ -996,7 +1003,7 @@ sub evaluate_and_validate_params {
        # parameters which are pathnames
        our $project = $input_params{'project'};
        if (defined $project) {
-               if (!validate_project($project)) {
+               if (!is_valid_project($project)) {
                        undef $project;
                        die_error(404, "No such project");
                }
@@ -1004,21 +1011,21 @@ sub evaluate_and_validate_params {
 
        our $project_filter = $input_params{'project_filter'};
        if (defined $project_filter) {
-               if (!validate_pathname($project_filter)) {
+               if (!is_valid_pathname($project_filter)) {
                        die_error(404, "Invalid project_filter parameter");
                }
        }
 
        our $file_name = $input_params{'file_name'};
        if (defined $file_name) {
-               if (!validate_pathname($file_name)) {
+               if (!is_valid_pathname($file_name)) {
                        die_error(400, "Invalid file parameter");
                }
        }
 
        our $file_parent = $input_params{'file_parent'};
        if (defined $file_parent) {
-               if (!validate_pathname($file_parent)) {
+               if (!is_valid_pathname($file_parent)) {
                        die_error(400, "Invalid file parent parameter");
                }
        }
@@ -1026,21 +1033,21 @@ sub evaluate_and_validate_params {
        # parameters which are refnames
        our $hash = $input_params{'hash'};
        if (defined $hash) {
-               if (!validate_refname($hash)) {
+               if (!is_valid_refname($hash)) {
                        die_error(400, "Invalid hash parameter");
                }
        }
 
        our $hash_parent = $input_params{'hash_parent'};
        if (defined $hash_parent) {
-               if (!validate_refname($hash_parent)) {
+               if (!is_valid_refname($hash_parent)) {
                        die_error(400, "Invalid hash parent parameter");
                }
        }
 
        our $hash_base = $input_params{'hash_base'};
        if (defined $hash_base) {
-               if (!validate_refname($hash_base)) {
+               if (!is_valid_refname($hash_base)) {
                        die_error(400, "Invalid hash base parameter");
                }
        }
@@ -1060,7 +1067,7 @@ sub evaluate_and_validate_params {
 
        our $hash_parent_base = $input_params{'hash_parent_base'};
        if (defined $hash_parent_base) {
-               if (!validate_refname($hash_parent_base)) {
+               if (!is_valid_refname($hash_parent_base)) {
                        die_error(400, "Invalid hash parent base parameter");
                }
        }
@@ -1083,7 +1090,7 @@ sub evaluate_and_validate_params {
        our $search_use_regexp = $input_params{'search_use_regexp'};
 
        our $searchtext = $input_params{'searchtext'};
-       our $search_regexp;
+       our $search_regexp = undef;
        if (defined $searchtext) {
                if (length($searchtext) < 2) {
                        die_error(403, "At least two characters are required for search parameter");
@@ -1133,7 +1140,7 @@ sub handle_errors_html {
 
        # to avoid infinite loop where error occurs in die_error,
        # change handler to default handler, disabling handle_errors_html
-       set_message("Error occured when inside die_error:\n$msg");
+       set_message("Error occurred when inside die_error:\n$msg");
 
        # you cannot jump out of die_error when called as error handler;
        # the subroutine set via CGI::Carp::set_message is called _after_
@@ -1412,27 +1419,30 @@ sub href {
 ## ======================================================================
 ## validation, quoting/unquoting and escaping
 
-sub validate_action {
-       my $input = shift || return undef;
+sub is_valid_action {
+       my $input = shift;
        return undef unless exists $actions{$input};
-       return $input;
+       return 1;
 }
 
-sub validate_project {
-       my $input = shift || return undef;
-       if (!validate_pathname($input) ||
+sub is_valid_project {
+       my $input = shift;
+
+       return unless defined $input;
+       if (!is_valid_pathname($input) ||
                !(-d "$projectroot/$input") ||
                !check_export_ok("$projectroot/$input") ||
                ($strict_export && !project_in_list($input))) {
                return undef;
        } else {
-               return $input;
+               return 1;
        }
 }
 
-sub validate_pathname {
-       my $input = shift || return undef;
+sub is_valid_pathname {
+       my $input = shift;
 
+       return undef unless defined $input;
        # no '.' or '..' as elements of path, i.e. no '.' nor '..'
        # at the beginning, at the end, and between slashes.
        # also this catches doubled slashes
@@ -1443,24 +1453,33 @@ sub validate_pathname {
        if ($input =~ m!\0!) {
                return undef;
        }
-       return $input;
+       return 1;
 }
 
-sub validate_refname {
-       my $input = shift || return undef;
+sub is_valid_ref_format {
+       my $input = shift;
 
-       # textual hashes are O.K.
-       if ($input =~ m/^[0-9a-fA-F]{40}$/) {
-               return $input;
-       }
-       # it must be correct pathname
-       $input = validate_pathname($input)
-               or return undef;
+       return undef unless defined $input;
        # restrictions on ref name according to git-check-ref-format
        if ($input =~ m!(/\.|\.\.|[\000-\040\177 ~^:?*\[]|/$)!) {
                return undef;
        }
-       return $input;
+       return 1;
+}
+
+sub is_valid_refname {
+       my $input = shift;
+
+       return undef unless defined $input;
+       # textual hashes are O.K.
+       if ($input =~ m/^[0-9a-fA-F]{40}$/) {
+               return 1;
+       }
+       # it must be correct pathname
+       is_valid_pathname($input) or return undef;
+       # check git-check-ref-format restrictions
+       is_valid_ref_format($input) or return undef;
+       return 1;
 }
 
 # decode sequences of octets in utf8 into Perl's internal form,
@@ -1553,7 +1572,7 @@ sub sanitize {
        return undef unless defined $str;
 
        $str = to_utf8($str);
-       $str =~ s|([[:cntrl:]])|($1 =~ /[\t\n\r]/ ? $1 : quot_cec($1))|eg;
+       $str =~ s|([[:cntrl:]])|(index("\t\n\r", $1) != -1 ? $1 : quot_cec($1))|eg;
        return $str;
 }
 
@@ -2065,7 +2084,7 @@ sub picon_url {
        if (!$avatar_cache{$email}) {
                my ($user, $domain) = split('@', $email);
                $avatar_cache{$email} =
-                       "http://www.cs.indiana.edu/cgi-pub/kinzler/piconsearch.cgi/" .
+                       "//www.cs.indiana.edu/cgi-pub/kinzler/piconsearch.cgi/" .
                        "$domain/$user/" .
                        "users+domains+unknown/up/single";
        }
@@ -2080,7 +2099,7 @@ sub gravatar_url {
        my $email = lc shift;
        my $size = shift;
        $avatar_cache{$email} ||=
-               "http://www.gravatar.com/avatar/" .
+               "//www.gravatar.com/avatar/" .
                        Digest::MD5::md5_hex($email) . "?s=";
        return $avatar_cache{$email} . $size;
 }
@@ -2693,12 +2712,15 @@ sub git_get_project_config {
        # only subsection, if exists, is case sensitive,
        # and not lowercased by 'git config -z -l'
        if (my ($hi, $mi, $lo) = ($key =~ /^([^.]*)\.(.*)\.([^.]*)$/)) {
+               $lo =~ s/_//g;
                $key = join(".", lc($hi), $mi, lc($lo));
+               return if ($lo =~ /\W/ || $hi =~ /\W/);
        } else {
                $key = lc($key);
+               $key =~ s/_//g;
+               return if ($key =~ /\W/);
        }
        $key =~ s/^gitweb\.//;
-       return if ($key =~ m/\W/);
 
        # type sanity check
        if (defined $type) {
@@ -3976,7 +3998,9 @@ sub print_nav_breadcrumbs_path {
 sub print_nav_breadcrumbs {
        my %opts = @_;
 
-       print $cgi->a({-href => esc_url($home_link)}, $home_link_str) . " / ";
+       for my $crumb (@extra_breadcrumbs, [ $home_link_str => $home_link ]) {
+               print $cgi->a({-href => esc_url($crumb->[1])}, $crumb->[0]) . " / ";
+       }
        if (defined $project) {
                my @dirname = split '/', $project;
                my $projectbasename = pop @dirname;
@@ -4024,8 +4048,8 @@ sub print_search_form {
              $cgi->input({-name=>"h", -value=>$search_hash, -type=>"hidden"}) . "\n" .
              $cgi->popup_menu(-name => 'st', -default => 'commit',
                               -values => ['commit', 'grep', 'author', 'committer', 'pickaxe']) .
-             $cgi->sup($cgi->a({-href => href(action=>"search_help")}, "?")) .
-             " search:\n",
+             " " . $cgi->a({-href => href(action=>"search_help"),
+                            -title => "search help" }, "?") . " search:\n",
              $cgi->textfield(-name => "s", -value => $searchtext, -override => 1) . "\n" .
              "<span title=\"Extended regular expression\">" .
              $cgi->checkbox(-name => 'sr', -value => 1, -label => 're',
@@ -4495,6 +4519,16 @@ sub git_print_log {
                        next;
                }
 
+               if ($line =~ m,\s*([a-z]*link): (https?://\S+),i) {
+                       if (! $opts{'-remove_signoff'}) {
+                               print "<span class=\"signoff\">" . esc_html($1) . ": " .
+                                       "<a href=\"" . esc_html($2) . "\">" . esc_html($2) . "</a>" .
+                                       "</span><br/>\n";
+                               $skip_blank_line = 1;
+                       }
+                       next;
+               }
+
                # print only one empty line
                # do not print empty line after signoff
                if ($line eq "") {
@@ -5512,23 +5546,30 @@ sub fill_project_list_info {
 
 sub sort_projects_list {
        my ($projlist, $order) = @_;
-       my @projects;
 
-       my %order_info = (
-               project => { key => 'path', type => 'str' },
-               descr => { key => 'descr_long', type => 'str' },
-               owner => { key => 'owner', type => 'str' },
-               age => { key => 'age', type => 'num' }
-       );
-       my $oi = $order_info{$order};
-       return @$projlist unless defined $oi;
-       if ($oi->{'type'} eq 'str') {
-               @projects = sort {$a->{$oi->{'key'}} cmp $b->{$oi->{'key'}}} @$projlist;
-       } else {
-               @projects = sort {$a->{$oi->{'key'}} <=> $b->{$oi->{'key'}}} @$projlist;
+       sub order_str {
+               my $key = shift;
+               return sub { $a->{$key} cmp $b->{$key} };
        }
 
-       return @projects;
+       sub order_num_then_undef {
+               my $key = shift;
+               return sub {
+                       defined $a->{$key} ?
+                               (defined $b->{$key} ? $a->{$key} <=> $b->{$key} : -1) :
+                               (defined $b->{$key} ? 1 : 0)
+               };
+       }
+
+       my %orderings = (
+               project => order_str('path'),
+               descr => order_str('descr_long'),
+               owner => order_str('owner'),
+               age => order_num_then_undef('age'),
+       );
+
+       my $ordering = $orderings{$order};
+       return defined $ordering ? sort $ordering @$projlist : @$projlist;
 }
 
 # returns a hash of categories, containing the list of project
@@ -6440,7 +6481,7 @@ sub git_summary {
        print "<div class=\"title\">&nbsp;</div>\n";
        print "<table class=\"projects_list\">\n" .
              "<tr id=\"metadata_desc\"><td>description</td><td>" . esc_html($descr) . "</td></tr>\n";
-        unless ($omit_owner) {
+        if ($owner and not $omit_owner) {
                print  "<tr id=\"metadata_owner\"><td>owner</td><td>" . esc_html($owner) . "</td></tr>\n";
         }
        if (defined $cd{'rfc2822'}) {
@@ -6603,6 +6644,7 @@ sub git_blame_common {
                        $hash_base, '--', $file_name
                        or die_error(500, "Open git-blame --porcelain failed");
        }
+       binmode $fd, ':utf8';
 
        # incremental blame data returns early
        if ($format eq 'data') {
@@ -7462,7 +7504,7 @@ sub git_object {
                system(git_cmd(), "cat-file", '-e', $hash_base) == 0
                        or die_error(404, "Base object does not exist");
 
-               # here errors should not hapen
+               # here errors should not happen
                open my $fd, "-|", git_cmd(), "ls-tree", $hash_base, "--", $file_name
                        or die_error(500, "Open git-ls-tree failed");
                my $line = <$fd>;
@@ -8014,7 +8056,7 @@ sub git_feed {
                %latest_commit = %{$commitlist[0]};
                my $latest_epoch = $latest_commit{'committer_epoch'};
                exit_if_unmodified_since($latest_epoch);
-               %latest_date = parse_date($latest_epoch, $latest_commit{'comitter_tz'});
+               %latest_date = parse_date($latest_epoch, $latest_commit{'committer_tz'});
        }
        print $cgi->header(
                -type => $content_type,
@@ -8041,6 +8083,7 @@ sub git_feed {
                $feed_type = 'history';
        }
        $title .= " $feed_type";
+       $title = esc_html($title);
        my $descr = git_get_project_description($project);
        if (defined $descr) {
                $descr = esc_html($descr);
This page took 0.203803 seconds and 4 git commands to generate.