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   2278684 use strict;
  41         356  
  41         1223  
4 41     41   204 use warnings;
  41         82  
  41         2705  
5              
6             our $VERSION = '5.21';
7              
8             # 1=version 5.10 and earlier; 0=version 5.11 and later
9 41 100   41   294 use constant HAS_RESERVED_SQUARE_BRACKETS => $ENV{URI_HAS_RESERVED_SQUARE_BRACKETS} ? 1 : 0;
  41         77  
  41         12028  
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   310 use Carp ();
  41         95  
  41         914  
41 41     41   18619 use URI::Escape ();
  41         104  
  41         4439  
42              
43 852     852   54343 use overload ('""' => sub { ${$_[0]} },
  852         4163  
44 31     31   164 '==' => sub { _obj_eq(@_) },
45 1     1   1096 '!=' => sub { !_obj_eq(@_) },
46 41         441 fallback => 1,
47 41     41   52831 );
  41         44956  
48              
49             # Check if two objects are the same object
50             sub _obj_eq {
51 32     32   121 return overload::StrVal($_[0]) eq overload::StrVal($_[1]);
52             }
53              
54             sub new
55             {
56 1216     1216 1 66528 my($class, $uri, $scheme) = @_;
57              
58 1216 50       3016 $uri = defined ($uri) ? "$uri" : ""; # stringify
59             # Get rid of potential wrapping
60 1216         2249 $uri =~ s/^<(?:URL:)?(.*)>$/$1/; #
61 1216         1812 $uri =~ s/^"(.*)"$/$1/;
62 1216         2710 $uri =~ s/^\s+//;
63 1216         2086 $uri =~ s/\s+$//;
64              
65 1216         1509 my $impclass;
66 1216 100       5044 if ($uri =~ m/^($scheme_re):/so) {
67 925         2119 $scheme = $1;
68             }
69             else {
70 291 100 66     1817 if (($impclass = ref($scheme))) {
    100          
71 11         60 $scheme = $scheme->scheme;
72             }
73             elsif ($scheme && $scheme =~ m/^($scheme_re)(?::|$)/o) {
74 136         383 $scheme = $1;
75             }
76             }
77             $impclass ||= implementor($scheme) ||
78 1216   66     3905 do {
      66        
79             require URI::_foreign;
80             $impclass = 'URI::_foreign';
81             };
82              
83 1216         3430 return $impclass->_init($uri, $scheme);
84             }
85              
86              
87             sub new_abs
88             {
89 1     1 1 295 my($class, $uri, $base) = @_;
90 1         5 $uri = $class->new($uri, $base);
91 1         8 $uri->abs($base);
92             }
93              
94              
95             sub _init
96             {
97 1214     1214   1897 my $class = shift;
98 1214         2175 my($str, $scheme) = @_;
99             # find all funny characters and encode the bytes.
100 1214         3025 $str = $class->_uric_escape($str);
101 1214 50 66     6120 $str = "$scheme:$str" unless $str =~ /^$scheme_re:/o ||
102             $class->_no_scheme_ok;
103 1214         2601 my $self = bless \$str, $class;
104 1214         3731 $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   1478 return if HAS_RESERVED_SQUARE_BRACKETS;
116 1208 100       2838 return if $_[0] !~ /%/;
117 75 100       833 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       430 if ($_[0] =~ m{^(?:$URI::fallback_schemes_re):}os) {
121 10         31 $_[0] =~ s/\%5B/[/gi;
122 10         21 $_[0] =~ s/\%5D/]/gi;
123 10         15 return;
124             }
125              
126 61 100       854 if ($_[0] =~ m{^((?:$URI::scheme_re:)?)//([^/?\#]+)(.*)$}os) {
127 46         132 my $orig = $2;
128 46         280 my ($user, $host) = $orig =~ /^(.*@)?([^@]*)$/;
129 46   100     220 $user ||= '';
130 46 100       181 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         182 $host =~ s/\%5B/[/gi;
133 46         121 $host =~ s/\%5D/]/gi;
134 46         784 $_[0] =~ s/\Q$orig\E/$user$host$port/;
135             }
136             }
137              
138              
139             sub _uric_escape
140             {
141 1214     1214   2077 my($class, $str) = @_;
142 1214         3251 $str =~ s*([^$uric\#])* URI::Escape::escape_char($1) *ego;
  256         531  
143 1214         2471 _fix_uric_escape_for_host_part( $str );
144 1214         2734 utf8::downgrade($str);
145 1214         2365 return $str;
146             }
147              
148             my %require_attempted;
149              
150             sub implementor
151             {
152 1205     1205 0 2256 my($scheme, $impclass) = @_;
153 1205 100 66     5970 if (!$scheme || $scheme !~ /\A$scheme_re\z/o) {
154 144         746 require URI::_generic;
155 144         636 return "URI::_generic";
156             }
157              
158 1061         2208 $scheme = lc($scheme);
159              
160 1061 50       1867 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         1821 my $ic = $implements{$scheme};
169 1061 100       4215 return $ic if $ic;
170              
171             # scheme not yet known, look for internal or
172             # preloaded (with 'use') implementation
173 107         262 $ic = "URI::$scheme"; # default location
174              
175             # turn scheme into a valid perl identifier by a simple transformation...
176 107         228 $ic =~ s/\+/_P/g;
177 107         179 $ic =~ s/\./_O/g;
178 107         195 $ic =~ s/\-/_/g;
179              
180 41     41   45146 no strict 'refs';
  41         110  
  41         68807  
181             # check we actually have one for the scheme:
182 107 100       159 unless (@{"${ic}::ISA"}) {
  107         846  
183 103 50       334 if (not exists $require_attempted{$ic}) {
184             # Try to load it
185 103         190 my $_old_error = $@;
186 103         5311 eval "require $ic";
187 103 50 66     1158 die $@ if $@ && $@ !~ /Can\'t locate.*in \@INC/;
188 103         274 $@ = $_old_error;
189             }
190 103 100       162 return undef unless @{"${ic}::ISA"};
  103         625  
191             }
192              
193 62         658 $ic->_init_implementor($scheme);
194 62         198 $implements{$scheme} = $ic;
195 62         365 $ic;
196             }
197              
198              
199             sub _init_implementor
200             {
201 62     62   350 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 816 my $self = shift;
210 421         698 my $other = $$self;
211 421         983 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   1905 my $self = shift;
221              
222 1554 100       2941 unless (@_) {
223 1220 100       6428 return undef unless $$self =~ /^($scheme_re):/o;
224 904         2973 return $1;
225             }
226              
227 334         425 my $old;
228 334         498 my $new = shift;
229 334 100 100     1188 if (defined($new) && length($new)) {
230 270 50       1279 Carp::croak("Bad scheme '$new'") unless $new =~ /^$scheme_re$/o;
231 270 100       1109 $old = $1 if $$self =~ s/^($scheme_re)://o;
232 270         870 my $newself = URI->new("$new:$$self");
233 270         745 $$self = $$newself;
234 270         715 bless $self, ref($newself);
235             }
236             else {
237 64 50       203 if ($self->_no_scheme_ok) {
238 64 50       597 $old = $1 if $$self =~ s/^($scheme_re)://o;
239 64 50 33     246 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         601 return $old;
248             }
249              
250             sub scheme
251             {
252 1085     1085 1 2146 my $scheme = shift->_scheme(@_);
253 1085 100       2492 return undef unless defined $scheme;
254 585         1503 lc($scheme);
255             }
256              
257             sub has_recognized_scheme {
258 6     6 1 37 my $self = shift;
259 6         41 return ref($self) !~ /^URI::_(?:foreign|generic)\z/;
260             }
261              
262             sub opaque
263             {
264 84     84 1 135 my $self = shift;
265              
266 84 100       187 unless (@_) {
267 57 50       692 $$self =~ /^(?:$scheme_re:)?([^\#]*)/o or die;
268 57         298 return $1;
269             }
270              
271 27 50       410 $$self =~ /^($scheme_re:)? # optional scheme
272             ([^\#]*) # opaque
273             (\#.*)? # optional fragment
274             $/sx or die;
275              
276 27         72 my $old_scheme = $1;
277 27         71 my $old_opaque = $2;
278 27         43 my $old_frag = $3;
279              
280 27         40 my $new_opaque = shift;
281 27 100       50 $new_opaque = "" unless defined $new_opaque;
282 27         219 $new_opaque =~ s/([^$uric])/ URI::Escape::escape_char($1)/ego;
  14         35  
283 27         70 utf8::downgrade($new_opaque);
284              
285 27 100       66 $$self = defined($old_scheme) ? $old_scheme : "";
286 27         62 $$self .= $new_opaque;
287 27 100       57 $$self .= $old_frag if defined $old_frag;
288              
289 27         71 $old_opaque;
290             }
291              
292 1     1 1 28 sub path { goto &opaque } # alias
293              
294              
295             sub fragment
296             {
297 71     71 1 127 my $self = shift;
298 71 100       163 unless (@_) {
299 51 100       239 return undef unless $$self =~ /\#(.*)/s;
300 24         120 return $1;
301             }
302              
303 20         35 my $old;
304 20 100       131 $old = $1 if $$self =~ s/\#(.*)//s;
305              
306 20         42 my $new_frag = shift;
307 20 100       64 if (defined $new_frag) {
308 18         129 $new_frag =~ s/([^$uric])/ URI::Escape::escape_char($1) /ego;
  2         6  
309 18         65 utf8::downgrade($new_frag);
310 18         51 $$self .= "#$new_frag";
311             }
312 20         42 $old;
313             }
314              
315              
316             sub as_string
317             {
318 726     726 1 1931 my $self = shift;
319 726         3070 $$self;
320             }
321              
322              
323             sub as_iri
324             {
325 15     15 1 18 my $self = shift;
326 15         27 my $str = $$self;
327 15 100       70 if ($str =~ s/%([89a-fA-F][0-9a-fA-F])/chr(hex($1))/eg) {
  274         670  
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         51 require Encode;
336 10         26 my $enc = Encode::find_encoding("UTF-8");
337 10         276 my $u = "";
338 10         24 while (length $str) {
339 13         66 $u .= $enc->decode($str, Encode::FB_QUIET());
340 13 100       99 if (length $str) {
341             # escape next char
342 3         13 $u .= URI::Escape::escape_char(substr($str, 0, 1, ""));
343             }
344             }
345 10         18 $str = $u;
346             }
347 15         38 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 660 my $self = shift;
357 458   100     866 my $scheme = $self->_scheme || "";
358 458         949 my $uc_scheme = $scheme =~ /[A-Z]/;
359 458         806 my $esc = $$self =~ /%[a-fA-F0-9]{2}/;
360 458 100 100     1900 return $self unless $uc_scheme || $esc;
361              
362 53         164 my $other = $self->clone;
363 53 100       124 if ($uc_scheme) {
364 10         38 $other->_scheme(lc $scheme);
365             }
366 53 100       111 if ($esc) {
367 43         184 $$other =~ s{%([0-9a-fA-F]{2})}
  226         484  
368 226 100       985 { my $a = chr(hex($1));
369             $a =~ /^[$unreserved]\z/o ? $a : "%\U$1"
370             }ge;
371 53         168 }
372             return $other;
373             }
374              
375             # Compare two URIs, subclasses will provide a more correct implementation
376 15     15 1 48 sub eq {
377 15 50       38 my($self, $other) = @_;
378 15 100       58 $self = URI->new($self, $other) unless ref $self;
379 15 50       69 $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 6 # generic-URI transformation methods
385 1     1 1 6 sub abs { $_[0]; }
386             sub rel { $_[0]; }
387 4     4 1 19  
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__