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 59     59   4074068 use strict;
  59         93  
  59         1825  
4 59     59   206 use warnings;
  59         127  
  59         4647  
5              
6             our $VERSION = '5.35';
7              
8             # 1=version 5.10 and earlier; 0=version 5.11 and later
9 59 100   59   326 use constant HAS_RESERVED_SQUARE_BRACKETS => $ENV{URI_HAS_RESERVED_SQUARE_BRACKETS} ? 1 : 0;
  59         86  
  59         14915  
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 59     59   355 use Carp ();
  59         204  
  59         1103  
41 59     59   22574 use URI::Escape ();
  59         151  
  59         5701  
42              
43 922     922   87114 use overload ('""' => sub { ${$_[0]} },
  922         2330  
44 61     61   165 '==' => sub { _obj_eq(@_) },
45 1     1   460 '!=' => sub { !_obj_eq(@_) },
46 59         2187 fallback => 1,
47 59     59   31883 );
  59         109976  
48              
49             # Check if two objects are the same object
50             sub _obj_eq {
51 63     63   166 return overload::StrVal($_[0]) eq overload::StrVal($_[1]);
52             }
53              
54             sub new
55             {
56 1260     1260 1 14610405 my($class, $uri, $scheme) = @_;
57              
58 1260 50       2925 $uri = defined ($uri) ? "$uri" : ""; # stringify
59             # Get rid of potential wrapping
60 1260         2195 $uri =~ s/^<(?:URL:)?(.*)>$/$1/; #
61 1260         1740 $uri =~ s/^"(.*)"$/$1/;
62 1260         2642 $uri =~ s/^\s+//;
63 1260         2199 $uri =~ s/\s+$//;
64              
65 1260         1458 my $impclass;
66 1260 100       6634 if ($uri =~ m/^($scheme_re):/so) {
67 969         1788 $scheme = $1;
68             }
69             else {
70 291 100 66     1872 if (($impclass = ref($scheme))) {
    100          
71 11         39 $scheme = $scheme->scheme;
72             }
73             elsif ($scheme && $scheme =~ m/^($scheme_re)(?::|$)/o) {
74 136         325 $scheme = $1;
75             }
76             }
77             $impclass ||= implementor($scheme) ||
78 1260   66     4310 do {
      66        
79             require URI::_foreign;
80             $impclass = 'URI::_foreign';
81             };
82              
83 1260         3231 return $impclass->_init($uri, $scheme);
84             }
85              
86              
87             sub new_abs
88             {
89 1     1 1 240 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 1258     1258   1600 my $class = shift;
98 1258         2087 my($str, $scheme) = @_;
99             # find all funny characters and encode the bytes.
100 1258         2815 $str = $class->_uric_escape($str);
101 1258 50 66     5634 $str = "$scheme:$str" unless $str =~ /^$scheme_re:/o ||
102             $class->_no_scheme_ok;
103 1258         2178 my $self = bless \$str, $class;
104 1258         3873 $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 1258     1258   1236 return if HAS_RESERVED_SQUARE_BRACKETS;
116 1252 100       2637 return if $_[0] !~ /%/;
117 79 100       944 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 75 100       319 if ($_[0] =~ m{^(?:$URI::fallback_schemes_re):}os) {
121 10         35 $_[0] =~ s/\%5B/[/gi;
122 10         16 $_[0] =~ s/\%5D/]/gi;
123 10         10 return;
124             }
125              
126 65 100       944 if ($_[0] =~ m{^((?:$URI::scheme_re:)?)//([^/?\#]+)(.*)$}os) {
127 50         94 my $orig = $2;
128 50         273 my ($user, $host) = $orig =~ /^(.*@)?([^@]*)$/;
129 50   100     251 $user ||= '';
130 50 100       169 my $port = $host =~ s/(:\d+)$// ? $1 : '';
131             #MAINT: die() here if scheme indicates TCP/UDP and port is out of range [0..65535] ?
132 50         119 $host =~ s/\%5B/[/gi;
133 50         85 $host =~ s/\%5D/]/gi;
134 50         738 $_[0] =~ s/\Q$orig\E/$user$host$port/;
135             }
136             }
137              
138              
139             sub _uric_escape
140             {
141 1258     1258   1876 my($class, $str) = @_;
142 1258         3677 $str =~ s*([^$uric\#])* URI::Escape::escape_char($1) *ego;
  260         354  
143 1258         2472 _fix_uric_escape_for_host_part( $str );
144 1258         2470 utf8::downgrade($str);
145 1258         2220 return $str;
146             }
147              
148             my %require_attempted;
149              
150             sub implementor
151             {
152 1249     1249 0 2078 my($scheme, $impclass) = @_;
153 1249 100 66     6338 if (!$scheme || $scheme !~ /\A$scheme_re\z/o) {
154 144         767 require URI::_generic;
155 144         618 return "URI::_generic";
156             }
157              
158 1105         1938 $scheme = lc($scheme);
159              
160 1105 50       2019 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 1105         1937 my $ic = $implements{$scheme};
169 1105 100       3839 return $ic if $ic;
170              
171             # scheme not yet known, look for internal or
172             # preloaded (with 'use') implementation
173 125         219 $ic = "URI::$scheme"; # default location
174              
175             # turn scheme into a valid perl identifier by a simple transformation...
176 125         229 $ic =~ s/\+/_P/g;
177 125         232 $ic =~ s/\./_O/g;
178 125         201 $ic =~ s/\-/_/g;
179              
180 59     59   72874 no strict 'refs';
  59         188  
  59         89919  
181             # check we actually have one for the scheme:
182 125 100       159 unless (@{"${ic}::ISA"}) {
  125         926  
183 120 100       322 if (not exists $require_attempted{$ic}) {
184 84         191 $require_attempted{$ic} = 1;
185              
186             # Try to load it
187 84         146 my $_old_error = $@;
188 84         4903 eval "require $ic";
189 84 50 66     671 die $@ if $@ && $@ !~ /Can\'t locate.*in \@INC/;
190 84         219 $@ = $_old_error;
191             }
192 120 100       157 return undef unless @{"${ic}::ISA"};
  120         547  
193             }
194              
195 80         870 $ic->_init_implementor($scheme);
196 80         213 $implements{$scheme} = $ic;
197 80         500 $ic;
198             }
199              
200              
201             sub _init_implementor
202             {
203 80     80   271 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 442     442 1 845 my $self = shift;
212 442         698 my $other = $$self;
213 442         953 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 1600     1600   1849 my $self = shift;
223              
224 1600 100       2509 unless (@_) {
225 1265 100       6877 return undef unless $$self =~ /^($scheme_re):/o;
226 949         2484 return $1;
227             }
228              
229 335         368 my $old;
230 335         463 my $new = shift;
231 335 100 100     1030 if (defined($new) && length($new)) {
232 271 50       1321 Carp::croak("Bad scheme '$new'") unless $new =~ /^$scheme_re$/o;
233 271 100       1145 $old = $1 if $$self =~ s/^($scheme_re)://o;
234 271         789 my $newself = URI->new("$new:$$self");
235 271         822 $$self = $$newself;
236 271         577 bless $self, ref($newself);
237             }
238             else {
239 64 50       138 if ($self->_no_scheme_ok) {
240 64 50       560 $old = $1 if $$self =~ s/^($scheme_re)://o;
241 64 50 33     233 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         542 return $old;
250             }
251              
252             sub scheme
253             {
254 1106     1106 1 3211 my $scheme = shift->_scheme(@_);
255 1106 100       2163 return undef unless defined $scheme;
256 606         1459 lc($scheme);
257             }
258              
259             sub has_recognized_scheme {
260 7     7 1 25 my $self = shift;
261 7         42 return ref($self) !~ /^URI::_(?:foreign|generic)\z/;
262             }
263              
264             sub opaque
265             {
266 88     88 1 130 my $self = shift;
267              
268 88 100       209 unless (@_) {
269 61 50       746 $$self =~ /^(?:$scheme_re:)?([^\#]*)/o or die;
270 61         383 return $1;
271             }
272              
273 27 50       452 $$self =~ /^($scheme_re:)? # optional scheme
274             ([^\#]*) # opaque
275             (\#.*)? # optional fragment
276             $/sx or die;
277              
278 27         64 my $old_scheme = $1;
279 27         73 my $old_opaque = $2;
280 27         50 my $old_frag = $3;
281              
282 27         40 my $new_opaque = shift;
283 27 100       54 $new_opaque = "" unless defined $new_opaque;
284 27         380 $new_opaque =~ s/([^$uric])/ URI::Escape::escape_char($1)/ego;
  14         41  
285 27         68 utf8::downgrade($new_opaque);
286              
287 27 100       69 $$self = defined($old_scheme) ? $old_scheme : "";
288 27         70 $$self .= $new_opaque;
289 27 100       54 $$self .= $old_frag if defined $old_frag;
290              
291 27         65 $old_opaque;
292             }
293              
294 2     2 1 10 sub path { goto &opaque } # alias
295              
296              
297             sub fragment
298             {
299 77     77 1 120 my $self = shift;
300 77 100       163 unless (@_) {
301 57 100       337 return undef unless $$self =~ /\#(.*)/s;
302 24         111 return $1;
303             }
304              
305 20         24 my $old;
306 20 100       81 $old = $1 if $$self =~ s/\#(.*)//s;
307              
308 20         27 my $new_frag = shift;
309 20 100       39 if (defined $new_frag) {
310 18         181 $new_frag =~ s/([^$uric])/ URI::Escape::escape_char($1) /ego;
  2         4  
311 18         37 utf8::downgrade($new_frag);
312 18         38 $$self .= "#$new_frag";
313             }
314 20         31 $old;
315             }
316              
317              
318             sub as_string
319             {
320 741     741 1 1777 my $self = shift;
321 741         2440 $$self;
322             }
323              
324              
325             sub as_iri
326             {
327 15     15 1 18 my $self = shift;
328 15         20 my $str = $$self;
329 15 100       54 if ($str =~ s/%([89a-fA-F][0-9a-fA-F])/chr(hex($1))/eg) {
  274         415  
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         48 require Encode;
338 10         43 my $enc = Encode::find_encoding("UTF-8");
339 10         268 my $u = "";
340 10         18 while (length $str) {
341 13         101 $u .= $enc->decode($str, Encode::FB_QUIET());
342 13 100       84 if (length $str) {
343             # escape next char
344 3         13 $u .= URI::Escape::escape_char(substr($str, 0, 1, ""));
345             }
346             }
347 10         15 $str = $u;
348             }
349 15         30 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 483     483 1 513 my $self = shift;
359 483   100     777 my $scheme = $self->_scheme || "";
360 483         852 my $uc_scheme = $scheme =~ /[A-Z]/;
361 483         713 my $esc = $$self =~ /%[a-fA-F0-9]{2}/;
362 483 100 100     1773 return $self unless $uc_scheme || $esc;
363              
364 54         121 my $other = $self->clone;
365 54 100       96 if ($uc_scheme) {
366 10         22 $other->_scheme(lc $scheme);
367             }
368 54 100       170 if ($esc) {
369 44         131 $$other =~ s{%([0-9a-fA-F]{2})}
370 263         362 { my $a = chr(hex($1));
371 263 100       760 $a =~ /^[$unreserved]\z/o ? $a : "%\U$1"
372             }ge;
373             }
374 54         133 return $other;
375             }
376              
377             # Compare two URIs, subclasses will provide a more correct implementation
378             sub eq {
379 17     17 1 33 my($self, $other) = @_;
380 17 50       36 $self = URI->new($self, $other) unless ref $self;
381 17 100       42 $other = URI->new($other, $self) unless ref $other;
382 17 50       66 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 4 sub abs { $_[0]; }
388 1     1 1 8 sub rel { $_[0]; }
389              
390 7     7 1 22 sub secure { 0 }
391              
392             # help out Storable
393             sub STORABLE_freeze {
394 1     1 0 499 my($self, $cloning) = @_;
395 1         93 return $$self;
396             }
397              
398             sub STORABLE_thaw {
399 1     1 0 12 my($self, $cloning, $str) = @_;
400 1         64 $$self = $str;
401             }
402              
403             1;
404              
405             __END__