File Coverage

blib/lib/URLprocessor.pm
Criterion Covered Total %
statement 146 150 97.3
branch 73 94 77.6
condition 40 72 55.5
subroutine 29 30 96.6
pod 20 20 100.0
total 308 366 84.1


line stmt bran cond sub pod time code
1             package URLprocessor;
2              
3             # h2xs -XA -n URLprocessor
4              
5 1     1   29571 use 5.010001;
  1         4  
  1         39  
6 1     1   6 use strict;
  1         2  
  1         2886  
7             #use warnings;
8              
9             require Exporter;
10              
11             our @ISA = qw(Exporter);
12              
13             # Items to export into callers namespace by default. Note: do not export
14             # names by default without a very good reason. Use EXPORT_OK instead.
15             # Do not simply export all your public functions/methods/constants.
16              
17             # This allows declaration use URLprocessor ':all';
18             # If you do not need this, moving things directly into @EXPORT or @EXPORT_OK
19             # will save memory.
20             our %EXPORT_TAGS = ( 'all' => [ qw(
21            
22             ) ] );
23              
24             our @EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } );
25              
26             our @EXPORT = qw(
27              
28             );
29              
30             our $VERSION = '0.02';
31              
32              
33             # Let's rock.
34              
35             ##########################################################################################
36             ### Public interface.
37             ##########################################################################################
38              
39             sub new {
40 3     3 1 105 my $class = shift;
41 3         37 my $self = {
42             URL => undef,
43             GLOBAL_PART => undef,
44             LOCAL_PART => undef,
45             #---
46             PROTOCOL => undef,
47             LOGIN => undef,
48             PASSWD => undef,
49             HOST => undef,
50             PORT => undef,
51             LOCAL_PATH => undef,
52             PARAMS => {}, # hash representation of params.
53             FRAGMENT => undef, # after sign #
54             #--
55             PARAMS_VALID => undef,
56             };
57 3         12 bless($self, $class);
58            
59 3 50       21 $self->{URL} = shift if @_;
60              
61             # Parsing (lower case) url.
62 3 50 33     28 if (defined $self->{URL} and $self->{URL} ne '') {
63 3         13 $self->{URL} =~ s/^\s+//g;
64 3         12 $self->{URL} =~ s/\s+$//g;
65 3         13 $self->{URL} = lc $self->{URL};
66 3         13 $self->_parse_url;
67             }
68            
69 3         9 return $self;
70             }
71              
72              
73             sub url {
74 6     6 1 19 my $self = shift;
75 6         20 $self->_prepare_url;
76 6         49 return $self->{URL}; # String of URL.
77             }
78              
79              
80             sub url_global_part {
81 1     1 1 22 my $self = shift;
82 1         6 return $self->{GLOBAL_PART};
83             }
84              
85              
86             sub url_local_part {
87 1     1 1 2 my $self = shift;
88 1         4 return $self->{LOCAL_PART};
89             }
90              
91              
92             sub protocol {
93 5     5 1 8 my $self = shift;
94 5 100       22 return $self->{PROTOCOL} unless @_;
95            
96 2         4 $self->{PROTOCOL} = shift;
97 2 100       8 $self->{PROTOCOL} = lc $self->{PROTOCOL} if defined $self->{PROTOCOL};
98             }
99              
100             sub login {
101 6     6 1 12 my $self = shift;
102 6 100       28 return $self->{LOGIN} unless @_;
103            
104 2         6 $self->{LOGIN} = shift;
105             }
106              
107             sub passwd {
108 6     6 1 11 my $self = shift;
109 6 100       172 return $self->{PASSWD} unless @_;
110            
111 2         7 $self->{PASSWD} = shift;
112             }
113              
114             sub host {
115 5     5 1 9 my $self = shift;
116 5 100       24 return $self->{HOST} unless @_;
117            
118 2         5 $self->{HOST} = shift;
119 2 100       10 $self->{HOST} = lc $self->{HOST} if defined $self->{HOST};
120             }
121              
122             sub port {
123 10     10 1 18 my $self = shift;
124 10 100       41 return $self->{PORT} unless @_;
125            
126 4         10 $self->{PORT} = shift;
127             }
128              
129             sub localpath {
130 16     16 1 37 my $self = shift;
131 16 100       69 return $self->{LOCAL_PATH} unless @_;
132            
133 5         6 my $path = shift;
134             # Only SCALAR, ARRAY and undef are allowed.
135 5 100 66     36 if (ref $path eq '' or ref $path eq 'SCALAR') {
    100          
136 2         5 $self->{LOCAL_PATH} = $path;
137             }
138             elsif (ref $path eq 'ARRAY') {
139 2         3 $self->{LOCAL_PATH} = join '/', @{$path};
  2         7  
140             }
141            
142 5 100       15 return unless defined $self->{LOCAL_PATH};
143 4         13 $self->{LOCAL_PATH} = '/'.$self->{LOCAL_PATH};
144 4         22 $self->{LOCAL_PATH} =~ s/^\/+/\//;
145             # $self->{LOCAL_PATH} = lc $self->{LOCAL_PATH};
146              
147             }
148              
149             sub localpath_array {
150 3     3 1 5 my $self = shift;
151 3         8 my $path = $self->localpath;
152 3         12 $path =~ s/^\///;
153            
154 3         19 return split '/', $path;
155             }
156              
157             sub fragment {
158 6     6 1 17 my $self = shift;
159 6 100       36 return $self->{FRAGMENT} unless @_;
160            
161 2         7 $self->{FRAGMENT} = shift;
162             }
163              
164              
165              
166             ##########################################################################################
167             ### Params methods.
168             ##########################################################################################
169              
170             sub params_hash {
171 1     1 1 11 my $self = shift;
172 1 50       7 return $self->{PARAMS} unless @_;
173            
174 0         0 $self->{PARAMS} = shift;
175             }
176              
177              
178             sub params_string {
179 10     10 1 18 my $self = shift;
180 10 100       27 my $userDelimiter = (@_) ? shift : '&'; # & - default value
181            
182 10         13 my $params = '';
183 10         10 my $delimiter = '';
184 10         14 while(my($k, $v) = each %{$self->{PARAMS}}) {
  26         98  
185 16         33 $params .= "$delimiter$k=$v";
186 16         30 $delimiter = $userDelimiter;
187             }
188 10         36 return $params;
189             }
190              
191              
192             sub param_value {
193 5     5 1 730 my $self = shift;
194 5         8 my $param = shift;
195            
196 5 100 100     37 return $self->{PARAMS}->{$param} if defined $param and exists $self->{PARAMS}->{$param};
197 3         13 return undef;
198             }
199              
200              
201             # Pay attention with arguments order!
202             # This function may generate hidden errors.
203             sub param_add {
204 3     3 1 36 my $self = shift;
205 3         5 my $param = shift;
206 3         4 my $val = shift;
207            
208 3 50 33     25 $self->{PARAMS}->{$param} = $val if defined $param and defined $val;
209             }
210              
211             sub param_del {
212 2     2 1 5 my $self = shift;
213 2         3 my $param = shift;
214            
215 2 50       11 delete $self->{PARAMS}->{$param} if defined $param;
216             }
217              
218             # Check parameter existence.
219             sub param_exist {
220 3     3 1 6 my $self = shift;
221 3         4 my $param = shift;
222              
223 3 100 66     25 return 1 if defined $param and exists $self->{PARAMS}->{$param};
224 2         8 return 0;
225             }
226              
227             # There is no way to set string of params from outside. It is bad idea, but it isn't rule.
228             # In the future it will may be implemented it.
229              
230              
231             ##########################################################################################
232             ### Parsing "private" methods.
233             ##########################################################################################
234              
235             sub _prepare_url {
236 6     6   8 my $self = shift;
237 6         15 $self->{URL} = '';
238 6 50       27 $self->{URL} = "$self->{PROTOCOL}://" if defined $self->{PROTOCOL};
239 6 100 66     50 $self->{URL} .= "$self->{LOGIN}:$self->{PASSWD}@" if defined $self->{LOGIN} and defined $self->{PASSWD};
240 6 50       22 $self->{URL} .= $self->{HOST} if defined $self->{HOST};
241 6 100       25 $self->{URL} .= ":$self->{PORT}" if defined $self->{PORT};
242 6 50       17 if (defined $self->{LOCAL_PATH}) {
243 6 50       36 $self->{URL} .= '/' if $self->{LOCAL_PATH} !~ /^\//;
244 6         32 $self->{URL} .= $self->{LOCAL_PATH}; # Sign '/' is not required on the end because the localpath may contains a file.
245 6         18 my $params_str = $self->params_string;
246 6 100       24 $self->{URL} .= "?$params_str" if $params_str ne '';
247 6 50 33     47 $self->{URL} .= "#$self->{FRAGMENT}" if defined $self->{FRAGMENT} and $self->{FRAGMENT} ne '';
248             }
249             }
250              
251              
252             sub _split_url {
253 12     12   2943 my $self = shift;
254             # global_part local_part
255 12         97 return $self->{URL} =~ m|^(\w*?://[^/?&#]+)(?:(.+))?|;
256             }
257              
258              
259             sub _parse_global_part {
260 17     17   4987 my $self = shift;
261             # protocol auth host port
262 17         140 return $self->{GLOBAL_PART} =~ m|^(\w*)://(?:([^@/]+)@)?([^:@/?#]*)(?:\:(.+))?|;
263             # If host has a + instead of * then it isn't working correctly.
264             }
265              
266              
267             sub _parse_local_part {
268 18     18   6651 my $self = shift;
269             # localpath params fragment
270 18         116 return $self->{LOCAL_PART} =~ m|^(/[^?&#]*)(?:\?([^#]*))?(?:#(.+))?|;
271             }
272              
273              
274             # This is normal function, not method. It isn't working on object attributes.
275             # Return PARAMS_VALID, \%PARAMS
276             # PARAMS_VALID == 0 - error
277             # PARAMS_VALID == 1 - ok
278             sub _parse_params {
279 17     17   3786 my $params = shift;
280 17         19 my $separator = shift;
281              
282             # Params is empty.
283 17 100 100     98 if (!defined $params or $params eq '') {
284 3         12 return (1, undef);
285             }
286              
287 14         18 my $params_ref = {};
288 14         15 my $valid_status = 0;
289             # Split by pairs param=val
290 14         66 foreach (split $separator, $params) {
291 19 100       39 return (0, undef) if $_ eq '';
292            
293 16         34 my @param_val = split '=', $_;
294 16 100 66     89 if(scalar(@param_val) == 2 and $param_val[0] ne '' and $param_val[1] ne '') {
      66        
295 11         25 $params_ref->{$param_val[0]} = $param_val[1];
296 11         23 $valid_status = 1;
297             } else {
298 5         20 return (0, undef);
299             }
300             }
301            
302 6         17 return ($valid_status, $params_ref);
303             }
304              
305              
306             # Parse URL string and save those parts.
307             # This method sets attributes.
308             sub _parse_url {
309 3     3   6 my $self = shift;
310            
311             # Split URL to global part and optionally localpath.
312 3         14 ($self->{GLOBAL_PART}, $self->{LOCAL_PART}) = $self->_split_url;
313            
314             # Parse global part.
315 3 50       13 return unless defined $self->{GLOBAL_PART};
316 3         4 my $auth;
317 3         10 ($self->{PROTOCOL}, $auth, $self->{HOST}, $self->{PORT}) = $self->_parse_global_part;
318              
319             # In global part: parse login and passwd.
320 3 100 66     44 ($self->{LOGIN}, $self->{PASSWD}) = split(':', $auth) if (defined $auth and $auth ne '');
321              
322             # Parse local part.
323 3 50       9 return unless defined $self->{LOCAL_PART};
324 3         3 my $params;
325 3         12 ($self->{LOCAL_PATH}, $params, $self->{FRAGMENT}) = $self->_parse_local_part;
326              
327             # Parse params.
328 3         12 ($self->{PARAMS_VALID}, $self->{PARAMS}) = _parse_params($params, '&'); # & - separator.
329              
330             }
331              
332              
333              
334              
335             ##########################################################################################
336             ### Validating methods.
337             ##########################################################################################
338              
339             sub _is_valid {
340 11     11   2306 my $self = shift;
341 11         17 my $msg = ''; # Message container
342            
343 11 50       36 $msg .= "Global part is undef\n" if !defined $self->{GLOBAL_PART};
344            
345 11 100 66     69 $msg .= "Protocol is empty\n" if !defined $self->{PROTOCOL} or $self->{PROTOCOL} eq '';
346 11 50 66     50 $msg .= "Login is undef\n" if !defined $self->{LOGIN} and defined $self->{PASSWD};
347 11 50 66     51 $msg .= "Passwd is undef\n" if defined $self->{LOGIN} and !defined $self->{PASSWD};
348 11 100 66     44 if (defined $self->{LOGIN} and defined $self->{PASSWD}){
349 7 50 33     24 $msg .= "Login is empty\n" if $self->{LOGIN} eq '' and $self->{PASSWD} ne '';
350 7 50 33     38 $msg .= "Passwd is empty\n" if $self->{LOGIN} ne '' and $self->{PASSWD} eq '';
351             }
352              
353 11 100 66     54 $msg .= "Host is empty\n" if !defined $self->{HOST} or $self->{HOST} eq '';
354 11 100 100     75 $msg .= "Port must be numeric value\n" if defined $self->{PORT} and $self->{PORT} !~ /^\d+$/;
355 11 50 33     84 if (defined $self->{LOCAL_PART} and length $self->{LOCAL_PART} > 0 and !defined $self->{LOCAL_PATH}) {
      33        
356             # Chcek only defined localpath because params and fragmet are functional dependend by localpath.
357 0         0 $msg .= "Local part is set but localpath, parameters and fragment are undef\n";
358             }
359 11 0 0     29 if (!defined $self->{LOCAL_PATH} and (scalar keys %{$self->{PARAMS}} or defined $self->{FRAGMENT})) {
      33        
360 0         0 $msg .= "Localpath cannot be undef when parameters or fragment is set\n";
361             }
362            
363 11 50 33     56 $msg .= "Badly parameters\n" if defined $self->{PARAMS_VALID} and $self->{PARAMS_VALID} == 0;
364              
365 11 100       56 return (1, "OK") if $msg eq '';
366 5         21 $msg =~ s/\n$//;
367 5         26 return (0, $msg); # 0 - invalid URL.
368             }
369              
370              
371             sub valid_status {
372 8     8 1 29 my $self = shift;
373              
374 8         23 return ($self->_is_valid)[0];
375             }
376              
377              
378             sub valid_msg {
379 3     3 1 12 my $self = shift;
380              
381 3         11 return ($self->_is_valid)[1];
382             }
383              
384              
385              
386              
387             ##########################################################################################
388             ### Additional and useful "private" functions.
389             ##########################################################################################
390              
391             sub _here_im {
392 0     0     return (caller(1))[3];
393             }
394              
395              
396             1;
397             __END__