File Coverage

blib/lib/URI.pm
Criterion Covered Total %
statement 190 198 95.9
branch 80 94 85.1
condition 23 31 74.1
subroutine 34 36 94.4
pod 15 19 78.9
total 342 378 90.4


line stmt bran cond sub pod time code
1             package URI;
2              
3 58     58   5433416 use strict;
  58         119  
  58         2482  
4 58     58   288 use warnings;
  58         116  
  58         5917  
5              
6             our $VERSION = '5.34';
7              
8             # 1=version 5.10 and earlier; 0=version 5.11 and later
9 58 100   58   489 use constant HAS_RESERVED_SQUARE_BRACKETS => $ENV{URI_HAS_RESERVED_SQUARE_BRACKETS} ? 1 : 0;
  58         103  
  58         19865  
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             # smtp, 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 58     58   482 use Carp ();
  58         161  
  58         1336  
41 58     58   28793 use URI::Escape ();
  58         189  
  58         7258  
42              
43 912     912   128183 use overload ('""' => sub { ${$_[0]} },
  912         3502  
44 31     31   127 '==' => sub { _obj_eq(@_) },
45 1     1   495 '!=' => sub { !_obj_eq(@_) },
46 58         6353 fallback => 1,
47 58     58   37925 );
  58         139141  
