@@ -1186,7 +1186,7 @@ sub href {
11861186 $href =~ s ,/$, ,;
11871187
11881188 # Then add the project name, if present
1189- $href .= " /" .esc_url ($params {' project' });
1189+ $href .= " /" .esc_path_info ($params {' project' });
11901190 delete $params {' project' };
11911191
11921192 # since we destructively absorb parameters, we keep this
@@ -1196,7 +1196,8 @@ sub href {
11961196 # Summary just uses the project path URL, any other action is
11971197 # added to the URL
11981198 if (defined $params {' action' }) {
1199- $href .= " /" .esc_url($params {' action' }) unless $params {' action' } eq ' summary' ;
1199+ $href .= " /" .esc_path_info($params {' action' })
1200+ unless $params {' action' } eq ' summary' ;
12001201 delete $params {' action' };
12011202 }
12021203
@@ -1206,33 +1207,33 @@ sub href {
12061207 || $params {' hash_parent' } || $params {' hash' });
12071208 if (defined $params {' hash_base' }) {
12081209 if (defined $params {' hash_parent_base' }) {
1209- $href .= esc_url ($params {' hash_parent_base' });
1210+ $href .= esc_path_info ($params {' hash_parent_base' });
12101211 # skip the file_parent if it's the same as the file_name
12111212 if (defined $params {' file_parent' }) {
12121213 if (defined $params {' file_name' } && $params {' file_parent' } eq $params {' file_name' }) {
12131214 delete $params {' file_parent' };
12141215 } elsif ($params {' file_parent' } !~ / \.\. / ) {
1215- $href .= " :/" .esc_url ($params {' file_parent' });
1216+ $href .= " :/" .esc_path_info ($params {' file_parent' });
12161217 delete $params {' file_parent' };
12171218 }
12181219 }
12191220 $href .= " .." ;
12201221 delete $params {' hash_parent' };
12211222 delete $params {' hash_parent_base' };
12221223 } elsif (defined $params {' hash_parent' }) {
1223- $href .= esc_url ($params {' hash_parent' }). " .." ;
1224+ $href .= esc_path_info ($params {' hash_parent' }). " .." ;
12241225 delete $params {' hash_parent' };
12251226 }
12261227
1227- $href .= esc_url ($params {' hash_base' });
1228+ $href .= esc_path_info ($params {' hash_base' });
12281229 if (defined $params {' file_name' } && $params {' file_name' } !~ / \.\. / ) {
1229- $href .= " :/" .esc_url ($params {' file_name' });
1230+ $href .= " :/" .esc_path_info ($params {' file_name' });
12301231 delete $params {' file_name' };
12311232 }
12321233 delete $params {' hash' };
12331234 delete $params {' hash_base' };
12341235 } elsif (defined $params {' hash' }) {
1235- $href .= esc_url ($params {' hash' });
1236+ $href .= esc_path_info ($params {' hash' });
12361237 delete $params {' hash' };
12371238 }
12381239
@@ -1265,6 +1266,9 @@ sub href {
12651266 }
12661267 $href .= " ?" . join (' ;' , @result ) if scalar @result ;
12671268
1269+ # final transformation: trailing spaces must be escaped (URI-encoded)
1270+ $href =~ s / (\s +)$/ CGI::escape($1 )/ e ;
1271+
12681272 return $href ;
12691273}
12701274
@@ -1347,6 +1351,17 @@ sub esc_param {
13471351 return $str ;
13481352}
13491353
1354+ # the quoting rules for path_info fragment are slightly different
1355+ sub esc_path_info {
1356+ my $str = shift ;
1357+ return undef unless defined $str ;
1358+
1359+ # path_info doesn't treat '+' as space (specially), but '?' must be escaped
1360+ $str =~ s / ([^A-Za-z0-9\- _.~();\/ ;:@&= +]+)/ CGI::escape($1 )/ eg ;
1361+
1362+ return $str ;
1363+ }
1364+
13501365# quote unsafe chars in whole URL, so some characters cannot be quoted
13511366sub esc_url {
13521367 my $str = shift ;
0 commit comments