| line | stmt | bran | cond | sub | pod | time | code | 
| 1 |  |  |  |  |  |  | package URI::_generic; | 
| 2 |  |  |  |  |  |  |  | 
| 3 | 38 |  |  | 38 |  | 16952 | use strict; | 
|  | 38 |  |  |  |  | 107 |  | 
|  | 38 |  |  |  |  | 1059 |  | 
| 4 | 38 |  |  | 38 |  | 184 | use warnings; | 
|  | 38 |  |  |  |  | 73 |  | 
|  | 38 |  |  |  |  | 1056 |  | 
| 5 |  |  |  |  |  |  |  | 
| 6 | 38 |  |  | 38 |  | 173 | use parent qw(URI URI::_query); | 
|  | 38 |  |  |  |  | 65 |  | 
|  | 38 |  |  |  |  | 240 |  | 
| 7 |  |  |  |  |  |  |  | 
| 8 | 38 |  |  | 38 |  | 2007 | use URI::Escape qw(uri_unescape); | 
|  | 38 |  |  |  |  | 77 |  | 
|  | 38 |  |  |  |  | 1561 |  | 
| 9 | 38 |  |  | 38 |  | 214 | use Carp (); | 
|  | 38 |  |  |  |  | 76 |  | 
|  | 38 |  |  |  |  | 86840 |  | 
| 10 |  |  |  |  |  |  |  | 
| 11 |  |  |  |  |  |  | our $VERSION = '5.21'; | 
| 12 |  |  |  |  |  |  |  | 
| 13 |  |  |  |  |  |  | my $ACHAR = URI::HAS_RESERVED_SQUARE_BRACKETS ? $URI::uric : $URI::uric4host;  $ACHAR =~ s,\\[/?],,g; | 
| 14 |  |  |  |  |  |  | my $PCHAR = $URI::uric;                                                        $PCHAR =~ s,\\[?],,g; | 
| 15 |  |  |  |  |  |  |  | 
| 16 | 353 |  |  | 353 |  | 1438 | sub _no_scheme_ok { 1 } | 
| 17 |  |  |  |  |  |  |  | 
| 18 |  |  |  |  |  |  | our $IPv6_re; | 
| 19 |  |  |  |  |  |  |  | 
| 20 |  |  |  |  |  |  | sub _looks_like_raw_ip6_address { | 
| 21 | 319 |  |  | 319 |  | 528 | my $addr = shift; | 
| 22 |  |  |  |  |  |  |  | 
| 23 | 319 | 100 |  |  |  | 646 | if ( !$IPv6_re ) { #-- lazy / runs once / use Regexp::IPv6 if installed | 
| 24 |  |  |  |  |  |  | eval { | 
| 25 | 21 |  |  |  |  | 3480 | require Regexp::IPv6; | 
| 26 | 0 |  |  |  |  | 0 | Regexp::IPv6->import( qw($IPv6_re) ); | 
| 27 | 0 |  |  |  |  | 0 | 1; | 
| 28 | 21 | 50 |  |  |  | 56 | }  ||  do { $IPv6_re = qr/[:0-9a-f]{3,}/; }; #-- fallback: unambitious guess | 
|  | 21 |  |  |  |  | 142 |  | 
| 29 |  |  |  |  |  |  | } | 
| 30 |  |  |  |  |  |  |  | 
| 31 | 319 | 50 |  |  |  | 643 | return 0 unless $addr; | 
| 32 | 319 | 100 |  |  |  | 1134 | return 0 if $addr =~ tr/:/:/ < 2;  #-- fallback must not create false positive for IPv4:Port = 0:0 | 
| 33 | 26 | 100 |  |  |  | 327 | return 1 if $addr =~ /^$IPv6_re$/i; | 
| 34 | 24 |  |  |  |  | 70 | return 0; | 
| 35 |  |  |  |  |  |  | } | 
| 36 |  |  |  |  |  |  |  | 
| 37 |  |  |  |  |  |  |  | 
| 38 |  |  |  |  |  |  | sub authority | 
| 39 |  |  |  |  |  |  | { | 
| 40 | 2351 |  |  | 2351 | 1 | 3108 | my $self = shift; | 
| 41 | 2351 | 50 |  |  |  | 11040 | $$self =~ m,^((?:$URI::scheme_re:)?)(?://([^/?\#]*))?(.*)$,os or die; | 
| 42 |  |  |  |  |  |  |  | 
| 43 | 2351 | 100 |  |  |  | 4520 | if (@_) { | 
| 44 | 418 |  |  |  |  | 622 | my $auth = shift; | 
| 45 | 418 |  |  |  |  | 799 | $$self = $1; | 
| 46 | 418 |  |  |  |  | 732 | my $rest = $3; | 
| 47 | 418 | 100 |  |  |  | 818 | if (defined $auth) { | 
| 48 | 343 |  |  |  |  | 1047 | $auth =~ s/([^$ACHAR])/ URI::Escape::escape_char($1)/ego; | 
|  | 7 |  |  |  |  | 16 |  | 
| 49 | 343 | 100 |  |  |  | 1625 | if ( my ($user, $host) = $auth =~ /^(.*@)?([^@]+)$/ ) { #-- special escape userinfo part | 
| 50 | 319 |  | 100 |  |  | 1192 | $user ||= ''; | 
| 51 | 319 |  |  |  |  | 863 | $user =~ s/([^$URI::uric4user])/ URI::Escape::escape_char($1)/ego; | 
|  | 56 |  |  |  |  | 139 |  | 
| 52 | 319 |  |  |  |  | 579 | $user =~ s/%40$/\@/; # recover final '@' | 
| 53 | 319 | 100 |  |  |  | 612 | $host = "[$host]" if _looks_like_raw_ip6_address( $host ); | 
| 54 | 319 |  |  |  |  | 677 | $auth = $user . $host; | 
| 55 |  |  |  |  |  |  | } | 
| 56 | 343 |  |  |  |  | 854 | utf8::downgrade($auth); | 
| 57 | 343 |  |  |  |  | 740 | $$self .= "//$auth"; | 
| 58 |  |  |  |  |  |  | } | 
| 59 | 418 |  |  |  |  | 1023 | _check_path($rest, $$self); | 
| 60 | 418 |  |  |  |  | 892 | $$self .= $rest; | 
| 61 |  |  |  |  |  |  | } | 
| 62 | 2351 |  |  |  |  | 6410 | $2; | 
| 63 |  |  |  |  |  |  | } | 
| 64 |  |  |  |  |  |  |  | 
| 65 |  |  |  |  |  |  | sub path | 
| 66 |  |  |  |  |  |  | { | 
| 67 | 1321 |  |  | 1321 | 1 | 1985 | my $self = shift; | 
| 68 | 1321 | 50 |  |  |  | 5837 | $$self =~ m,^((?:[^:/?\#]+:)?(?://[^/?\#]*)?)([^?\#]*)(.*)$,s or die; | 
| 69 |  |  |  |  |  |  |  | 
| 70 | 1321 | 100 |  |  |  | 2529 | if (@_) { | 
| 71 | 301 |  |  |  |  | 620 | $$self = $1; | 
| 72 | 301 |  |  |  |  | 519 | my $rest = $3; | 
| 73 | 301 |  |  |  |  | 476 | my $new_path = shift; | 
| 74 | 301 | 100 |  |  |  | 550 | $new_path = "" unless defined $new_path; | 
| 75 | 301 |  |  |  |  | 907 | $new_path =~ s/([^$PCHAR])/ URI::Escape::escape_char($1)/ego; | 
|  | 191 |  |  |  |  | 345 |  | 
| 76 | 301 |  |  |  |  | 711 | utf8::downgrade($new_path); | 
| 77 | 301 |  |  |  |  | 609 | _check_path($new_path, $$self); | 
| 78 | 301 |  |  |  |  | 672 | $$self .= $new_path . $rest; | 
| 79 |  |  |  |  |  |  | } | 
| 80 | 1321 |  |  |  |  | 3363 | $2; | 
| 81 |  |  |  |  |  |  | } | 
| 82 |  |  |  |  |  |  |  | 
| 83 |  |  |  |  |  |  | sub path_query | 
| 84 |  |  |  |  |  |  | { | 
| 85 | 58 |  |  | 58 | 1 | 98 | my $self = shift; | 
| 86 | 58 | 50 |  |  |  | 398 | $$self =~ m,^((?:[^:/?\#]+:)?(?://[^/?\#]*)?)([^\#]*)(.*)$,s or die; | 
| 87 |  |  |  |  |  |  |  | 
| 88 | 58 | 100 |  |  |  | 134 | if (@_) { | 
| 89 | 3 |  |  |  |  | 9 | $$self = $1; | 
| 90 | 3 |  |  |  |  | 4 | my $rest = $3; | 
| 91 | 3 |  |  |  |  | 6 | my $new_path = shift; | 
| 92 | 3 | 50 |  |  |  | 6 | $new_path = "" unless defined $new_path; | 
| 93 | 3 |  |  |  |  | 41 | $new_path =~ s/([^$URI::uric])/ URI::Escape::escape_char($1)/ego; | 
|  | 1 |  |  |  |  | 4 |  | 
| 94 | 3 |  |  |  |  | 11 | utf8::downgrade($new_path); | 
| 95 | 3 |  |  |  |  | 8 | _check_path($new_path, $$self); | 
| 96 | 3 |  |  |  |  | 12 | $$self .= $new_path . $rest; | 
| 97 |  |  |  |  |  |  | } | 
| 98 | 58 |  |  |  |  | 184 | $2; | 
| 99 |  |  |  |  |  |  | } | 
| 100 |  |  |  |  |  |  |  | 
| 101 |  |  |  |  |  |  | sub _check_path | 
| 102 |  |  |  |  |  |  | { | 
| 103 | 722 |  |  | 722 |  | 1531 | my($path, $pre) = @_; | 
| 104 | 722 |  |  |  |  | 904 | my $prefix; | 
| 105 | 722 | 100 |  |  |  | 1782 | if ($pre =~ m,/,) {  # authority present | 
| 106 | 570 | 100 | 100 |  |  | 2725 | $prefix = "/" if length($path) && $path !~ m,^[/?\#],; | 
| 107 |  |  |  |  |  |  | } | 
| 108 |  |  |  |  |  |  | else { | 
| 109 | 152 | 50 | 66 |  |  | 695 | if ($path =~ m,^//,) { | 
|  |  | 50 |  |  |  |  |  | 
| 110 | 0 | 0 |  |  |  | 0 | Carp::carp("Path starting with double slash is confusing") | 
| 111 |  |  |  |  |  |  | if $^W; | 
| 112 |  |  |  |  |  |  | } | 
| 113 |  |  |  |  |  |  | elsif (!length($pre) && $path =~ m,^[^:/?\#]+:,) { | 
| 114 | 0 | 0 |  |  |  | 0 | Carp::carp("Path might look like scheme, './' prepended") | 
| 115 |  |  |  |  |  |  | if $^W; | 
| 116 | 0 |  |  |  |  | 0 | $prefix = "./"; | 
| 117 |  |  |  |  |  |  | } | 
| 118 |  |  |  |  |  |  | } | 
| 119 | 722 | 100 |  |  |  | 2068 | substr($_[0], 0, 0) = $prefix if defined $prefix; | 
| 120 |  |  |  |  |  |  | } | 
| 121 |  |  |  |  |  |  |  | 
| 122 |  |  |  |  |  |  | sub path_segments | 
| 123 |  |  |  |  |  |  | { | 
| 124 | 90 |  |  | 90 | 1 | 561 | my $self = shift; | 
| 125 | 90 |  |  |  |  | 180 | my $path = $self->path; | 
| 126 | 90 | 100 |  |  |  | 192 | if (@_) { | 
| 127 | 6 |  |  |  |  | 23 | my @arg = @_;  # make a copy | 
| 128 | 6 |  |  |  |  | 16 | for (@arg) { | 
| 129 | 21 | 100 |  |  |  | 37 | if (ref($_)) { | 
| 130 | 1 |  |  |  |  | 29 | my @seg = @$_; | 
| 131 | 1 |  |  |  |  | 3 | $seg[0] =~ s/%/%25/g; | 
| 132 | 1 |  |  |  |  | 1 | for (@seg) { s/;/%3B/g; } | 
|  | 3 |  |  |  |  | 5 |  | 
| 133 | 1 |  |  |  |  | 4 | $_ = join(";", @seg); | 
| 134 |  |  |  |  |  |  | } | 
| 135 |  |  |  |  |  |  | else { | 
| 136 | 20 |  |  |  |  | 28 | s/%/%25/g; s/;/%3B/g; | 
|  | 20 |  |  |  |  | 24 |  | 
| 137 |  |  |  |  |  |  | } | 
| 138 | 21 |  |  |  |  | 41 | s,/,%2F,g; | 
| 139 |  |  |  |  |  |  | } | 
| 140 | 6 |  |  |  |  | 30 | $self->path(join("/", @arg)); | 
| 141 |  |  |  |  |  |  | } | 
| 142 | 90 | 100 |  |  |  | 201 | return $path unless wantarray; | 
| 143 | 81 | 100 |  |  |  | 260 | map {/;/ ? $self->_split_segment($_) | 
|  | 194 |  |  |  |  | 580 |  | 
| 144 |  |  |  |  |  |  | : uri_unescape($_) } | 
| 145 |  |  |  |  |  |  | split('/', $path, -1); | 
| 146 |  |  |  |  |  |  | } | 
| 147 |  |  |  |  |  |  |  | 
| 148 |  |  |  |  |  |  |  | 
| 149 |  |  |  |  |  |  | sub _split_segment | 
| 150 |  |  |  |  |  |  | { | 
| 151 | 4 |  |  | 4 |  | 12 | my $self = shift; | 
| 152 | 4 |  |  |  |  | 912 | require URI::_segment; | 
| 153 | 4 |  |  |  |  | 27 | URI::_segment->new(@_); | 
| 154 |  |  |  |  |  |  | } | 
| 155 |  |  |  |  |  |  |  | 
| 156 |  |  |  |  |  |  |  | 
| 157 |  |  |  |  |  |  | sub abs | 
| 158 |  |  |  |  |  |  | { | 
| 159 | 274 |  |  | 274 | 1 | 601 | my $self = shift; | 
| 160 | 274 |  | 33 |  |  | 724 | my $base = shift || Carp::croak("Missing base argument"); | 
| 161 |  |  |  |  |  |  |  | 
| 162 | 274 | 100 |  |  |  | 684 | if (my $scheme = $self->scheme) { | 
| 163 | 29 | 100 |  |  |  | 137 | return $self unless $URI::ABS_ALLOW_RELATIVE_SCHEME; | 
| 164 | 10 | 50 |  |  |  | 34 | $base = URI->new($base) unless ref $base; | 
| 165 | 10 | 100 |  |  |  | 26 | return $self unless $scheme eq $base->scheme; | 
| 166 |  |  |  |  |  |  | } | 
| 167 |  |  |  |  |  |  |  | 
| 168 | 252 | 100 |  |  |  | 695 | $base = URI->new($base) unless ref $base; | 
| 169 | 252 |  |  |  |  | 628 | my $abs = $self->clone; | 
| 170 | 252 |  |  |  |  | 537 | $abs->scheme($base->scheme); | 
| 171 | 252 | 100 |  |  |  | 920 | return $abs if $$self =~ m,^(?:$URI::scheme_re:)?//,o; | 
| 172 | 241 |  |  |  |  | 575 | $abs->authority($base->authority); | 
| 173 |  |  |  |  |  |  |  | 
| 174 | 241 |  |  |  |  | 549 | my $path = $self->path; | 
| 175 | 241 | 100 |  |  |  | 628 | return $abs if $path =~ m,^/,; | 
| 176 |  |  |  |  |  |  |  | 
| 177 | 222 | 100 |  |  |  | 485 | if (!length($path)) { | 
| 178 | 26 |  |  |  |  | 83 | my $abs = $base->clone; | 
| 179 | 26 |  |  |  |  | 167 | my $query = $self->query; | 
| 180 | 26 | 100 |  |  |  | 89 | $abs->query($query) if defined $query; | 
| 181 | 26 |  |  |  |  | 149 | my $fragment = $self->fragment; | 
| 182 | 26 | 100 |  |  |  | 84 | $abs->fragment($fragment) if defined $fragment; | 
| 183 | 26 |  |  |  |  | 164 | return $abs; | 
| 184 |  |  |  |  |  |  | } | 
| 185 |  |  |  |  |  |  |  | 
| 186 | 196 |  |  |  |  | 401 | my $p = $base->path; | 
| 187 | 196 |  |  |  |  | 815 | $p =~ s,[^/]+$,,; | 
| 188 | 196 |  |  |  |  | 382 | $p .= $path; | 
| 189 | 196 |  |  |  |  | 694 | my @p = split('/', $p, -1); | 
| 190 | 196 | 100 | 66 |  |  | 987 | shift(@p) if @p && !length($p[0]); | 
| 191 | 196 |  |  |  |  | 335 | my $i = 1; | 
| 192 | 196 |  |  |  |  | 429 | while ($i < @p) { | 
| 193 |  |  |  |  |  |  | #print "$i ", join("/", @p), " ($p[$i])\n"; | 
| 194 | 616 | 100 | 100 |  |  | 1716 | if ($p[$i-1] eq ".") { | 
|  |  | 100 |  |  |  |  |  | 
| 195 | 32 |  |  |  |  | 62 | splice(@p, $i-1, 1); | 
| 196 | 32 | 50 |  |  |  | 86 | $i-- if $i > 1; | 
| 197 |  |  |  |  |  |  | } | 
| 198 |  |  |  |  |  |  | elsif ($p[$i] eq ".." && $p[$i-1] ne "..") { | 
| 199 | 107 |  |  |  |  | 192 | splice(@p, $i-1, 2); | 
| 200 | 107 | 100 |  |  |  | 214 | if ($i > 1) { | 
| 201 | 77 |  |  |  |  | 93 | $i--; | 
| 202 | 77 | 100 |  |  |  | 188 | push(@p, "") if $i == @p; | 
| 203 |  |  |  |  |  |  | } | 
| 204 |  |  |  |  |  |  | } | 
| 205 |  |  |  |  |  |  | else { | 
| 206 | 477 |  |  |  |  | 860 | $i++; | 
| 207 |  |  |  |  |  |  | } | 
| 208 |  |  |  |  |  |  | } | 
| 209 | 196 | 100 | 100 |  |  | 646 | $p[-1] = "" if @p && $p[-1] eq ".";  # trailing "/." | 
| 210 | 196 | 100 |  |  |  | 383 | if ($URI::ABS_REMOTE_LEADING_DOTS) { | 
| 211 | 3 |  | 66 |  |  | 37 | shift @p while @p && $p[0] =~ /^\.\.?$/; | 
| 212 |  |  |  |  |  |  | } | 
| 213 | 196 |  |  |  |  | 748 | $abs->path("/" . join("/", @p)); | 
| 214 | 196 |  |  |  |  | 1029 | $abs; | 
| 215 |  |  |  |  |  |  | } | 
| 216 |  |  |  |  |  |  |  | 
| 217 |  |  |  |  |  |  | # The opposite of $url->abs.  Return a URI which is as relative as possible | 
| 218 |  |  |  |  |  |  | sub rel { | 
| 219 | 67 |  |  | 67 | 1 | 131 | my $self = shift; | 
| 220 | 67 |  | 33 |  |  | 134 | my $base = shift || Carp::croak("Missing base argument"); | 
| 221 | 67 |  |  |  |  | 137 | my $rel = $self->clone; | 
| 222 | 67 | 50 |  |  |  | 195 | $base = URI->new($base) unless ref $base; | 
| 223 |  |  |  |  |  |  |  | 
| 224 |  |  |  |  |  |  | #my($scheme, $auth, $path) = @{$rel}{qw(scheme authority path)}; | 
| 225 | 67 |  |  |  |  | 153 | my $scheme = $rel->scheme; | 
| 226 | 67 |  |  |  |  | 175 | my $auth   = $rel->canonical->authority; | 
| 227 | 67 |  |  |  |  | 139 | my $path   = $rel->path; | 
| 228 |  |  |  |  |  |  |  | 
| 229 | 67 | 0 | 33 |  |  | 153 | if (!defined($scheme) && !defined($auth)) { | 
| 230 |  |  |  |  |  |  | # it is already relative | 
| 231 | 0 |  |  |  |  | 0 | return $rel; | 
| 232 |  |  |  |  |  |  | } | 
| 233 |  |  |  |  |  |  |  | 
| 234 |  |  |  |  |  |  | #my($bscheme, $bauth, $bpath) = @{$base}{qw(scheme authority path)}; | 
| 235 | 67 |  |  |  |  | 145 | my $bscheme = $base->scheme; | 
| 236 | 67 |  |  |  |  | 166 | my $bauth   = $base->canonical->authority; | 
| 237 | 67 |  |  |  |  | 138 | my $bpath   = $base->path; | 
| 238 |  |  |  |  |  |  |  | 
| 239 | 67 |  |  |  |  | 139 | for ($bscheme, $bauth, $auth) { | 
| 240 | 201 | 100 |  |  |  | 378 | $_ = '' unless defined | 
| 241 |  |  |  |  |  |  | } | 
| 242 |  |  |  |  |  |  |  | 
| 243 | 67 | 100 | 100 |  |  | 262 | unless ($scheme eq $bscheme && $auth eq $bauth) { | 
| 244 |  |  |  |  |  |  | # different location, can't make it relative | 
| 245 | 5 |  |  |  |  | 29 | return $rel; | 
| 246 |  |  |  |  |  |  | } | 
| 247 |  |  |  |  |  |  |  | 
| 248 | 62 | 100 |  |  |  | 97 | for ($path, $bpath) {  $_ = "/$_" unless m,^/,; } | 
|  | 124 |  |  |  |  | 354 |  | 
| 249 |  |  |  |  |  |  |  | 
| 250 |  |  |  |  |  |  | # Make it relative by eliminating scheme and authority | 
| 251 | 62 |  |  |  |  | 166 | $rel->scheme(undef); | 
| 252 | 62 |  |  |  |  | 142 | $rel->authority(undef); | 
| 253 |  |  |  |  |  |  |  | 
| 254 |  |  |  |  |  |  | # This loop is based on code from Nicolai Langfeldt . | 
| 255 |  |  |  |  |  |  | # First we calculate common initial path components length ($li). | 
| 256 | 62 |  |  |  |  | 77 | my $li = 1; | 
| 257 | 62 |  |  |  |  | 82 | while (1) { | 
| 258 | 137 |  |  |  |  | 215 | my $i = index($path, '/', $li); | 
| 259 | 137 | 100 | 100 |  |  | 534 | last if $i < 0 || | 
|  |  |  | 100 |  |  |  |  | 
| 260 |  |  |  |  |  |  | $i != index($bpath, '/', $li) || | 
| 261 |  |  |  |  |  |  | substr($path,$li,$i-$li) ne substr($bpath,$li,$i-$li); | 
| 262 | 75 |  |  |  |  | 137 | $li=$i+1; | 
| 263 |  |  |  |  |  |  | } | 
| 264 |  |  |  |  |  |  | # then we nuke it from both paths | 
| 265 | 62 |  |  |  |  | 126 | substr($path, 0,$li) = ''; | 
| 266 | 62 |  |  |  |  | 115 | substr($bpath,0,$li) = ''; | 
| 267 |  |  |  |  |  |  |  | 
| 268 | 62 | 100 | 100 |  |  | 229 | if ($path eq $bpath && | 
|  |  |  | 100 |  |  |  |  | 
| 269 |  |  |  |  |  |  | defined($rel->fragment) && | 
| 270 |  |  |  |  |  |  | !defined($rel->query)) { | 
| 271 | 1 |  |  |  |  | 4 | $rel->path(""); | 
| 272 |  |  |  |  |  |  | } | 
| 273 |  |  |  |  |  |  | else { | 
| 274 |  |  |  |  |  |  | # Add one "../" for each path component left in the base path | 
| 275 | 61 |  |  |  |  | 191 | $path = ('../' x $bpath =~ tr|/|/|) . $path; | 
| 276 | 61 | 100 |  |  |  | 163 | $path = "./" if $path eq ""; | 
| 277 | 61 |  |  |  |  | 143 | $rel->path($path); | 
| 278 |  |  |  |  |  |  | } | 
| 279 |  |  |  |  |  |  |  | 
| 280 | 62 |  |  |  |  | 250 | $rel; | 
| 281 |  |  |  |  |  |  | } | 
| 282 |  |  |  |  |  |  |  | 
| 283 |  |  |  |  |  |  | 1; |