File Coverage

blib/lib/URI.pm
Criterion Covered Total %
statement 185 197 93.9
branch 79 94 84.0
condition 23 31 74.1
subroutine 32 36 88.8
pod 15 19 78.9
total 334 377 88.5


line stmt bran cond sub pod time code
1             package URI;
2              
3 41     41   2192652 use strict;
  41         377  
  41         1198  
4 41     41   202 use warnings;
  41         116  
  41         2649  
5              
6             our $VERSION = '5.20';
7              
8             # 1=version 5.10 and earlier; 0=version 5.11 and later
9 41 100   41   308 use constant HAS_RESERVED_SQUARE_BRACKETS => $ENV{URI_HAS_RESERVED_SQUARE_BRACKETS} ? 1 : 0;
  41         87  
  41         12196  
10              
11             our ($ABS_REMOTE_LEADING_DOTS, $ABS_ALLOW_RELATIVE_SCHEME, $DEFAULT_QUERY_FORM_DELIMITER);
12              
13             my %implements; # mapping from scheme to implementor class
14              
15             # Some "official" character classes
16              
17             our $reserved = HAS_RESERVED_SQUARE_BRACKETS ? q(;/?:@&=+$,[]) : q(;/?:@&=+$,);
18             our $mark = q(-_.!~*'()); #'; emacs
19             our $unreserved = "A-Za-z0-9\Q$mark\E";
20             our $uric = quotemeta($reserved) . $unreserved . "%";
21             our $uric4host = $uric . ( HAS_RESERVED_SQUARE_BRACKETS ? '' : quotemeta( q([]) ) );
22             our $uric4user = quotemeta( q{!$'()*,;:._~%-+=%&} ) . "A-Za-z0-9" . ( HAS_RESERVED_SQUARE_BRACKETS ? quotemeta( q([]) ) : '' ); # RFC-3987: iuserinfo w/o UTF
23              
24             our $scheme_re = '[a-zA-Z][a-zA-Z0-9.+\-]*';
25              
26             # These schemes don't have an IPv6+ address part.
27             our $schemes_without_host_part_re = 'data|ldapi|urn|sqlite|sqlite3';
28              
29             # These schemes can have an IPv6+ authority part:
30             # file, ftp, gopher, http, https, ldap, ldaps, mms, news, nntp, nntps, pop, rlogin, rtsp, rtspu, rsync, sip, sips, snews,
31             # telnet, tn3270, ssh, sftp
32             # (all DB URIs, i.e. cassandra, couch, couchdb, etc.), except 'sqlite:', 'sqlite3:'. Others?
33             #MAINT: URI has no test coverage for DB schemes
34             #MAINT: decoupling - perhaps let each class decide itself by defining a member function 'scheme_has_authority_part()'?
35              
36             #MAINT: 'mailto:' needs special treatment for IPv* addresses / RFC 5321 (4.1.3). Until then: restore all '[', ']'
37             # These schemes need fallback to previous (<= 5.10) encoding until a specific handler is available.
38             our $fallback_schemes_re = 'mailto';
39              
40 41     41   313 use Carp ();
  41         87  
  41         792  
41 41     41   17641 use URI::Escape ();
  41         142  
  41         4316  
42              
43 852     852   53524 use overload ('""' => sub { ${$_[0]} },
  852         3934  
44 31     31   150 '==' => sub { _obj_eq(@_) },
45 1     1   994 '!=' => sub { !_obj_eq(@_) },
46 41         441 fallback => 1,
47 41     41   50412 );
  41         42550  
48              
49             # Check if two objects are the same object
50             sub _obj_eq {
51 32     32   110 return overload::StrVal($_[0]) eq overload::StrVal($_[1]);
52             }
53              
54             sub new
55             {
56 1216     1216 1 65683 my($class, $uri, $scheme) = @_;
57              
58 1216 50       3010 $uri = defined ($uri) ? "$uri" : ""; # stringify
59             # Get rid of potential wrapping
60 1216         2303 $uri =~ s/^<(?:URL:)?(.*)>$/$1/; #
61 1216         1796 $uri =~ s/^"(.*)"$/$1/;
62 1216         2631 $uri =~ s/^\s+//;
63 1216         2092 $uri =~ s/\s+$//;
64              
65 1216         1478 my $impclass;
66 1216 100       4952 if ($uri =~ m/^($scheme_re):/so) {
67 925         2121 $scheme = $1;
68             }
69             else {
70 291 100 66     1779 if (($impclass = ref($scheme))) {
    100          
71 11         53 $scheme = $scheme->scheme;
72             }
73             elsif ($scheme && $scheme =~ m/^($scheme_re)(?::|$)/o) {
74 136         377 $scheme = $1;
75             }
76             }
77             $impclass ||= implementor($scheme) ||
78 1216   66     3862 do {
      66        
79             require URI::_foreign;
80             $impclass = 'URI::_foreign';
81             };
82              
83 1216         3631 return $impclass->_init($uri, $scheme);
84             }
85              
86              
87             sub new_abs
88             {
89 1     1 1 460 my($class, $uri, $base) = @_;
90 1         3 $uri = $class->new($uri, $base);
91 1         3 $uri->abs($base);
92             }
93              
94              
95             sub _init
96             {
97 1214     1214   2227 my $class = shift;
98 1214         2173 my($str, $scheme) = @_;
99             # find all funny characters and encode the bytes.
100 1214         3084 $str = $class->_uric_escape($str);
101 1214 50 66     5357 $str = "$scheme:$str" unless $str =~ /^$scheme_re:/o ||
102             $class->_no_scheme_ok;
103 1214         2535 my $self = bless \$str, $class;
104 1214         3629 $self;
105             }
106              
107              
108             #-- Version: 5.11+
109             # Since the complete URI will be percent-encoded including '[' and ']',
110             # we selectively unescape square brackets from the authority/host part of the URI.
111             # Derived modules that implement _uric_escape() should take this into account
112             # if they do not rely on URI::_uric_escape().
113             # No unescaping is performed for the userinfo@ part of the authority part.
114             sub _fix_uric_escape_for_host_part {
115 1214     1214   1338 return if HAS_RESERVED_SQUARE_BRACKETS;
116 1208 100       2767 return if $_[0] !~ /%/;
117 75 100       808 return if $_[0] =~ m{^(?:$URI::schemes_without_host_part_re):}os;
118              
119             # until a scheme specific handler is available, fall back to previous behavior of v5.10 (i.e. 'mailto:')
120 71 100       383 if ($_[0] =~ m{^(?:$URI::fallback_schemes_re):}os) {
121 10         30 $_[0] =~ s/\%5B/[/gi;
122 10         21 $_[0] =~ s/\%5D/]/gi;
123 10         15 return;
124             }
125              
126 61 100       781 if ($_[0] =~ m{^((?:$URI::scheme_re:)?)//([^/?\#]+)(.*)$}os) {
127 46         124 my $orig = $2;
128 46         285 my ($user, $host) = $orig =~ /^(.*@)?([^@]*)$/;
129 46   100     217 $user ||= '';
130 46 100       185 my $port = $host =~ s/(:\d+)$// ? $1 : '';
131             #MAINT: die() here if scheme indicates TCP/UDP and port is out of range [0..65535] ?
132 46         235 $host =~ s/\%5B/[/gi;
133 46         123 $host =~ s/\%5D/]/gi;
134 46         746 $_[0] =~ s/\Q$orig\E/$user$host$port/;
135             }
136             }
137              
138              
139             sub _uric_escape
140             {
141 1214     1214   2069 my($class, $str) = @_;
142 1214         3057 $str =~ s*([^$uric\#])* URI::Escape::escape_char($1) *ego;
  256         545  
143 1214         2473 _fix_uric_escape_for_host_part( $str );
144 1214         2692 utf8::downgrade($str);
145 1214         2233 return $str;
146             }
147              
148             my %require_attempted;
149              
150             sub implementor
151             {
152 1205     1205 0 2212 my($scheme, $impclass) = @_;
153 1205 100 66     5955 if (!$scheme || $scheme !~ /\A$scheme_re\z/o) {
154 144         718 require URI::_generic;
155 144         608 return "URI::_generic";
156             }
157              
158 1061         2151 $scheme = lc($scheme);
159              
160 1061 50       1821 if ($impclass) {
161             # Set the implementor class for a given scheme
162 0         0 my $old = $implements{$scheme};
163 0         0 $impclass->_init_implementor($scheme);
164 0         0 $implements{$scheme} = $impclass;
165 0         0 return $old;
166             }
167              
168 1061         1828 my $ic = $implements{$scheme};
169 1061 100       4113 return $ic if $ic;
170              
171             # scheme not yet known, look for internal or
172             # preloaded (with 'use') implementation
173 107         214 $ic = "URI::$scheme"; # default location
174              
175             # turn scheme into a valid perl identifier by a simple transformation...
176 107         230 $ic =~ s/\+/_P/g;
177 107         192 $ic =~ s/\./_O/g;
178 107         189 $ic =~ s/\-/_/g;
179              
180 41     41   43009 no strict 'refs';
  41         119  
  41         65265  
181             # check we actually have one for the scheme:
182 107 100       143 unless (@{"${ic}::ISA"}) {
  107         795  
183 103 50       328 if (not exists $require_attempted{$ic}) {
184             # Try to load it
185 103         182 my $_old_error = $@;
186 103         5223 eval "require $ic";
187 103 50 66     1165 die $@ if $@ && $@ !~ /Can\'t locate.*in \@INC/;
188 103         300 $@ = $_old_error;
189             }
190 103 100       149 return undef unless @{"${ic}::ISA"};
  103         615  
191             }
192              
193 62         660 $ic->_init_implementor($scheme);
194 62         175 $implements{$scheme} = $ic;
195 62         350 $ic;
196             }
197              
198              
199             sub _init_implementor
200             {
201 62     62   278 my($class, $scheme) = @_;
202             # Remember that one implementor class may actually
203             # serve to implement several URI schemes.
204             }
205              
206              
207             sub clone
208             {
209 421     421 1 776 my $self = shift;
210 421         742 my $other = $$self;
211 421         992 bless \$other, ref $self;
212             }
213              
214 0     0 0 0 sub TO_JSON { ${$_[0]} }
  0         0  
215              
216 0     0   0 sub _no_scheme_ok { 0 }
217              
218             sub _scheme
219             {
220 1554     1554   1943 my $self = shift;
221              
222 1554 100       2887 unless (@_) {
223 1220 100       6798 return undef unless $$self =~ /^($scheme_re):/o;
224 904         2859 return $1;
225             }
226              
227 334         418 my $old;
228 334         496 my $new = shift;
229 334 100 100     1158 if (defined($new) && length($new)) {
230 270 50       1286 Carp::croak("Bad scheme '$new'") unless $new =~ /^$scheme_re$/o;
231 270 100       1081 $old = $1 if $$self =~ s/^($scheme_re)://o;
232 270         885 my $newself = URI->new("$new:$$self");
233 270         755 $$self = $$newself;
234 270         627 bless $self, ref($newself);
235             }
236             else {
237 64 50       194 if ($self->_no_scheme_ok) {
238 64 50       592 $old = $1 if $$self =~ s/^($scheme_re)://o;
239 64 50 33     234 Carp::carp("Oops, opaque part now look like scheme")
240             if $^W && $$self =~ m/^$scheme_re:/o
241             }
242             else {
243 0 0       0 $old = $1 if $$self =~ m/^($scheme_re):/o;
244             }
245             }
246              
247 334         607 return $old;
248             }
249              
250             sub scheme
251             {
252 1085     1085 1 2198 my $scheme = shift->_scheme(@_);
253 1085 100       2473 return undef unless defined $scheme;
254 585         1835 lc($scheme);
255             }
256              
257             sub has_recognized_scheme {
258 6     6 1 45 my $self = shift;
259 6         39 return ref($self) !~ /^URI::_(?:foreign|generic)\z/;
260             }
261              
262             sub opaque
263             {
264 84     84 1 126 my $self = shift;
265              
266 84 100       172 unless (@_) {
267 57 50       688 $$self =~ /^(?:$scheme_re:)?([^\#]*)/o or die;
268 57         305 return $1;
269             }
270              
271 27 50       423 $$self =~ /^($scheme_re:)? # optional scheme
272             ([^\#]*) # opaque
273             (\#.*)? # optional fragment
274             $/sx or die;
275              
276 27         80 my $old_scheme = $1;
277 27         56 my $old_opaque = $2;
278 27         46 my $old_frag = $3;
279              
280 27         34 my $new_opaque = shift;
281 27 100       50 $new_opaque = "" unless defined $new_opaque;
282 27         216 $new_opaque =~ s/([^$uric])/ URI::Escape::escape_char($1)/ego;
  14         41  
283 27         73 utf8::downgrade($new_opaque);
284              
285 27 100       63 $$self = defined($old_scheme) ? $old_scheme : "";
286 27         62 $$self .= $new_opaque;
287 27 100       52 $$self .= $old_frag if defined $old_frag;
288              
289 27         85 $old_opaque;
290             }
291              
292 1     1 1 12 sub path { goto &opaque } # alias
293              
294              
295             sub fragment
296             {
297 71     71 1 131 my $self = shift;
298 71 100       160 unless (@_) {
299 51 100       267 return undef unless $$self =~ /\#(.*)/s;
300 24         119 return $1;
301             }
302              
303 20         27 my $old;
304 20 100       108 $old = $1 if $$self =~ s/\#(.*)//s;
305              
306 20         41 my $new_frag = shift;
307 20 100       44 if (defined $new_frag) {
308 18         131 $new_frag =~ s/([^$uric])/ URI::Escape::escape_char($1) /ego;
  2         7  
309 18         45 utf8::downgrade($new_frag);
310 18         55 $$self .= "#$new_frag";
311             }
312 20         40 $old;
313             }
314              
315              
316             sub as_string
317             {
318 726     726 1 1917 my $self = shift;
319 726         2976 $$self;
320             }
321              
322              
323             sub as_iri
324             {
325 15     15 1 25 my $self = shift;
326 15         26 my $str = $$self;
327 15 100       77 if ($str =~ s/%([89a-fA-F][0-9a-fA-F])/chr(hex($1))/eg) {
  274         599  
328             # All this crap because the more obvious:
329             #
330             # Encode::decode("UTF-8", $str, sub { sprintf "%%%02X", shift })
331             #
332             # doesn't work before Encode 2.39. Wait for a standard release
333             # to bundle that version.
334              
335 10         53 require Encode;
336 10         27 my $enc = Encode::find_encoding("UTF-8");
337 10         303 my $u = "";
338 10         22 while (length $str) {
339 13         75 $u .= $enc->decode($str, Encode::FB_QUIET());
340 13 100       108 if (length $str) {
341             # escape next char
342 3         21 $u .= URI::Escape::escape_char(substr($str, 0, 1, ""));
343             }
344             }
345 10         19 $str = $u;
346             }
347 15         86 return $str;
348             }
349              
350              
351             sub canonical
352             {
353             # Make sure scheme is lowercased, that we don't escape unreserved chars,
354             # and that we use upcase escape sequences.
355              
356 458     458 1 615 my $self = shift;
357 458   100     822 my $scheme = $self->_scheme || "";
358 458         912 my $uc_scheme = $scheme =~ /[A-Z]/;
359 458         783 my $esc = $$self =~ /%[a-fA-F0-9]{2}/;
360 458 100 100     1840 return $self unless $uc_scheme || $esc;
361              
362 53         149 my $other = $self->clone;
363 53 100       117 if ($uc_scheme) {
364 10         29 $other->_scheme(lc $scheme);
365             }
366 53 100       110 if ($esc) {
367 43         186 $$other =~ s{%([0-9a-fA-F]{2})}
  226         550  
368 226 100       935 { my $a = chr(hex($1));
369             $a =~ /^[$unreserved]\z/o ? $a : "%\U$1"
370             }ge;
371 53         176 }
372             return $other;
373             }
374              
375             # Compare two URIs, subclasses will provide a more correct implementation
376 15     15 1 69 sub eq {
377 15 50       40 my($self, $other) = @_;
378 15 100       45 $self = URI->new($self, $other) unless ref $self;
379 15 50       63 $other = URI->new($other, $self) unless ref $other;
380             ref($self) eq ref($other) && # same class
381             $self->canonical->as_string eq $other->canonical->as_string;
382             }
383              
384 1     1 1 4 # generic-URI transformation methods
385 1     1 1 5 sub abs { $_[0]; }
386             sub rel { $_[0]; }
387 4     4 1 18  
388             sub secure { 0 }
389              
390             # help out Storable
391 0     0 0   sub STORABLE_freeze {
392 0           my($self, $cloning) = @_;
393             return $$self;
394             }
395              
396 0     0 0   sub STORABLE_thaw {
397 0           my($self, $cloning, $str) = @_;
398             $$self = $str;
399             }
400              
401             1;
402              
403             __END__