@@ -1230,7 +1230,7 @@ sub href {
12301230 $href =~ s ,/$, ,;
12311231
12321232 # Then add the project name, if present
1233- $href .= " /" .esc_url ($params {' project' });
1233+ $href .= " /" .esc_path_info ($params {' project' });
12341234 delete $params {' project' };
12351235
12361236 # since we destructively absorb parameters, we keep this
@@ -1240,7 +1240,8 @@ sub href {
12401240 # Summary just uses the project path URL, any other action is
12411241 # added to the URL
12421242 if (defined $params {' action' }) {
1243- $href .= " /" .esc_url($params {' action' }) unless $params {' action' } eq ' summary' ;
1243+ $href .= " /" .esc_path_info($params {' action' })
1244+ unless $params {' action' } eq ' summary' ;
12441245 delete $params {' action' };
12451246 }
12461247
@@ -1250,33 +1251,33 @@ sub href {
12501251 || $params {' hash_parent' } || $params {' hash' });
12511252 if (defined $params {' hash_base' }) {
12521253 if (defined $params {' hash_parent_base' }) {
1253- $href .= esc_url ($params {' hash_parent_base' });
1254+ $href .= esc_path_info ($params {' hash_parent_base' });
12541255 # skip the file_parent if it's the same as the file_name
12551256 if (defined $params {' file_parent' }) {
12561257 if (defined $params {' file_name' } && $params {' file_parent' } eq $params {' file_name' }) {
12571258 delete $params {' file_parent' };
12581259 } elsif ($params {' file_parent' } !~ / \.\. / ) {
1259- $href .= " :/" .esc_url ($params {' file_parent' });
1260+ $href .= " :/" .esc_path_info ($params {' file_parent' });
12601261 delete $params {' file_parent' };
12611262 }
12621263 }
12631264 $href .= " .." ;
12641265 delete $params {' hash_parent' };
12651266 delete $params {' hash_parent_base' };
12661267 } elsif (defined $params {' hash_parent' }) {
1267- $href .= esc_url ($params {' hash_parent' }). " .." ;
1268+ $href .= esc_path_info ($params {' hash_parent' }). " .." ;
12681269 delete $params {' hash_parent' };
12691270 }
12701271
1271- $href .= esc_url ($params {' hash_base' });
1272+ $href .= esc_path_info ($params {' hash_base' });
12721273 if (defined $params {' file_name' } && $params {' file_name' } !~ / \.\. / ) {
1273- $href .= " :/" .esc_url ($params {' file_name' });
1274+ $href .= " :/" .esc_path_info ($params {' file_name' });
12741275 delete $params {' file_name' };
12751276 }
12761277 delete $params {' hash' };
12771278 delete $params {' hash_base' };
12781279 } elsif (defined $params {' hash' }) {
1279- $href .= esc_url ($params {' hash' });
1280+ $href .= esc_path_info ($params {' hash' });
12801281 delete $params {' hash' };
12811282 }
12821283
@@ -1309,6 +1310,9 @@ sub href {
13091310 }
13101311 $href .= " ?" . join (' ;' , @result ) if scalar @result ;
13111312
1313+ # final transformation: trailing spaces must be escaped (URI-encoded)
1314+ $href =~ s / (\s +)$/ CGI::escape($1 )/ e ;
1315+
13121316 return $href ;
13131317}
13141318
@@ -1391,6 +1395,17 @@ sub esc_param {
13911395 return $str ;
13921396}
13931397
1398+ # the quoting rules for path_info fragment are slightly different
1399+ sub esc_path_info {
1400+ my $str = shift ;
1401+ return undef unless defined $str ;
1402+
1403+ # path_info doesn't treat '+' as space (specially), but '?' must be escaped
1404+ $str =~ s / ([^A-Za-z0-9\- _.~();\/ ;:@&= +]+)/ CGI::escape($1 )/ eg ;
1405+
1406+ return $str ;
1407+ }
1408+
13941409# quote unsafe chars in whole URL, so some characters cannot be quoted
13951410sub esc_url {
13961411 my $str = shift ;
0 commit comments