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 40     40   2145835 use strict;
  40         372  
  40         1185  
4 40     40   196 use warnings;
  40         115  
  40         3258  
5              
6             our $VERSION = '5.19';
7              
8             # 1=version 5.10 and earlier; 0=version 5.11 and later
9 40 100   40   261 use constant HAS_RESERVED_SQUARE_BRACKETS => $ENV{URI_HAS_RESERVED_SQUARE_BRACKETS} ? 1 : 0;
  40         82  
  40         11907  
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 40     40   288 use Carp ();
  40         77  
  40         879  
41 40     40   17242 use URI::Escape ();
  40         107  
  40         4259  
42              
43 845     845   53146 use overload ('""' => sub { ${$_[0]} },
  845         4165  
44 31     31   154 '==' => sub { _obj_eq(@_) },
45 1     1   1082 '!=' => sub { !_obj_eq(@_) },
46 40         438 fallback => 1,
47 40     40   49072 );
  40         42190  
48              
49             # Check if two objects are the same object
50             sub _obj_eq {
51 32     32   128 return overload::StrVal($_[0]) eq overload::StrVal($_[1]);
52             }
53              
54             sub new
55             {
56 1213     1213 1 70639 my($class, $uri, $scheme) = @_;
57              
58 1213 50       3175 $uri = defined ($uri) ? "$uri" : ""; # stringify
59             # Get rid of potential wrapping
60 1213         2323 $uri =~ s/^<(?:URL:)?(.*)>$/$1/; #
61 1213         1668 $uri =~ s/^"(.*)"$/$1/;
62 1213         2738 $uri =~ s/^\s+//;
63 1213         2123 $uri =~ s/\s+$//;
64              
65 1213         1466 my $impclass;
66 1213 100       5071 if ($uri =~ m/^($scheme_re):/so) {
67 922         2106 $scheme = $1;
68             }
69             else {
70 291 100 66     1890 if (($impclass = ref($scheme))) {
    100          
71 11         50 $scheme = $scheme->scheme;
72             }
73             elsif ($scheme && $scheme =~ m/^($scheme_re)(?::|$)/o) {
74 136         383 $scheme = $1;
75             }
76             }
77             $impclass ||= implementor($scheme) ||
78 1213   66     4065 do {
      66        
79             require URI::_foreign;
80             $impclass = 'URI::_foreign';
81             };
82              
83 1213         3533 return $impclass->_init($uri, $scheme);
84             }
85              
86              
87             sub new_abs
88             {
89 1     1 1 402 my($class, $uri, $base) = @_;
90 1         4 $uri = $class->new($uri, $base);
91 1         8 $uri->abs($base);
92             }
93              
94              
95             sub _init
96             {
97 1211     1211   1888 my $class = shift;
98 1211         2151 my($str, $scheme) = @_;
99             # find all funny characters and encode the bytes.
100 1211         3031 $str = $class->_uric_escape($str);
101 1211 50 66     5349 $str = "$scheme:$str" unless $str =~ /^$scheme_re:/o ||
102             $class->_no_scheme_ok;
103 1211         2554 my $self = bless \$str, $class;
104 1211         3905 $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 1211     1211   1439 return if HAS_RESERVED_SQUARE_BRACKETS;
116 1205 100       3057 return if $_[0] !~ /%/;
117 73 100       719 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 69 100       466 if ($_[0] =~ m{^(?:$URI::fallback_schemes_re):}os) {
121 10         26 $_[0] =~ s/\%5B/[/gi;
122 10         23 $_[0] =~ s/\%5D/]/gi;
123 10         16 return;
124             }
125              
126 59 100       765 if ($_[0] =~ m{^((?:$URI::scheme_re:)?)//([^/?\#]+)(.*)$}os) {
127 44         148 my $orig = $2;
128 44         276 my ($user, $host) = $orig =~ /^(.*@)?([^@]*)$/;
129 44   100     204 $user ||= '';
130 44 100       190 my $port = $host =~ s/(:\d+)$// ? $1 : '';
131             #MAINT: die() here if scheme indicates TCP/UDP and port is out of range [0..65535] ?
132 44         190 $host =~ s/\%5B/[/gi;
133 44         126 $host =~ s/\%5D/]/gi;
134 44         701 $_[0] =~ s/\Q$orig\E/$user$host$port/;
135             }
136             }
137              
138              
139             sub _uric_escape
140             {
141 1211     1211   2130 my($class, $str) = @_;
142 1211         3105 $str =~ s*([^$uric\#])* URI::Escape::escape_char($1) *ego;
  255         505  
143 1211         2462 _fix_uric_escape_for_host_part( $str );
144 1211         2662 utf8::downgrade($str);
145 1211         2284 return $str;
146             }
147              
148             my %require_attempted;
149              
150             sub implementor
151             {
152 1202     1202 0 2194 my($scheme, $impclass) = @_;
153 1202 100 66     6022 if (!$scheme || $scheme !~ /\A$scheme_re\z/o) {
154 144         716 require URI::_generic;
155 144         591 return "URI::_generic";
156             }
157              
158 1058         2252 $scheme = lc($scheme);
159              
160 1058 50       1781 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 1058         1843 my $ic = $implements{$scheme};
169 1058 100       3991 return $ic if $ic;
170              
171             # scheme not yet known, look for internal or
172             # preloaded (with 'use') implementation
173 105         216 $ic = "URI::$scheme"; # default location
174              
175             # turn scheme into a valid perl identifier by a simple transformation...
176 105         239 $ic =~ s/\+/_P/g;
177 105         178 $ic =~ s/\./_O/g;
178 105         175 $ic =~ s/\-/_/g;
179              
180 40     40   42421 no strict 'refs';
  40         145  
  40         63516  
181             # check we actually have one for the scheme:
182 105 100       147 unless (@{"${ic}::ISA"}) {
  105         5051  
183 101 50       333 if (not exists $require_attempted{$ic}) {
184             # Try to load it
185 101         181 my $_old_error = $@;
186 101         5264 eval "require $ic";
187 101 50 66     1173 die $@ if $@ && $@ !~ /Can\'t locate.*in \@INC/;
188 101         341 $@ = $_old_error;
189             }
190 101 100       160 return undef unless @{"${ic}::ISA"};
  101         654  
191             }
192              
193 60         669 $ic->_init_implementor($scheme);
194 60         187 $implements{$scheme} = $ic;
195 60         349 $ic;
196             }
197              
198              
199             sub _init_implementor
200             {
201 60     60   270 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 420     420 1 821 my $self = shift;
210 420         699 my $other = $$self;
211 420         990 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 1552     1552   1923 my $self = shift;
221              
222 1552 100       2871 unless (@_) {
223 1219 100       6432 return undef unless $$self =~ /^($scheme_re):/o;
224 903         2865 return $1;
225             }
226              
227 333         415 my $old;
228 333         483 my $new = shift;
229 333 100 100     1217 if (defined($new) && length($new)) {
230 269 50       1306 Carp::croak("Bad scheme '$new'") unless $new =~ /^$scheme_re$/o;
231 269 100       1141 $old = $1 if $$self =~ s/^($scheme_re)://o;
232 269         902 my $newself = URI->new("$new:$$self");
233 269         698 $$self = $$newself;
234 269         599 bless $self, ref($newself);
235             }
236             else {
237 64 50       185 if ($self->_no_scheme_ok) {
238 64 50       541 $old = $1 if $$self =~ s/^($scheme_re)://o;
239 64 50 33     233 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 333         587 return $old;
248             }
249              
250             sub scheme
251             {
252 1084     1084 1 2100 my $scheme = shift->_scheme(@_);
253 1084 100       2558 return undef unless defined $scheme;
254 584         1533 lc($scheme);
255             }
256              
257             sub has_recognized_scheme {
258 5     5 1 37 my $self = shift;
259 5         35 return ref($self) !~ /^URI::_(?:foreign|generic)\z/;
260             }
261              
262             sub opaque
263             {
264 84     84 1 128 my $self = shift;
265              
266 84 100       179 unless (@_) {
267 57 50       706 $$self =~ /^(?:$scheme_re:)?([^\#]*)/o or die;
268 57         336 return $1;
269             }
270              
271 27 50       400 $$self =~ /^($scheme_re:)? # optional scheme
272             ([^\#]*) # opaque
273             (\#.*)? # optional fragment
274             $/sx or die;
275              
276 27         66 my $old_scheme = $1;
277 27         59 my $old_opaque = $2;
278 27         49 my $old_frag = $3;
279              
280 27         39 my $new_opaque = shift;
281 27 100       52 $new_opaque = "" unless defined $new_opaque;
282 27         221 $new_opaque =~ s/([^$uric])/ URI::Escape::escape_char($1)/ego;
  14         38  
283 27         71 utf8::downgrade($new_opaque);
284              
285 27 100       62 $$self = defined($old_scheme) ? $old_scheme : "";
286 27         70 $$self .= $new_opaque;
287 27 100       58 $$self .= $old_frag if defined $old_frag;
288              
289 27         73 $old_opaque;
290             }
291              
292 1     1 1 17 sub path { goto &opaque } # alias
293              
294              
295             sub fragment
296             {
297 71     71 1 126 my $self = shift;
298 71 100       150 unless (@_) {
299 51 100       248 return undef unless $$self =~ /\#(.*)/s;
300 24         140 return $1;
301             }
302              
303 20         28 my $old;
304 20 100       92 $old = $1 if $$self =~ s/\#(.*)//s;
305              
306 20         34 my $new_frag = shift;
307 20 100       44 if (defined $new_frag) {
308 18         159 $new_frag =~ s/([^$uric])/ URI::Escape::escape_char($1) /ego;
  2         6  
309 18         48 utf8::downgrade($new_frag);
310 18         48 $$self .= "#$new_frag";
311             }
312 20         40 $old;
313             }
314              
315              
316             sub as_string
317             {
318 726     726 1 2391 my $self = shift;
319 726         3062 $$self;
320             }
321              
322              
323             sub as_iri
324             {
325 15     15 1 22 my $self = shift;
326 15         25 my $str = $$self;
327 15 100       74 if ($str =~ s/%([89a-fA-F][0-9a-fA-F])/chr(hex($1))/eg) {
  274         636  
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         54 require Encode;
336 10         30 my $enc = Encode::find_encoding("UTF-8");
337 10         307 my $u = "";
338 10         26 while (length $str) {
339 13         67 $u .= $enc->decode($str, Encode::FB_QUIET());
340 13 100       101 if (length $str) {
341             # escape next char
342 3         17 $u .= URI::Escape::escape_char(substr($str, 0, 1, ""));
343             }
344             }
345 10         20 $str = $u;
346             }
347 15         40 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 457     457 1 612 my $self = shift;
357 457   100     951 my $scheme = $self->_scheme || "";
358 457         957 my $uc_scheme = $scheme =~ /[A-Z]/;
359 457         824 my $esc = $$self =~ /%[a-fA-F0-9]{2}/;
360 457 100 100     1967 return $self unless $uc_scheme || $esc;
361              
362 52         147 my $other = $self->clone;
363 52 100       124 if ($uc_scheme) {
364 10         30 $other->_scheme(lc $scheme);
365             }
366 52 100       115 if ($esc) {
367 42         188 $$other =~ s{%([0-9a-fA-F]{2})}
  189         407  
368 189 100       806 { my $a = chr(hex($1));
369             $a =~ /^[$unreserved]\z/o ? $a : "%\U$1"
370             }ge;
371 52         153 }
372             return $other;
373             }
374              
375             # Compare two URIs, subclasses will provide a more correct implementation
376 15     15 1 40 sub eq {
377 15 50       38 my($self, $other) = @_;
378 15 100       45 $self = URI->new($self, $other) unless ref $self;
379 15 50       65 $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 8 # generic-URI transformation methods
385 1     1 1 5 sub abs { $_[0]; }
386             sub rel { $_[0]; }
387 3     3 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__