File Coverage

lib/CGI/ValidOp.pm
Criterion Covered Total %
statement 201 215 93.4
branch 116 124 93.5
condition 33 38 86.8
subroutine 32 33 96.9
pod 11 24 45.8
total 393 434 90.5


line stmt bran cond sub pod time code
1             package CGI::ValidOp;
2 9     9   18885 use strict;
  9         17  
  9         484  
3 9     9   48 use warnings;
  9         19  
  9         433  
4              
5             our $VERSION = '0.56';
6              
7 9     9   49 use base qw/ CGI::ValidOp::Base /;
  9         16  
  9         4544  
8 9     9   4501 use CGI::ValidOp::Op;
  9         27  
  9         371  
9 9     9   54 use CGI::ValidOp::Param;
  9         16  
  9         155  
10 9     9   5686 use CGI::ValidOp::Object;
  9         23  
  9         225  
11 9     9   24901 use CGI;
  9         160995  
  9         61  
12 9     9   567 use Carp qw/ croak confess /;
  9         21  
  9         582  
13 9     9   51 use Data::Dumper;
  9         20  
  9         24904  
14              
15             # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
16             sub PROPERTIES {
17             {
18 29     29 0 225 ops => undef,
19             print_warnings => 1,
20             default_op => 'default',
21             runmode_name => 'op',
22             disable_uploads => 1,
23             post_max => 25_000,
24             -cgi_object => new CGI,
25             -error_decoration => undef,
26             -allow_unexpected => 1,
27             -on_error_return_undef => 0,
28             -on_error_return_encoded => 0,
29             -on_error_return_tainted => 0,
30             -return_only_received => 0,
31             }
32             }
33              
34             # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
35             # the argument parsing means:
36             # 1) if an argument is prefixed with a '-', take it as a config option
37             # 2) else take it as an op
38             sub init {
39 29     29 0 63 my $self = shift;
40 29         66 my( $args ) = @_;
41              
42 29         48 my( %ops, %config );
43 29 100       110 if( ref $args eq 'HASH' ) {
44 20         83 for( keys %$args ) {
45 45 100       309 $_ =~ /^-(.*)$/
46             ? $config{ $1 } = $args->{ $_ }
47             : $ops{ $_ } = $args->{ $_ };
48             }
49 20 100       394 $config{ ops } = \%ops if keys %ops;
50 20         122 $self->SUPER::init( \%config );
51             }
52             else {
53 9         48 $self->SUPER::init;
54             }
55              
56             # order of precedence for on_error arguments -- only one of the three
57             # shold be active at once
58 29 100 100     94 $self->on_error_return_undef( 1 )
59             unless $self->on_error_return_encoded
60             or $self->on_error_return_tainted;
61 29 100 100     72 $self->on_error_return_tainted( 0 )
62             if $self->on_error_return_undef
63             or $self->on_error_return_encoded;
64 29 100       68 $self->on_error_return_encoded( 0 )
65             if $self->on_error_return_undef;
66              
67 29         98 $self->get_cgi_vars;
68 29         159 $self;
69             }
70              
71             # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
72             sub cgi_object {
73 62     62 0 175 my $self = shift;
74 62         82 my( $value ) = @_;
75              
76             return $self->{ cgi_object }
77 62 100       810 unless defined $value;
78              
79 31         90 $self->{cgi_object} = $value;
80             }
81              
82              
83             # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
84             sub reset_on_error {
85 42     42 0 58 my $self = shift;
86              
87             # we want object construction not to account for precedence
88 42 100       126 return if $self->{ in_init };
89 24         133 $self->{ $_ } = 0 for qw/
90             on_error_return_undef
91             on_error_return_encoded
92             on_error_return_tainted
93             /;
94 24         53 return;
95             }
96              
97             # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
98             sub on_error_return_undef {
99 132     132 1 351 my $self = shift;
100 132         168 my( $value ) = @_;
101              
102             return $self->{ on_error_return_undef }
103 132 100       521 unless defined $value;
104 57 100       195 $self->reset_on_error if $value;
105 57 100       145 $self->{ on_error_return_undef } = $value ? 1 : 0;
106 57         123 return $self->{ on_error_return_undef };
107             }
108              
109             # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
110             sub on_error_return_encoded {
111 177     177 1 283 my $self = shift;
112 177         213 my( $value ) = @_;
113              
114             return $self->{ on_error_return_encoded }
115 177 100       745 unless defined $value;
116 61 100       144 $self->reset_on_error if $value;
117 61 100       157 $self->{ on_error_return_encoded } = $value ? 1 : 0;
118 61         128 return $self->{ on_error_return_encoded };
119             }
120              
121             # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
122             sub on_error_return_tainted {
123 149     149 1 234 my $self = shift;
124 149         193 my( $value ) = @_;
125              
126             return $self->{ on_error_return_tainted }
127 149 100       856 unless defined $value;
128 65 100       164 $self->reset_on_error if $value;
129 65 100       170 $self->{ on_error_return_tainted } = $value ? 1 : 0;
130 65         202 return $self->{ on_error_return_tainted };
131             }
132              
133             # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
134             # FIXME if you add a param and then change allow_unexpected, that param will go away
135             sub allow_unexpected {
136 101     101 1 1840 my $self = shift;
137              
138 101 100       372 return $self->{ allow_unexpected } unless @_;
139 41         95 $self->{ allow_unexpected } = shift;
140 41         144 $self->set_vars; # FIXME this is a hack; related to the above FIXME
141             }
142              
143             # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
144             sub return_only_received {
145 160     160 1 1091 my $self = shift;
146              
147 160 100       845 return $self->{ return_only_received } unless @_;
148 31         105 $self->{ return_only_received } = shift;
149 31         77 $self->{ return_only_received };
150             }
151              
152             # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
153             sub get_cgi_vars {
154 29     29 0 43 my $self = shift;
155              
156 29         106 $CGI::POST_MAX = $self->post_max;
157 29         96 $CGI::DISABLE_UPLOADS = $self->disable_uploads;
158 29         81 $self->set_vars({ $self->cgi_object->Vars });
159             # next two lines may be necessary for file uploads, but break existing
160             # multi-value param functionality
161             # my $cgi = CGI->new;
162             # $self->set_vars({ map { $_ => $cgi->param( $_ )} $cgi->param });
163 29         169 return; # so we can't get untainted user input
164             }
165              
166             # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
167             # an empty hashref {} resets vars
168             # TODO should accept arrayrefs as values
169             sub set_vars {
170 89     89 1 19861 my $self = shift;
171 89         132 my( $vars ) = @_;
172              
173 89 100       325 return if $self->{ in_init }; # if we're still being initialized
174 58 100       218 if( ref $vars eq 'HASH' ) {
175 43 100       144 if( keys %$vars == 0 ) {
176 23         42 delete $self->{ _vars };
177             }
178             else {
179 20         55 $self->{ _vars } = $vars;
180             }
181             }
182 58         330 $self->make_op;
183 58         184 $self->make_params;
184 58         183 return; # so we can't get untainted user input
185             }
186              
187             # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
188             # make the current Op object and add the defined params
189             sub make_op {
190 59     59 0 94 my $self = shift;
191              
192 59         741 delete $self->{ Op };
193 59         289 my $options = $self->ops;
194 59 100       216 return unless my $params = $options->{ $self->op };
195 14         397 for( keys %$params ) {
196 37 100       204 next if /^-.*/;
197 32         101 $self->add_param( $_, $params->{ $_ });
198             }
199 14         35 return;
200             }
201              
202             # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
203             # makes parameters using incoming vars
204             sub make_params {
205 58     58 0 92 my $self = shift;
206              
207 58         95 my $vars = $self->{ _vars };
208             # create params if we need to and are allowed
209 58 100       171 if( $self->allow_unexpected ) {
210 48         168 for( keys %$vars ) {
211 173 100       514 next if $_ eq $self->runmode_name; # don't make one for runmode
212 153 100 100     909 if (/\[/ || /^object--/) {
213 14         35 $self->append_to_object($_);
214             }
215             # Make it available even if it is added to an object
216 153 100       342 $self->add_param( $_ ) unless $self->Op->Param( $_ );
217             }
218             }
219             # set all tainted values
220 58         207 $_->tainted( $vars->{ $_->name }) for $self->Op->Param;
221 58         139 return;
222             }
223              
224             # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
225             # appends a parameter to an object - takes a parameter name as an argument.
226             sub append_to_object {
227 14     14 0 17 my $self = shift;
228 14         19 my ($param_name) = @_;
229              
230 14   50     38 $self->{_objects} ||= { };
231              
232 14 100       53 $param_name =~ /^object--(\w+)--/ || $param_name =~ /^([^\[]+)/;
233 14         36 my $name = $1;
234              
235 14 50       39 return unless ($self->{_objects}{$name});
236              
237 14         85 $self->{_objects}{$name}->set_var({ name => $param_name, value => $self->{_vars}{$param_name} });
238 14         34 return $name;
239             }
240              
241             # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
242             # accepts:
243             # ( $name )
244             # ( \%options )
245             # ( $name, \%options )
246             # ( $name, [ $label, @checks ])
247             sub add_param {
248 196     196 0 641 my $self = shift;
249              
250 196         223 my $param;
251 196 100       482 if( @_ == 1 ) { # either a hashref or a single name
252 149         472 $param = $self->Op->add_param( @_ );
253             }
254             else { # either a name and hashref or a name and arrayref
255 47         108 my( $name, $vars ) = @_;
256 47         60 my( $label, $checks );
257 47 100       139 if ( ref $vars eq 'ARRAY' ) {
    100          
258 43         73 $label = $vars->[0];
259             # slice and take a reference to that, copying 1..-1
260 43         109 $checks = [@{$vars}[1..$#$vars]];
  43         124  
261              
262 43         104 $param = $self->Op->add_param({
263             name => $name,
264             label => $label,
265             checks => $checks,
266             });
267             }
268             elsif( ref $vars eq 'HASH' ) {
269 3   50     27 $self->{_objects} ||= { };
270 3         30 $param = $self->{_objects}{$name} = CGI::ValidOp::Object->new($name, $vars);
271             }
272             else {
273 1         36 croak qr/Incorrect param definition./;
274             }
275             }
276              
277 195 100       942 if ($param->isa('CGI::ValidOp::Param')) {
278             $param->tainted( $self->{ _vars }{ $param->name })
279 192 100       939 if defined $self->{ _vars };
280             }
281              
282 195         573 $param;
283             }
284              
285             # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
286             # capitalized for CGI compatibility
287             sub Vars {
288 18     18 1 531 my $self = shift;
289 18         36 my %params;
290 18         143 my @vars = keys %{ $self->{ _vars }}
291 18 50       81 if $self->{ _vars };
292 18         78 for( $self->Op->Param ) {
293 128         432 my $name = $_->name;
294             next
295 128 100 100     360 if $self->return_only_received and not grep /^$name$/ => @vars;
296 127         344 $params{ $name } = $_->value;
297             }
298 18 100       142 return unless keys %params;
299             wantarray
300 16 100       369 ? %params
301             : \%params;
302             }
303              
304             # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
305             # fetches an object collection
306             sub objects {
307 6     6 0 1822 my $self = shift;
308 6         18 my ($object_name) = @_;
309              
310 6   50     30 $self->{_objects} ||= { };
311              
312 6 50       24 if (defined($object_name)) {
313 6 50       58 return $self->{_objects}{$object_name} ? $self->{_objects}{$object_name}->objects : [];
314             }
315              
316 0         0 my $hash = { };
317 0         0 foreach my $key (keys %{$self->{_objects}}) {
  0         0  
318 0   0     0 $hash->{$key} = $self->{_objects}{$key}->objects || [];
319             }
320              
321 0         0 return $hash;
322             }
323              
324             # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
325             # fetches all the errors for object collections
326             sub object_errors {
327 0     0 0 0 my $self = shift;
328 0         0 my ($object_name) = @_;
329              
330 0 0       0 if (defined($object_name)) {
331             # return the errors just for the requested object
332 0 0       0 return $self->{_objects}{$object_name} ? $self->{_objects}{$object_name}->object_errors : {};
333             }
334              
335 0         0 my $hash = { };
336             # return all the object errors in a hash keyed by the object name
337 0         0 foreach my $key (keys %{$self->{_objects}}) {
  0         0  
338 0         0 $hash->{$key} = $self->{_objects}{$key}->object_errors;
339             }
340              
341 0         0 return $hash;
342             }
343              
344             # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
345             sub op {
346 76     76 1 2152 my $self = shift;
347 76         468 $self->Op( @_ )->name;
348             }
349              
350             # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
351             # given a scalar, returns the op for which that scalar is an alias
352             # accounts both for alias being a string and an arrayref
353             # alias is case-sensitive
354             sub op_alias {
355 45     45 0 107 my $self = shift;
356 45         75 my( $alias ) = @_;
357              
358 45 100 100     205 return unless $alias and $self->ops;
359 32         56 for( keys %{ $self->ops }) {
  32         167  
360 64 100       162 next unless $self->ops->{ $_ }{ -alias };
361 45 100       108 return $_ if $self->ops->{ $_ }{ -alias } eq $alias;
362             return $_ if ref $self->ops->{ $_ }{ -alias } eq 'ARRAY'
363 44 100 100     103 and grep /^$alias$/, @{ $self->ops->{ $_ }{ -alias }};
  25         60  
364             }
365 19         64 return;
366             }
367              
368             # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
369             sub get_op_name {
370 67     67 0 93 my $self = shift;
371              
372 67         80 my $op_name;
373 67 100 100     517 if( $self->{ _vars } and $self->{ _vars }{ $self->runmode_name }) {
374 35         132 $op_name = $self->{ _vars }{ $self->runmode_name };
375 35         151 ( $op_name ) = split /\0/, $op_name; # if we get more than one, use the first
376 35 100       117 $op_name = $self->op_alias( $op_name )
377             if $self->op_alias( $op_name );
378 23         61 $op_name = $self->default_op
379             unless $self->ops
380 35 100 100     119 and grep /^$op_name$/i => keys %{ $self->ops };
381             }
382             else {
383 32         764 $op_name = $self->default_op;
384             }
385 67         240 lc $op_name;
386             }
387              
388             # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
389             # FIXME if you add a param and then change op, that param will go away
390             sub Op {
391 680     680 1 987 my $self = shift;
392 680         1034 my( $op_name ) = @_;
393              
394 680 100 100     2187 croak 'Invalid op name; only a word is allowed.'
395             if $op_name and $op_name !~ /^\w+$/;
396 677 100       1490 unless( $op_name ) {
397 675 100       7199 return $self->{ Op } if $self->{ Op };
398 59         151 $op_name = $self->get_op_name;
399             }
400              
401             # print STDERR Dumper[
402             # $self->{ on_error_return_undef },
403             # $self->{ on_error_return_encoded },
404             # $self->{ on_error_return_tainted },
405             # ];
406              
407 61 100       266 my $on_error_return = $self->on_error_return_encoded ? 'encoded'
    100          
408             : $self->on_error_return_tainted ? 'tainted'
409             : 'undef';
410 61         372 $self->{ Op } = CGI::ValidOp::Op->new({
411             name => $op_name,
412             error_decoration => [ $self->error_decoration ],
413             on_error_return => $on_error_return,
414             });
415 61         300 $self->{ Op };
416             }
417              
418             # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
419             sub param {
420 111     111 1 1097 my $self = shift;
421 111         674 my( $param_name, $checks ) = @_;
422              
423             # return all param names if we're not asked for one
424 111 100       469 unless( $param_name ) {
425 2         9 my @params = map $_->name, $self->Op->Param;
426 2 100       17 return @params if @params;
427 1         6 return;
428             }
429 109         276 my $param = $self->Op->Param( $param_name, $checks );
430 109 100 100     470 if( !$param and $checks ) { # if we have checks create the param
431 8         40 $param = $self->add_param($param_name, [ $param_name, @$checks ]);
432             }
433 109 100       685 return $param->value if $param;
434 20         109 return;
435             }
436              
437             # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
438             sub errors {
439 27     27 1 63 my $self = shift;
440              
441 27 100       110 return unless $self->Op->Param;
442 25         73 my @errors;
443 25         73 for( $self->Op->Param ) {
444 362         1016 $_->validate; # slightly nasty to have to do this
445 362 100       1043 next unless my $errors = $_->errors;
446 50         191 push @errors => @$errors;
447             }
448 25         176 @errors = sort @errors;
449 25 100       366 return \@errors if @errors;
450 2         15 return;
451             }
452              
453             1;
454              
455             __END__