48              
49             # Check if two objects are the same object
50             sub _obj_eq {
51 33     33   132 return overload::StrVal($_[0]) eq overload::StrVal($_[1]);
52             }
53              
54             sub new
55             {
56 1248     1248 1 21665803 my($class, $uri, $scheme) = @_;
57              
58 1248 50       3663 $uri = defined ($uri) ? "$uri" : ""; # stringify
59             # Get rid of potential wrapping
60 1248         2763 $uri =~ s/^<(?:URL:)?(.*)>$/$1/; #
61 1248         2113 $uri =~ s/^"(.*)"$/$1/;
62 1248         3499 $uri =~ s/^\s+//;
63 1248         2601 $uri =~ s/\s+$//;
64              
65 1248         1791 my $impclass;
66 1248 100       7886 if ($uri =~ m/^($scheme_re):/so) {
67 957         2361 $scheme = $1;
68             }
69             else {
70 291 100 66     2307 if (($impclass = ref($scheme))) {
    100          
71 11         49 $scheme = $scheme->scheme;
72             }
73             elsif ($scheme && $scheme =~ m/^($scheme_re)(?::|$)/o) {
74 136         385 $scheme = $1;
75             }
76             }
77             $impclass ||= implementor($scheme) ||
78 1248   66     5227 do {
      66        
79             require URI::_foreign;
80             $impclass = 'URI::_foreign';
81             };
82              
83 1248         4292 return $impclass->_init($uri, $scheme);
84             }
85              
86              
87             sub new_abs
88             {
89 1     1 1 263 my($class, $uri, $base) = @_;
90 1         4 $uri = $class->new($uri, $base);
91 1         4 $uri->abs($base);
92             }
93              
94              
95             sub _init
96             {
97 1246     1246   2094 my $class = shift;
98 1246         2631 my($str, $scheme) = @_;
99             # find all funny characters and encode the bytes.
100 1246         3654 $str = $class->_uric_escape($str);
101 1246 50 66     7665 $str = "$scheme:$str" unless $str =~ /^$scheme_re:/o ||
102             $class->_no_scheme_ok;
103 1246         2773 my $self = bless \$str, $class;
104 1246         4814 $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 1246     1246   1606 return if HAS_RESERVED_SQUARE_BRACKETS;
116 1240 100       3528 return if $_[0] !~ /%/;
117 77 100       1286 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 73 100       440 if ($_[0] =~ m{^(?:$URI::fallback_schemes_re):}os) {
121 10         26 $_[0] =~ s/\%5B/[/gi;
122 10         20 $_[0] =~ s/\%5D/]/gi;
123 10         13 return;
124             }
125              
126 63 100       1302 if ($_[0] =~ m{^((?:$URI::scheme_re:)?)//([^/?\#]+)(.*)$}os) {
127 48         133 my $orig = $2;
128 48         343 my ($user, $host) = $orig =~ /^(.*@)?([^@]*)$/;
129 48   100     275 $user ||= '';
130 48 100       217 my $port = $host =~ s/(:\d+)$// ? $1 : '';
131             #MAINT: die() here if scheme indicates TCP/UDP and port is out of range [0..65535] ?
132 48         155 $host =~ s/\%5B/[/gi;
133 48         129 $host =~ s/\%5D/]/gi;
134 48         992 $_[0] =~ s/\Q$orig\E/$user$host$port/;
135             }
136             }
137              
138              
139             sub _uric_escape
140             {
141 1246     1246   2530 my($class, $str) = @_;
142 1246         5120 $str =~ s*([^$uric\#])* URI::Escape::escape_char($1) *ego;
  256         470  
143 1246         3362 _fix_uric_escape_for_host_part( $str );
144 1246         3106 utf8::downgrade($str);
145 1246         2779 return $str;
146             }
147              
148             my %require_attempted;
149              
150             sub implementor
151             {
152 1237     1237 0 2451 my($scheme, $impclass) = @_;
153 1237 100 66     8178 if (!$scheme || $scheme !~ /\A$scheme_re\z/o) {
154 144         1204 require URI::_generic;
155 144         931 return "URI::_generic";
156             }
157              
158 1093         2262 $scheme = lc($scheme);
159              
160 1093 50       2368 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 1093         2373 my $ic = $implements{$scheme};
169 1093 100       4849 return $ic if $ic;
170              
171             # scheme not yet known, look for internal or
172             # preloaded (with 'use') implementation
173 122         326 $ic = "URI::$scheme"; # default location
174              
175             # turn scheme into a valid perl identifier by a simple transformation...
176 122         289 $ic =~ s/\+/_P/g;
177 122         260 $ic =~ s/\./_O/g;
178 122         291 $ic =~ s/\-/_/g;
179              
180 58     58   93743 no strict 'refs';
  58         283  
  58         116449  
181             # check we actually have one for the scheme:
182 122 100       194 unless (@{"${ic}::ISA"}) {
  122         1218  
183 117 100       385 if (not exists $require_attempted{$ic}) {
184 81         231 $require_attempted{$ic} = 1;
185              
186             # Try to load it
187 81         205 my $_old_error = $@;
188 81         5837 eval "require $ic";
189 81 50 66     754 die $@ if $@ && $@ !~ /Can\'t locate.*in \@INC/;
190 81         230 $@ = $_old_error;
191             }
192 117 100       220 return undef unless @{"${ic}::ISA"};
  117         775  
193             }
194              
195 77         947 $ic->_init_implementor($scheme);
196 77         318 $implements{$scheme} = $ic;
197 77         549 $ic;
198             }
199              
200              
201             sub _init_implementor
202             {
203 77     77   300 my($class, $scheme) = @_;
204             # Remember that one implementor class may actually
205             # serve to implement several URI schemes.
206             }
207              
208              
209             sub clone
210             {
211 423     423 1 869 my $self = shift;
212 423         812 my $other = $$self;
213 423         1217 bless \$other, ref $self;
214             }
215              
216 0     0 0 0 sub TO_JSON { ${$_[0]} }
  0         0  
217              
218 0     0   0 sub _no_scheme_ok { 0 }
219              
220             sub _scheme
221             {
222 1581     1581   2115 my $self = shift;
223              
224 1581 100       3096 unless (@_) {
225 1246 100       8799 return undef unless $$self =~ /^($scheme_re):/o;
226 930         3153 return $1;
227             }
228              
229 335         486 my $old;
230 335         604 my $new = shift;
231 335 100 100     1359 if (defined($new) && length($new)) {
232 271 50       1668 Carp::croak("Bad scheme '$new'") unless $new =~ /^$scheme_re$/o;
233 271 100       1384 $old = $1 if $$self =~ s/^($scheme_re)://o;
234 271         1110 my $newself = URI->new("$new:$$self");
235 271         1038 $$self = $$newself;
236 271         822 bless $self, ref($newself);
237             }
238             else {
239 64 50       222 if ($self->_no_scheme_ok) {
240 64 50       663 $old = $1 if $$self =~ s/^($scheme_re)://o;
241 64 50 33     261 Carp::carp("Oops, opaque part now look like scheme")
242             if $^W && $$self =~ m/^$scheme_re:/o
243             }
244             else {
245 0 0       0 $old = $1 if $$self =~ m/^($scheme_re):/o;
246             }
247             }
248              
249 335         703 return $old;
250             }
251              
252             sub scheme
253             {
254 1106     1106 1 4562 my $scheme = shift->_scheme(@_);
255 1106 100       3105 return undef unless defined $scheme;
256 606         1858 lc($scheme);
257             }
258              
259             sub has_recognized_scheme {
260 7     7 1 41 my $self = shift;
261 7         53 return ref($self) !~ /^URI::_(?:foreign|generic)\z/;
262             }
263              
264             sub opaque
265             {
266 88     88 1 127 my $self = shift;
267              
268 88 100       211 unless (@_) {
269 61 50       912 $$self =~ /^(?:$scheme_re:)?([^\#]*)/o or die;
270 61         273 return $1;
271             }
272              
273 27 50       463 $$self =~ /^($scheme_re:)? # optional scheme
274             ([^\#]*) # opaque
275             (\#.*)? # optional fragment
276             $/sx or die;
277              
278 27         63 my $old_scheme = $1;
279 27         55 my $old_opaque = $2;
280 27         53 my $old_frag = $3;
281              
282 27         36 my $new_opaque = shift;
283 27 100       54 $new_opaque = "" unless defined $new_opaque;
284 27         258 $new_opaque =~ s/([^$uric])/ URI::Escape::escape_char($1)/ego;
  14         35  
285 27         63 utf8::downgrade($new_opaque);
286              
287 27 100       68 $$self = defined($old_scheme) ? $old_scheme : "";
288 27         55 $$self .= $new_opaque;
289 27 100       49 $$self .= $old_frag if defined $old_frag;
290              
291 27         64 $old_opaque;
292             }
293              
294 2     2 1 7 sub path { goto &opaque } # alias
295              
296              
297             sub fragment
298             {
299 77     77 1 133 my $self = shift;
300 77 100       195 unless (@_) {
301 57 100       335 return undef unless $$self =~ /\#(.*)/s;
302 24         201 return $1;
303             }
304              
305 20         31 my $old;
306 20 100       323 $old = $1 if $$self =~ s/\#(.*)//s;
307              
308 20         37 my $new_frag = shift;
309 20 100       46 if (defined $new_frag) {
310 18         241 $new_frag =~ s/([^$uric])/ URI::Escape::escape_char($1) /ego;
  2         8  
311 18         49 utf8::downgrade($new_frag);
312 18         41 $$self .= "#$new_frag";
313             }
314 20         49 $old;
315             }
316              
317              
318             sub as_string
319             {
320 740     740 1 2238 my $self = shift;
321 740         3022 $$self;
322             }
323              
324              
325             sub as_iri
326             {
327 15     15 1 21 my $self = shift;
328 15         22 my $str = $$self;
329 15 100       82 if ($str =~ s/%([89a-fA-F][0-9a-fA-F])/chr(hex($1))/eg) {
  274         609  
330             # All this crap because the more obvious:
331             #
332             # Encode::decode("UTF-8", $str, sub { sprintf "%%%02X", shift })
333             #
334             # doesn't work before Encode 2.39. Wait for a standard release
335             # to bundle that version.
336              
337 10         74 require Encode;
338 10         42 my $enc = Encode::find_encoding("UTF-8");
339 10         378 my $u = "";
340 10         22 while (length $str) {
341 13         98 $u .= $enc->decode($str, Encode::FB_QUIET());
342 13 100       90 if (length $str) {
343             # escape next char
344 3         14 $u .= URI::Escape::escape_char(substr($str, 0, 1, ""));
345             }
346             }
347 10         15 $str = $u;
348             }
349 15         40 return $str;
350             }
351              
352              
353             sub canonical
354             {
355             # Make sure scheme is lowercased, that we don't escape unreserved chars,
356             # and that we use upcase escape sequences.
357              
358 464     464 1 626 my $self = shift;
359 464   100     932 my $scheme = $self->_scheme || "";
360 464         987 my $uc_scheme = $scheme =~ /[A-Z]/;
361 464         897 my $esc = $$self =~ /%[a-fA-F0-9]{2}/;
362 464 100 100     1958 return $self unless $uc_scheme || $esc;
363              
364 54         209 my $other = $self->clone;
365 54 100       131 if ($uc_scheme) {
366 10         30 $other->_scheme(lc $scheme);
367             }
368 54 100       173 if ($esc) {
369 44         202 $$other =~ s{%([0-9a-fA-F]{2})}
370 263         546 { my $a = chr(hex($1));
371 263 100       1168 $a =~ /^[$unreserved]\z/o ? $a : "%\U$1"
372             }ge;
373             }
374 54         174 return $other;
375             }
376              
377             # Compare two URIs, subclasses will provide a more correct implementation
378             sub eq {
379 17     17 1 43 my($self, $other) = @_;
380 17 50       36 $self = URI->new($self, $other) unless ref $self;
381 17 100       43 $other = URI->new($other, $self) unless ref $other;
382 17 50       77 ref($self) eq ref($other) && # same class
383             $self->canonical->as_string eq $other->canonical->as_string;
384             }
385              
386             # generic-URI transformation methods
387 1     1 1 3 sub abs { $_[0]; }
388 1     1 1 7 sub rel { $_[0]; }
389              
390 7     7 1 37 sub secure { 0 }
391              
392             # help out Storable
393             sub STORABLE_freeze {
394 1     1 0 626 my($self, $cloning) = @_;
395 1         82 return $$self;
396             }
397              
398             sub STORABLE_thaw {
399 1     1 0 28 my($self, $cloning, $str) = @_;
400 1         86 $$self = $str;
401             }
402              
403             1;
404              
405             __END__