File Coverage

lib/HTTP/Validate.pm
Criterion Covered Total %
statement 680 700 97.1
branch 423 542 78.0
condition 232 345 67.2
subroutine 66 66 100.0
pod 18 41 43.9
total 1419 1694 83.7


line stmt bran cond sub pod time code
1             package HTTP::Validate;
2              
3 8     8   683650 use strict;
  8         10  
  8         254  
4 8     8   24 use warnings;
  8         10  
  8         344  
5              
6 8     8   30 use Exporter qw( import );
  8         11  
  8         226  
7 8     8   27 use Carp qw( carp croak );
  8         15  
  8         385  
8 8     8   45 use Scalar::Util qw( reftype weaken looks_like_number );
  8         39  
  8         2287  
9              
10             # Check for the existence of the 'fc' function. If it exists, we can use it
11             # for casefolding enum values. Otherwise, we default to 'lc'.
12              
13             my $case_fold = $] >= 5.016 ? eval 'sub { return CORE::fc $_[0] }'
14             : $INC{'Unicode/CaseFold.pm'} ? eval 'sub { return Unicode::CaseFold::fc $_[0] }'
15             : eval 'sub { return lc $_[0] }';
16              
17             our $VERSION = '1.0';
18              
19             =head1 NAME
20              
21             HTTP::Validate - validate and clean HTTP parameter values according to a set of rules
22              
23             Version 1.0
24              
25             =head1 DESCRIPTION
26              
27             This module provides validation of a set of parameter values against a set of clearly
28             defined rules. It is designed to work with L, L, L,
29             L, and similar web application frameworks, both for interactive apps
30             and for data services. It can also be used with L, although the use of L
31             or another similar solution is recommended to avoid paying the penalty of loading this
32             module and initializing all of the rulesets over again for each request. Both an
33             object-oriented interface and a procedural interface are provided.
34              
35             There are several use cases for this module. It was originally designed for
36             validating HTTP request parameters, but it can also be used to validate other
37             sets of parameters and values, such as the body of an HTTP POST request.
38              
39             The rule definition mechanism is very flexible. A ruleset can be defined once
40             and used with multiple URL paths, and rulesets can be combined using the rule
41             types C and C. This allows a complex application that accepts
42             many different paths to apply common rule patterns. If the parameters fail
43             the validation test, an error message is provided which tells the client how
44             to amend the request in order to make it valid. A suite of built-in validator
45             functions is available, and you can also define your own.
46              
47             This module also provides a mechanism for generating documentation about the
48             parameter rules. The documentation is generated in Pod format, which can
49             then be converted to HTML, TeX, nroff, etc. as needed.
50              
51             =head1 SYNOPSIS
52              
53             package MyWebApp;
54            
55             use HTTP::Validate qw{:keywords :validators};
56            
57             define_ruleset( 'filters' =>
58             { param => 'lat', valid => DECI_VALUE('-90.0','90.0') },
59             "Return all datasets associated with the given latitude.",
60             { param => 'lng', valid => DECI_VALUE('-180.0','180.0') },
61             "Return all datasets associated with the given longitude.",
62             { together => ['lat', 'lng'], errmsg => "you must specify 'lng' and 'lat' together" },
63             "If either 'lat' or 'lng' is given, the other must be as well.",
64             { param => 'id', valid => POS_VALUE },
65             "Return the dataset with the given identifier",
66             { param => 'name', valid => ANY_VALUE },
67             "Return all datasets that match the given name");
68            
69             define_ruleset( 'display' =>
70             { optional => 'full', valid => FLAG_VALUE },
71             "If specified, then the full dataset descriptions are returned. No value is necessary",
72             { optional => 'short', valid => FLAG_VALUE },
73             "If specified, then a brief summary of the datasets is returned. No value is necessary",
74             { at_most_one => ['full', 'short'] },
75             { optional => 'limit', valid => [POS_ZERO_VALUE, ENUM_VALUE('all')], default => 'all',
76             errmsg => "acceptable values for 'limit' are either 'all', 0, or a positive integer" },
77             "Limits the number of results returned. Acceptable values are 'all', 0, or a positive integer.");
78            
79             define_ruleset( 'dataset_query' =>
80             "This URL queries for stored datasets. The following parameters select the datasets",
81             "to be displayed, and you must specify at least one of them:",
82             { require => 'filters',
83             errmsg => "you must specify at least one of the following: 'lat' and 'lng', 'id', 'name'" },
84             "The following optional parameters control how the data is returned:",
85             { allow => 'display' });
86            
87             # Validate the parameters found in %ARGS against the ruleset 'dataset_query'. This
88             # is just one example, and in general the parameters may be found in various places
89             # depending upon which module (CGI, Dancer, Mojolicious, etc.) you are using to
90             # accept and process HTTP requests.
91            
92             my $result = check_params('dataset_query', \%ARGS);
93            
94             if ( my @error_list = $result->errors )
95             {
96             # if an error message was generated, do whatever is necessary to abort the
97             # request and report the error back to the end user
98             }
99            
100             else
101             {
102             my %param_value = $result->values;
103              
104             # do whatever is necessary to process the request
105             }
106            
107             =head1 THE VALIDATION PROCESS
108              
109             The validation process starts with the definition of one or more sets of rules.
110             This is done via the L keyword. For example:
111              
112             define_ruleset 'some_params' =>
113             { param => 'id', valid => POS_VALUE },
114             { param => 'short', valid => FLAG_VALUE },
115             { param => 'full', valid => FLAG_VALUE },
116             { at_most_one => ['short', 'full'],
117             errmsg => "the parameters 'short' and 'full' cannot be used together" };
118              
119             This statement defines a ruleset named 'some_params' that enforces the following
120             rules:
121              
122             =over 4
123              
124             =item *
125              
126             The value of parameter 'id' must be a positive integer.
127              
128             =item *
129              
130             The parameter 'short' is considered to have a true value if it appears in a
131             request, and false otherwise. The value, if any, is ignored.
132              
133             =item *
134              
135             The parameter 'full' is treated likewise.
136              
137             =item *
138              
139             The parameters 'short' and 'full' must not be specified together in the same
140             request.
141              
142             =back
143              
144             You can define as many rulesets as you wish. For each URL path recognized by your code,
145             you can use the L function to validate the request parameters against the
146             appropriate ruleset for that path. If the given parameter values are not valid, one or
147             more error messages will be returned. These messages should be sent back to the HTTP
148             client, in order to instruct the user or programmer who originally generated the request
149             how to amend the parameters so that the request will succeed.
150              
151             During the validation process, a set of parameter values are considered to "pass"
152             against a given ruleset if they are consistent with all of its rules. Rulesets may be
153             included inside other rulesets by means of L and L rules. This allows
154             you to define common rulesets to validate various groups of parameters, and then combine
155             them together into specific rulesets for use with different URL paths.
156              
157             A ruleset is considered to be "fulfilled" by a request if at least one parameter
158             mentioned in a L rule is included in that request, or trivially if the ruleset
159             does not contain any rules of those types. When you use L to validate a
160             request against a particular ruleset, the request will be rejected unless the following
161             are both true:
162              
163             =over 4
164              
165             =item *
166              
167             The request passes against the specified ruleset and all those that it includes.
168              
169             =item *
170              
171             The specified ruleset is fulfilled, along with any other rulesets included by
172             L rules. Rulesets included by L rules do not have to be fulfilled.
173              
174             =back
175              
176             This provides you with a lot of flexibilty as to requiring or not requiring various
177             parameters. Note that a ruleset without any L rules is automatically fulfilled,
178             which allows you to make all of the paramters optional if you wish. You can augment this
179             mechanism by using L and L rules to specify which parameters
180             must or must not be used together.
181              
182             =head2 Ruleset names
183              
184             Each ruleset must have a unique name, which can be any non-empty string. You may name
185             them after paths, parameters, functionality ("display", "filter") or whatever else makes
186             sense to you.
187              
188             =head2 Ordering of rules
189              
190             The rules in a given ruleset are always checked in the order they were defined. Rulesets
191             that are included via L and L rules are checked immediately when the
192             including rule is evaluated. Each ruleset is checked at most once per validation, even
193             if it is included multiple times.
194              
195             You should be cautious about including multiple parameter rules that correspond to the
196             same parameter name, as this can lead to situations where no possible value is correct.
197              
198             =head2 Unrecognized parameters
199              
200             By default, a request will be rejected with an appropriate error message if it contains
201             any parameters not mentioned in any of the checked rulesets. This can be overridden (see
202             below) to generate warnings instead. However, please think carefully before choosing
203             this option. Allowing unrecognized parameters opens up the possibility that optional
204             parameters will be accidentally misspelled and thus ignored, so that the results are
205             mysteriously different from what was expected. If you override this behavior, you should
206             make sure that any resulting warnings are explicitly displayed in the response that you
207             generate.
208              
209             =head2 Rule syntax
210              
211             Every rule is represented by a hashref that contains a key indicating the rule type. For
212             clarity, you should always write this key first. It is an error to include more than one
213             of these keys in a single rule. You may optionally include additional keys to specify
214             what are the acceptable values for this parameter, what error message should be returned
215             if the parameter value is not acceptable, and L.
216              
217             =head3 Parameter rules
218              
219             The following three types of rules define the recognized parameter names.
220              
221             =head4 param
222              
223             { param => , valid => ... }
224              
225             If the specified parameter is present with a non-empty value, then its value must pass
226             one of the specified validators. If it passes any of them, the rest are ignored. If it
227             does not pass any of them, then an appropriate error message will be generated. If no
228             validators are specified, then the value will be accepted no matter what it is.
229              
230             If the specified parameter is present, then the containing ruleset will be marked as
231             "fulfilled". You could use this, for example, with a query URL in order to require that
232             the query not be empty but instead contain at least one significant criterion. The
233             parameters that count as "significant" would be declared by C rules, the others
234             by C rules.
235              
236             =head4 optional
237              
238             { optional => , valid => ... }
239              
240             An C rule is identical to a C rule, except that the presence or absence
241             of the parameter will have no effect on whether or not the containing ruleset is
242             fulfilled. A ruleset in which all of the parameter rules are C will always be
243             fulfilled. This kind of rule is useful in validating URL parameters, especially for GET
244             requests.
245              
246             =head4 mandatory
247              
248             { mandatory => , valid => ... }
249              
250             A C rule is identical to an C rule, except that this parameter is
251             required to be present with a non-empty value regardless of the presence or absence of
252             other parameters. If it is not, then an error message will be generated. This kind of
253             rule can be useful when validating HTML form submissions, for use with fields such as
254             "name" that must always be filled in.
255              
256             =head3 Parameter constraint rules
257              
258             The following rule types can be used to specify additional constraints on the presence
259             or absence of parameter names.
260              
261             =head4 together
262              
263             { together => [ ... ] }
264              
265             If one of the listed parameters is present, then all of them must be. This can be used
266             with parameters such as 'longitude' and 'latitude', where neither one makes sense
267             without the other.
268              
269             =head4 at_most_one
270              
271             { at_most_one => [ ... ] }
272              
273             At most one of the listed parameters may be present. This can be used along with a
274             series of C rules to require that exactly one of a particular set of parameters
275             is provided.
276              
277             =head4 ignore
278              
279             { ignore => [ ... ] }
280              
281             The specified parameter or parameters will be ignored if present, and will not be
282             included in the set of reported parameter values. This rule can be used to prevent
283             requests from being rejected with "unrecognized parameter" errors in cases where
284             spurious parameters may be present. If you are specifying only one parameter name, it
285             does need not be in a listref.
286              
287             =head3 Inclusion rules
288              
289             The following rule types can be used to include one ruleset inside of another. This
290             allows you, for example, to define rulesets for validating different groups of
291             parameters and then combine them into specific rulesets for use with different URL
292             paths.
293              
294             It is okay for an included ruleset to itself include other rulesets. A given ruleset is
295             checked at most once per validation no matter how many times it is included.
296              
297             =head4 allow
298              
299             { allow => }
300              
301             A rule of this type is essentially an 'include' statement. If this rule is encountered
302             during a validation, it causes the named ruleset to be checked immediately. The
303             parameters must pass against this ruleset, but it does not have to be fulfilled.
304              
305             =head4 require
306              
307             { require => }
308              
309             This is a variant of C, with an additional constraint. The validation will fail
310             unless the named ruleset not only passes but is also fulfilled by the parameters. You
311             could use this, for example, with a query URL in order to require that the query not be
312             empty but instead contain at least one significant criterion. The parameters that count
313             as "significant" would be declared by L rules, the others by L or
314             L rules.
315              
316             =head3 Inclusion constraint rules
317              
318             The following rule types can be used to specify additional constraints on the inclusion
319             of rulesets.
320              
321             =head4 require_one
322              
323             { require_one => [ ... ] }
324              
325             You can use a rule of this type to place an additional constraint on a list of rulesets
326             already included with L. Exactly one of the named
327             rulesets must be fulfilled, or else the request is rejected. You can use this, for
328             example, to ensure that a request includes either a parameter from group A or one from
329             group B, but not both.
330              
331             =head4 require_any
332              
333             { require_any => [ ... ] }
334              
335             This is a variant of C. At least one of the named rulesets must be
336             fulfilled, or else the request will be rejected.
337              
338             =head4 allow_one
339              
340             { allow_one => [ ... ] }
341              
342             Another variant of C. The request will be rejected if more than one
343             of the listed rulesets is fulfilled, but will pass if either none of them or
344             just one of them is fulfilled. This can be used to allow optional parameters
345             from either group A or group B, but not from both groups.
346              
347             =head3 Other rules
348              
349             =head4 content_type
350              
351             { content_type => , valid => [ ... ] }
352              
353             You can use a rule of this type, if you wish, to direct that the value of the
354             specified parameter be used to indicate the content type of the response. Only one
355             of these rules should occur in any given validation. The key C gives a
356             list of acceptable values and the content types they should map to. For
357             example, if you are using this module with L then you could do
358             something like the following:
359              
360             define_ruleset '/some/path' =>
361             { require => 'some_params' },
362             { allow => 'other_params' },
363             { content_type => 'ct', valid => ['html', 'json', 'frob=application/frobnicate'] };
364            
365             get '/some/path.:ct' => sub {
366            
367             my $valid_request = check_params('/some/path', params);
368             content_type $valid_request->content_type;
369             ...
370             }
371              
372             This code specifies that the content type of the response will be set by the
373             URL path suffix, which may be either C<.html>, C<.json> or C<.frob>.
374              
375             If the value given in a request does not occur in the list, or if no value is
376             found, then an error message will be generated that lists the accepted types.
377              
378             To match an empty parameter value, include a string that looks like
379             '=some/type'. You need not specify the actual content type string for the
380             well-known types 'html', 'json', 'xml', 'txt' or 'csv', unless you wish to
381             override the default given by this module.
382              
383             =head2 Rule attributes
384              
385             Any rule definition may also include one or more of the following attributes,
386             specified as key/value pairs in the rule hash:
387              
388             =head3 errmsg
389              
390             This attribute specifies the error message to be returned if the rule fails,
391             overriding the default message. For example:
392              
393             define_ruleset( 'specifier' =>
394             { param => 'name', valid => ANY_VALUE },
395             { param => 'id', valid => POS_VALUE });
396            
397             define_ruleset( 'my_route' =>
398             { require => 'specifier',
399             errmsg => "you must specify either of the parameters 'name' or 'id'" });
400              
401             Error messages may include any of the following placeholders: C<{param}>,
402             C<{value}>. These are replaced respectively by the relevant parameter name(s)
403             and original parameter value(s), single-quoted. This feature allows you to
404             define messages that quote the actual parameter values presented in the
405             request, as well as to define common messages and use them with multiple
406             rules.
407              
408             =head3 warn
409              
410             This attribute causes a warning to be generated rather than an error if the
411             rule fails. Unlike errors, warnings do not cause a request to be rejected.
412             At the end of the validation process, the list of generated warnings can be
413             retrieved by using the L method of the result object.
414              
415             If the value of this key is 1, then what would otherwise be the error
416             message will be used as the warning message. Otherwise, the specified string
417             will be used as the warning message.
418              
419             For parameter rules, this attribute affects only errors resulting from
420             validation of the parameter values. Other error conditions (i.e. multiple
421             parameter values without the L attribute) continue to be reported
422             as errors.
423              
424             =head3 key
425              
426             The attribute 'key' specifies the name under which any information generated by
427             the rule will be saved. For a parameter rule, the cleaned value will be saved
428             under this name. For all rules, any generated warnings or errors will be
429             stored under the specified name instead of the parameter name or rule number.
430             This allows you to easily determine after a validation which
431             warnings or errors were generated.
432              
433             The following keys can be used only with rules of type
434             L, L or L:
435              
436             =head3 valid
437              
438             This attribute specifies the domain of acceptable values for the parameter. The
439             value must be either a single code reference or a list of them. You can
440             either select from the list of L
441             included with this module, or provide your own.
442              
443             If the parameter named by this rule is present, its value must pass at least
444             one of the specified validators or else an error message will be generated.
445             If multiple validators are given, then the error message returned will be the
446             one generated by the last validator in the list. This can be overridden by
447             using the L key.
448              
449             =head3 multiple
450              
451             This attribute specifies that the parameter may appear multiple times in the
452             request. Without this directive, multiple values for the same parameter will
453             generate an error. For example:
454              
455             define_ruleset( 'identifiers' =>
456             { param => 'id', valid => POS_VALUE, multiple => 1 });
457              
458             If this attribute is present with a true value, then the cleaned value of the
459             parameter will be an array ref if at least one valid value was found and
460             I otherwise. If you wish a request to be considered valid even if some
461             of the values fail the validator, then either use the L attribute instead or
462             include a L key as well.
463              
464             =head3 split
465              
466             This attribute has the same effect as L, and in addition causes
467             each parameter value string to be split (L) as indicated by the
468             value of the directive. If this value is a string, then it will be compiled
469             into a regexp preceded and followed by C<\s*>. So in the
470             following example:
471              
472             define_ruleset( 'identifiers' =>
473             { param => 'id', valid => POS_VALUE, split => ',' });
474              
475             The value string will be considered to be valid if it contains one or more
476             positive integers separated by commas and optional whitespace. Empty strings
477             between separators are ignored.
478              
479             123,456 # returns [123, 456]
480             123 , ,456 # returns [123, 456]
481             , 456 # returns [456]
482             123 456 # not valid
483             123:456 # not valid
484              
485             If you wish more precise control over the separator expression, you can pass a
486             regexp quoted with L instead.
487              
488             =head3 list
489              
490             This attribute has the same effect as L, but generates warnings
491             instead of error messages when invalid values are encountered (as if
492             C<< warn => 1 >> was also specified). The resulting cleaned value will be a
493             listref containing any values which pass the validator, or I if no
494             valid values were found. See also L and L.
495              
496             =head3 bad_value
497              
498             This attribute can be useful in conjunction with L. If one or more
499             values are given for the parameter but none of them are valid, this attribute
500             comes into effect. If the value of this attribute is C, then the
501             validation will fail with an appropriate error message. Otherwise, this will
502             be used as the value of the parameter. It is recommended that you set the
503             value to something outside of the valid range, i.e. C<-1> for a C
504             parameter.
505              
506             Using this attribute allows you to easily distinguish between the case when
507             the parameter appears with an empty value (or not at all, which is considered
508             equivalent) vs. when the parameter appears with one or more invalid values and
509             no good ones.
510              
511             =head3 alias
512              
513             This attribute specifies one or more aliases for the parameter name (use a
514             listref for multiple aliases). These names may be used interchangeably in
515             requests, but any request that contains more than one of them will be rejected
516             with an appropriate error message unless L is also specified. The
517             parameter value and any error or warning messages will be reported under the
518             main parameter name for this rule, no matter which alias is used in the
519             request.
520              
521             =head3 clean
522              
523             This attribute specifies a subroutine which will be used to modify the
524             parameter values. This routine will be called with the raw value of the
525             parameter as its only argument, once for each value if multiple values are
526             allowed. The resulting values will be stored as the "cleaned" values. The
527             value of this directive may be either a code ref or one of the strings 'uc',
528             'lc' or 'fc'. These direct that the parameter values be converted to
529             uppercase, lowercase, or L respectively.
530              
531             =head3 default
532              
533             This attribute specifies a default value for the parameter, which will be
534             reported if the parameter is not present in the request or if it is present
535             with an empty value. If the rule also includes a validator and/or a cleaner,
536             the specified default value will be passed to it when the ruleset is defined.
537             An exception will be thrown if the default value does not pass the validator.
538              
539             =head3 allow_empty
540              
541             If this attribute is given a true value, then if the corresponding parameter has
542             the value I, that value will be reported for the parameter. Otherwise,
543             parameters with an undefined value will be ignored. This attribute is useful for
544             rulesets used to validate records for insertion into a database, because it
545             allows nulls to be expressed as I.
546              
547             =head3 before
548              
549             This attribute can be used to change the order in which the rules are listed.
550             Its value should be the name of a parameter rule or an inclusion rule in the
551             same ruleset. The rule will be placed in the list immediately before the named
552             rule, if it can be found. Otherwise, it will be placed at the end of the list as
553             usual.
554              
555             =head3 note
556              
557             This attribute attaches an arbitrary string to the rule. It can be used to
558             provide hints to other code modules as to how the rule should be displayed.
559              
560             =head3 undocumented
561              
562             If this attribute is given with a true value, then this rule will be ignored
563             by any calls to L. This feature allows you to include
564             parameters that are recognized as valid but that are not included in any
565             generated documentation. Such parameters will be invisible to users, but
566             will be visible and clearly marked to anybody browsing your source code.
567              
568             =head2 Documentation
569              
570             A ruleset definition may include strings interspersed with the rule
571             definitions (see the L) which can
572             be turned into documentation in Pod format by means of the L
573             keyword. It is recommended that you use this function to auto-generate the
574             C section of the documentation pages for the various URL paths
575             accepted by your web application, translating the output from Pod to whatever
576             format is appropriate. This will help you to keep the documentation and the
577             actual rules in synchrony with one another.
578              
579             The generated documentation will consist of one or more item lists, separated
580             by ordinary paragraphs. Each parameter rule will generate one item, whose body
581             consists of the documentation strings immediately following the rule
582             definition. Ordinary paragraphs (see below) can be used to separate the
583             parameters into groups for documentation purposes, or at the start or end of a
584             list as introductory or concluding material. Each L or L
585             rule causes the documentation for the indicated ruleset(s) to be interpolated,
586             except as noted below. Note that this subsidiary documentation will not be
587             nested. All of the parameters will be documented at the same list indentation
588             level, whether or not they are defined in subsidiary rulesets.
589              
590             Documentation strings may start with one of the following special characters:
591              
592             =over 4
593              
594             =item C<<< >> >>>
595              
596             The remainder of this string, plus any strings immediately following, will
597             appear as an ordinary paragraph. You can use this feature to provide
598             commentary paragraphs separating the documented parameters into groups.
599             Any documentation strings occurring before the first parameter rule
600             definition, or following an C or C rule, will always generate
601             ordinary paragraphs regardless of whether they start with this special
602             character.
603              
604             =item C<<< > >>>
605              
606             The remainder of this string, plus any strings immediately following, will
607             appear as a new paragraph of the same type as the preceding paragraph (item
608             body or ordinary paragraph).
609              
610             =item C
611              
612             The preceding rule definition will be ignored by any calls to
613             L, and all documentation for this rule will be suppressed.
614             This is equivalent to specifying the rule attribute L.
615              
616             =item C<^>
617              
618             Any documentation generated for the preceding rule definition will be
619             suppressed. The remainder of this string plus any strings immediately
620             following will appear as an ordinary paragraph in its place. You can use
621             this, for example, to document a subsidiary ruleset with an explanatory note
622             (i.e. a link to another documentation section or page) instead of explicitly
623             listing all of the included parameters.
624              
625             =item C
626              
627             This character is ignored at the beginning of a documentation string, and the
628             next character loses any special meaning it might have had. You can use this
629             in the unlikely event that you want a documentation paragraph to actually
630             start with one of these special characters.
631              
632             =back
633              
634             Note that modifier rules such as C, C, etc. are not explicitly
635             documented. Any documentation strings following them will be treated as if they apply to
636             the most recently preceding parameter rule or inclusion rule.
637              
638             =cut
639              
640             our (@EXPORT_OK, @VALIDATORS, %EXPORT_TAGS);
641              
642             BEGIN {
643              
644 8     8   92 @EXPORT_OK = qw(
645             define_ruleset check_params validation_settings ruleset_defined document_params
646             list_params list_rulesets list_rules
647             INT_VALUE POS_VALUE POS_ZERO_VALUE
648             DECI_VALUE
649             ENUM_VALUE
650             BOOLEAN_VALUE
651             MATCH_VALUE
652             FLAG_VALUE ANY_VALUE
653             );
654            
655 8         30 @VALIDATORS = qw(INT_VALUE POS_VALUE POS_ZERO_VALUE DECI_VALUE
656             ENUM_VALUE MATCH_VALUE BOOLEAN_VALUE FLAG_VALUE ANY_VALUE);
657              
658 8         53804 %EXPORT_TAGS = (
659             keywords => [qw(define_ruleset check_params validation_settings ruleset_defined
660             document_params list_params list_rulesets list_rules)],
661             validators => \@VALIDATORS,
662             );
663             };
664              
665             # The following defines a single global validator object, for use when this
666             # module is used in the non-object-oriented manner.
667              
668             our ($DEFAULT_INSTANCE) = bless { RULESETS => {}, SETTINGS => {} };
669              
670              
671             # Known media types are defined here
672              
673             my (%MEDIA_TYPE) =
674             ('html' => 'text/html',
675             'xml' => 'text/xml',
676             'txt' => 'text/plain',
677             'tsv' => 'text/tab-separated-values',
678             'csv' => 'text/csv',
679             'json' => 'application/json',
680             );
681              
682             # Default error messages
683              
684             my (%ERROR_MSG) =
685             ('ERR_INVALID' => "the value of parameter {param} is invalid (was {value})",
686             'ERR_BAD_VALUES' => "no valid values were specified for {param} (found {value})",
687             'ERR_MULT_NAMES' => "you may only include one of {param}",
688             'ERR_MULT_VALUES' => "you may only specify one value for {param}: found {value}",
689             'ERR_MANDATORY' => "you must specify a value for {param}",
690             'ERR_TOGETHER' => "you must specify {param} together or not at all",
691             'ERR_AT_MOST' => "you may not specify more than one of {param}",
692             'ERR_REQ_SINGLE' => "you must specify the parameter {param}",
693             'ERR_REQ_MULT' => "you must specify at least one of the parameters {param}",
694             'ERR_REQ_ONE' => "you may not include parameters from more than one of these groups: {param}",
695             'ERR_MEDIA_TYPE' => "you must specify a media type, from the following list: {value}",
696             'ERR_DEFAULT' => "parameter value error: {param}",
697             );
698              
699             =head1 INTERFACE
700              
701             This module can be used in either an object-oriented or a procedural manner.
702             To use the object-oriented interface, generate a new instance of
703             HTTP::Validate and use any of the routines listed below as methods:
704              
705             use HTTP::Validate qw(:validators);
706            
707             my $validator = HTTP::Validate->new();
708            
709             $validator->define_ruleset('my_params' =>
710             { param => 'foo', valid => INT_VALUE, default => '0' });
711            
712             my $result = $validator->check_params('my_params', \%ARGS);
713              
714             Otherwise, you can export these routines to your module and call them
715             directly. In this case, a global ruleset namespace will be assumed:
716              
717             use HTTP::Validate qw(:keywords :validators);
718            
719             define_ruleset('my_params' =>
720             { param => 'foo', valid => INT_VALUE, default => '0' });
721            
722             my $validated = check_params('my_params', \%ARGS);
723              
724             Using C<:keywords> will import all of the keywords listed below, except
725             'new'. Using C<:validators> will import all of the L
726             listed below.
727              
728             The following can be called either as subroutines or as method names,
729             depending upon which paradigm you prefer:
730              
731             =head3 new
732              
733             This can be called as a class method to generate a new validation instance
734             (see example above) with its own ruleset namespace. Any of the arguments that
735             can be passed to L can also be passed to this routine.
736              
737             =cut
738              
739             sub new {
740              
741 17     17 1 547283 my ($class, @settings) = @_;
742            
743 17 50       75 croak "You must call 'new' as a class method" unless defined $class;
744            
745             # Create a new object
746            
747 17         55 my $self = bless { RULESETS => {}, SETTINGS => {} }, $class;
748            
749             # Set the requested settings
750            
751 17         53 $self->validation_settings(@settings);
752            
753             # Return the new object
754            
755 15         31 return $self;
756             }
757              
758              
759             =head3 define_ruleset
760              
761             This keyword defines a set of rules to be used for validating parameters. The
762             first argument is the ruleset's name, which must be unique within its
763             namespace. The rest of the parameters must be a list of rules (hashrefs) interspersed
764             with documentation strings. For examples, see above.
765              
766             =cut
767              
768             sub define_ruleset {
769            
770             # If we were called as a method, use the object on which we were called.
771             # Otherwise, use the default instance.
772            
773 93 100   93 1 306555 my $self = ref $_[0] eq 'HTTP::Validate' ? shift : $DEFAULT_INSTANCE;
774              
775 93         167 my ($ruleset_name, @rest) = @_;
776            
777             # Next make sure we know where this is called from, for the purpose of
778             # generating useful error messages.
779            
780 93         192 my ($package, $filename, $line) = caller;
781            
782             # Check the arguments, then create a new ruleset object.
783            
784 93 100 100     598 croak "The first argument to 'define_ruleset' must be a non-empty string"
      100        
785             unless defined $ruleset_name && !ref $ruleset_name && $ruleset_name ne '';
786            
787 90         170 my $rs = $self->create_ruleset($ruleset_name, $filename, $line);
788            
789             # Then add the rules and documentation strings.
790            
791 88         161 $self->add_rules($rs, @rest);
792            
793             # If we get here without any errors, install the ruleset and return.
794            
795 77         160 $self->{RULESETS}{$ruleset_name} = $rs;
796 77         203 return 1;
797             };
798              
799              
800             =head3 check_params
801              
802             my $result = check_params('my_ruleset', undef, params('query'));
803            
804             if ( $result->passed )
805             {
806             # process the request using the keys and values returned by
807             # $result->values
808             }
809            
810             else
811             {
812             # redisplay the form, send an error response, or otherwise handle the
813             # error condition using the error messages returned by $result->errors
814             }
815              
816             This function validates a set of parameters and values (which may be provided
817             either as one or more hashrefs or as a flattened list of keys and values or a
818             combination of the two) against the named ruleset with the specified context. It
819             returns a response object from which you can get the cleaned parameter values
820             along with any errors or warnings that may have been generated.
821              
822             The second parameter must be either a hashref or undefined. If it is defined,
823             it is passed to each of the validator functions as "context". This allows you
824             to provide attributes such as a database handle to the validator functions.
825             The third parameter must be either a hashref or a listref containing parameter
826             names and values. If it is a listref, any items at the beginning of the list
827             which are themselves hashrefs will be expanded before the list is processed
828             (this allows you, for example, to pass in a hashref plus some additional names
829             and values without having to modify the hashref in place).
830              
831             You can use the L method on the returned object to determine if the
832             validation passed or failed. In the latter case, you can return an HTTP error
833             response to the user, or perhaps redisplay a submitted form.
834              
835             Note that you can validate against multiple rulesets at once by defining a
836             ruleset with inclusion rules referring to all of the rulesets
837             you wish to validate against.
838              
839             =cut
840              
841             sub check_params {
842            
843             # If we were called as a method, use the object on which we were called.
844             # Otherwise, use the globally defined one.
845            
846 78 100   78 1 38826 my $self = ref $_[0] eq 'HTTP::Validate' ? shift : $DEFAULT_INSTANCE;
847            
848 78         120 my ($ruleset_name, $context, $parameters) = @_;
849            
850             # Create a new validation-execution object using the specified context
851             # and parameters.
852            
853 78         135 my $vr = $self->new_execution($context, $parameters);
854            
855             # Now execute that validation using the specified ruleset, and return the
856             # result.
857            
858 76         123 return $self->execute_validation($vr, $ruleset_name);
859             };
860              
861              
862             =head3 validation_settings
863              
864             This function allows you to change the settings on the validation routine.
865             For example:
866              
867             validation_settings( allow_unrecognized => 1 );
868              
869             If you are using this module in an object-oriented way, then you can also pass
870             any of these settings as parameters to the constructor method. Available
871             settings include:
872              
873             =over 4
874              
875             =item allow_unrecognized
876              
877             If specified, then unrecognized parameters will generate warnings instead of errors.
878              
879             =item ignore_unrecognized
880              
881             If specified, then unrecognized parameters will be ignored entirely.
882              
883             =back
884              
885             You may also specify one or more of the following keys, each followed by a string. These
886             allow you to redefine the default messages that are generated when parameter errors are
887             detected:
888              
889             ERR_INVALID, ERR_BAD_VALUES, ERR_MULT_NAMES, ERR_MULT_VALUES, ERR_MANDATORY, ERR_TOGETHER,
890             ERR_AT_MOST, ERR_REQ_SINGLE, ERR_REQ_MULT, ERR_REQ_ONE, ERR_MEDIA_TYPE, ERR_DEFAULT
891              
892             For example:
893              
894             validation_settings( ERR_MANDATORY => 'Missing mandatory parameter {param}',
895             ERR_REQ_SINGLE => 'Found {value} for {param}: only one value is allowed' );
896              
897             =cut
898              
899             sub validation_settings {
900            
901             # If we were called as a method, use the object on which we were called.
902             # Otherwise, use the globally defined one.
903            
904 23 100   23 1 3359 my $self = ref $_[0] eq 'HTTP::Validate' ? shift : $DEFAULT_INSTANCE;
905            
906 23         56 while (@_)
907             {
908 34         35 my $key = shift;
909 34         33 my $value = shift;
910            
911 34 100       63 if ( $key eq 'allow_unrecognized' )
    100          
    100          
912             {
913 5 50       24 $self->{SETTINGS}{permissive} = $value ? 1 : 0;
914             }
915            
916             elsif ( $key eq 'ignore_unrecognized' )
917             {
918 2 50       7 $self->{SETTINGS}{ignore_unrecognized} = $value ? 1 : 0;
919             }
920            
921             elsif ( $ERROR_MSG{$key} )
922             {
923 24         40 $self->{SETTINGS}{$key} = $value;
924             }
925            
926             else
927             {
928 3         389 croak "unrecognized setting: '$key'";
929             }
930             }
931            
932 20         27 return 1;
933             }
934              
935              
936             =head3 ruleset_defined
937              
938             if ( ruleset_defined($ruleset_name) ) {
939             # then do something
940             }
941              
942             This function returns true if a ruleset has been defined with the given name,
943             false otherwise.
944              
945             =cut
946              
947             sub ruleset_defined {
948              
949             # If we were called as a method, use the object on which we were called.
950             # Otherwise, use the globally defined one.
951            
952 2 50   2 1 2324 my $self = ref $_[0] eq 'HTTP::Validate' ? shift : $DEFAULT_INSTANCE;
953            
954 2         5 my ($ruleset_name) = @_;
955            
956             # Return the requested result
957            
958 2         7 return defined $self->{RULESETS}{$ruleset_name};
959             }
960              
961              
962             =head3 document_params
963              
964             This function generates L for the given
965             ruleset, in L format. This works best if you have included
966             documentation strings in your calls to L. The method returns
967             I if the specified ruleset is not found.
968              
969             $my_doc = document_params($ruleset_name);
970              
971             This capability has been included in order to simplify the process of
972             documenting web services implemented using this module. The author has
973             noticed that documentation is much easier to maintain and more likely to be
974             kept up-to-date if the documentation strings are located right next to the
975             relevant definitions.
976              
977             Any parameter rules that you wish to leave undocumented should either be given
978             the attribute 'undocumented' or be immediately followed by a string starting
979             with "!". All others will automatically generate list items in the resulting
980             documentation, even if no documentation string is provided (in this case, the
981             item body will be empty).
982              
983             =cut
984              
985             sub document_params {
986              
987             # If we were called as a method, use the object on which we were called.
988             # Otherwise, use the globally defined instance.
989            
990 4 50   4 1 593 my $self = ref $_[0] eq 'HTTP::Validate' ? shift : $DEFAULT_INSTANCE;
991            
992 4         6 my ($ruleset_name) = @_;
993            
994             # Make sure we have a valid ruleset, or else return false.
995            
996 4 50       9 return unless defined $ruleset_name;
997            
998 4 50       10 my $rs = $self->{RULESETS}{$ruleset_name} or return;
999            
1000             # Now generate the requested documentation.
1001            
1002 4         14 return $self->generate_docstring($rs, { in_list => 0, level => 0, processed => {} });
1003             }
1004              
1005              
1006             =head3 list_params
1007              
1008             This function returns a list of the names of all parameters accepted by the
1009             specified ruleset, including those accepted by included rulesets.
1010              
1011             my @parameter_names = list_params($ruleset_name);
1012              
1013             This may be useful if your validations allow unrecognized parameters, as it
1014             enables you to determine which of the parameters in a given request are
1015             significant to that request.
1016              
1017             =cut
1018              
1019             sub list_params {
1020              
1021             # If we were called as a method, use the object on which we were called.
1022             # Otherwise, use the globally defined instance.
1023            
1024 1 50   1 1 3 my $self = ref $_[0] eq 'HTTP::Validate' ? shift : $DEFAULT_INSTANCE;
1025            
1026 1         2 my ($ruleset_name) = @_;
1027            
1028             # Make sure we have a valid ruleset, or else return false.
1029            
1030 1 50       3 return unless defined $ruleset_name;
1031            
1032 1 50       3 my $rs = $self->{RULESETS}{$ruleset_name} or return;
1033            
1034             # Now generate the requested list.
1035            
1036 1         5 return $self->generate_param_list($ruleset_name);
1037             }
1038              
1039              
1040             =head3 list_rulesets
1041              
1042             This function returns a list of defined ruleset names.
1043              
1044             =cut
1045              
1046             sub list_rulesets {
1047              
1048             # If we were called as a method, use the object on which we were called.
1049             # Otherwise, use the globally defined instance.
1050            
1051 1 50   1 1 8 my $self = ref $_[0] eq 'HTTP::Validate' ? shift : $DEFAULT_INSTANCE;
1052            
1053 1         2 return keys %{$self->{RULESETS}};
  1         7  
1054             }
1055              
1056              
1057             =head3 list_rules
1058              
1059             This function returns a list of the rules belonging to the specified ruleset and
1060             any included rulesets.
1061              
1062             my @rules = list_rules($ruleset_name);
1063              
1064             =cut
1065              
1066             sub list_rules {
1067              
1068             # If we were called as a method, use the object on which we were called.
1069             # Otherwise, use the globally defined instance.
1070            
1071 1 50   1 1 342 my $self = ref $_[0] eq 'HTTP::Validate' ? shift : $DEFAULT_INSTANCE;
1072            
1073 1         2 my ($ruleset_name) = @_;
1074            
1075             # Make sure we have a valid ruleset, or else return false.
1076            
1077 1 50       2 return unless defined $ruleset_name;
1078            
1079 1 50       3 my $rs = $self->{RULESETS}{$ruleset_name} or return;
1080            
1081             # Now generate the requested list.
1082            
1083 1         4 return $self->generate_rule_list($ruleset_name);
1084             }
1085              
1086              
1087             # Here are the implementing functions for the methods listed above:
1088             # =================================================================
1089              
1090             # create_ruleset ( ruleset_name, filename, line )
1091             #
1092             # Create a new ruleset with the given name, noting that it was defined in the
1093             # given filename at the given line number.
1094              
1095             sub create_ruleset {
1096              
1097 90     90 0 141 my ($validator, $ruleset_name, $filename, $line_no) = @_;
1098            
1099             # Make sure that a non-empty name was given, and that no ruleset has
1100             # already been defined under that name.
1101            
1102 90 50 33     216 croak "you must provide a non-empty string as the ruleset name"
1103             if $ruleset_name eq '' || ref $ruleset_name;
1104            
1105 90 100       167 if ( exists $validator->{RULESETS}{$ruleset_name} )
1106             {
1107 2         4 my $filename = $validator->{RULESETS}{$ruleset_name}{filename};
1108 2         3 my $line_no = $validator->{RULESETS}{$ruleset_name}{line_no};
1109 2         196 croak "ruleset '$ruleset_name' was already defined at line $line_no of $filename\n";
1110             }
1111            
1112             # Create the new ruleset.
1113            
1114 88         375 my $rs = { name => $ruleset_name,
1115             filename => $filename,
1116             line_no => $line_no,
1117             doc_items => [],
1118             fulfill_order => [],
1119             params => {},
1120             includes => {},
1121             rules => [] };
1122            
1123 88         213 return bless $rs, 'HTTP::Validate::Ruleset';
1124             }
1125              
1126              
1127             # List all of the keys that are allowed in rule specifications. Those whose
1128             # value is 2 indicate the rule type, and at most one of these may be included
1129             # per rule. The others are optional.
1130              
1131             my %DIRECTIVE = ( param => 2, optional => 2, mandatory => 2,
1132             together => 2, at_most_one => 2, ignore => 2,
1133             require => 2, allow => 2, require_one => 2,
1134             require_any => 2, allow_one => 2, content_type => 2,
1135             valid => 1, clean => 1, allow_empty => 1, note => 1,
1136             multiple => 1, split => 1, list => 1, bad_value => 1,
1137             error => 1, errmsg => 1, warn => 1, undocumented => 1,
1138             alias => 1, key => 1, default => 1, before => 1 );
1139              
1140              
1141             # Categorize the rule types
1142              
1143             my %CATEGORY = ( param => 'param',
1144             optional => 'param',
1145             mandatory => 'param',
1146             together => 'modifier',
1147             at_most_one => 'modifier',
1148             ignore => 'modifier',
1149             require => 'include',
1150             allow => 'include',
1151             require_one => 'constraint',
1152             allow_one => 'constraint',
1153             require_any => 'constraint',
1154             content_type => 'content' );
1155              
1156              
1157             # List the special validators.
1158              
1159             my (%VALIDATOR_DEF) = ( FLAG_VALUE => 1, ANY_VALUE => 1 );
1160              
1161             my (%CLEANER_DEF) = ( 'uc' => eval 'sub { return uc $_[0] }',
1162             'lc' => eval 'sub { return lc $_[0] }',
1163             'fc' => $case_fold );
1164              
1165              
1166             # add_rules ( ruleset, rule ... )
1167             #
1168             # Add rules to the specified ruleset. The rules may be optionally
1169             # interspersed with documentation strings.
1170              
1171             sub add_rules {
1172            
1173 88     88 0 96 my ($self) = shift;
1174 88         83 my ($rs) = shift;
1175            
1176 88         103 my @doc_lines; # collect up documentation strings until we know how to apply them
1177             my $doc_rule; # the rule to which all new documentation strings should be added
1178            
1179             # Go through the items in @_, one by one.
1180            
1181             RULE:
1182 88         111 foreach my $rule (@_)
1183             {
1184             # If the item is a scalar, then it is a documentation string.
1185            
1186 232 100 33     413 unless ( ref $rule )
1187             {
1188             # If the string starts with >, !, ^, or ? then treat it specially.
1189            
1190 16 100       74 if ( $rule =~ qr{ ^ ([!^?] | >>?) (.*) }xs )
1191             {
1192             # If >>, then close the active documentation section (if any)
1193             # and start a new one that is not tied to any rule. This will
1194             # generate an ordinary paragraph starting with the remainder
1195             # of the line.
1196            
1197 5 100       27 if ( $1 eq '>>' )
    100          
    100          
    100          
1198             {
1199 1 50 33     6 $self->add_doc($rs, $doc_rule, @doc_lines) if $doc_rule || @doc_lines;
1200 1         4 @doc_lines = $2;
1201 1         1 $doc_rule = undef;
1202             }
1203            
1204             # If >, then add to the current documentation a blank line
1205             # (which will cause a new paragraph) followed by the remainder
1206             # of this line.
1207            
1208             elsif ( $1 eq '>' )
1209             {
1210 1         2 push @doc_lines, "", $2;
1211             }
1212            
1213             # If !, then discard the contents of the current documentation
1214             # section and replace them with this line (including the !
1215             # character). This will cause add_doc to later discard them.
1216            
1217             elsif ( $1 eq '!' )
1218             {
1219 1         2 @doc_lines = $rule;
1220             }
1221            
1222             # If ^, then discard the contents of the current documentation
1223             # section and replace them with the remainder of the line.
1224             # Set $doc_rule to undef, which will cause the rule currently
1225             # being documented to be forgotten and the documentation to be
1226             # added as an ordinary paragraph instead.
1227            
1228             elsif ( $1 eq '^' )
1229             {
1230 1         2 @doc_lines = $2;
1231 1         3 $doc_rule = undef;
1232             }
1233            
1234             # If ?, then add the remainder of the line to the current
1235             # documentation section. This will prevent the next character
1236             # from being interpreted specially.
1237            
1238             else
1239             {
1240 1         2 push @doc_lines, $2;
1241             }
1242             }
1243            
1244             # Otherwise, just add this string to the current documentation section.
1245            
1246             else
1247             {
1248 11         17 push @doc_lines, $rule;
1249             }
1250            
1251 16         29 next RULE;
1252             }
1253            
1254             # All other items must be hashrefs, otherwise throw an exception.
1255            
1256             elsif ( reftype $rule ne 'HASH' )
1257             {
1258             croak "The arguments to 'define_ruleset' must all be hashrefs and/or strings";
1259             }
1260            
1261             # If we get here, assume the item represents a rule and create a new record to
1262             # represent it.
1263            
1264 216         164 my $rr = { rn => scalar(@{$rs->{rules}}) + 1 };
  216         332  
1265              
1266             # If the item has the 'before' attribute set, add the new rule before
1267             # the rule whose parameter name is equal to the value of 'before', if one
1268             # such is found.
1269            
1270 216         182 my $inserted;
1271            
1272 216 100       283 if ( my $search_for = $rule->{before} )
1273             {
1274 1         3 foreach my $i ( 0 .. $rs->{rules}->$#* )
1275             {
1276 1         1 my $rule_i = $rs->{rules}[$i];
1277            
1278 1 50 0     7 if ( $rule_i->{type} eq 'param' &&
      33        
      33        
      33        
1279             $search_for eq ($rule_i->{param} || $rule_i->{optional} ||
1280             $rule_i->{mandatory}) ||
1281             $rule_i->{type} eq 'include' &&
1282             $search_for eq $rule_i->{ruleset} )
1283             {
1284 1         2 splice $rs->{rules}->@*, $i, 0, $rr;
1285 1         1 $inserted = 1;
1286 1         2 last;
1287             }
1288             }
1289             }
1290            
1291             # Otherwise, add it to the end of the rule list.
1292            
1293 216 100       253 unless ( $inserted )
1294             {
1295 215         163 push @{$rs->{rules}}, $rr;
  215         260  
1296             }
1297            
1298             # Check all of the keys in the rule definition, making sure that all
1299             # are valid, and determine the rule type.
1300            
1301 216         167 my $type;
1302            
1303             KEY:
1304 216         305 foreach my $key (keys %$rule)
1305             {
1306 375 100 100     696 croak "unknown attribute '$key' found in rule" unless $DIRECTIVE{$key} || $ERROR_MSG{$key};
1307            
1308 373 100 100     790 if ( defined $DIRECTIVE{$key} && $DIRECTIVE{$key} == 2 )
1309             {
1310 215 100       367 croak "a rule definition cannot contain the attributes '$key' and '$type' together, because they indicate different rule types"
1311             if $type;
1312 214         178 $type = $key;
1313 214         268 $rr->{$type} = $rule->{$type};
1314 214         237 next KEY;
1315             }
1316             }
1317            
1318             # Then process the other keys.
1319            
1320 213         281 foreach my $key (keys %$rule)
1321             {
1322 371         336 my $value = $rule->{$key};
1323            
1324 371 100 100     1297 if ( $key eq 'valid' )
    100 66        
    100          
    100          
    100          
    100          
    100          
1325             {
1326             croak "the attribute 'valid' is only allowed with parameter rules"
1327 108 50 66     183 unless $CATEGORY{$type} eq 'param' || $type eq 'content_type';
1328             }
1329            
1330             elsif ( $key eq 'alias' )
1331             {
1332             croak "the attribute 'alias' is only allowed with parameter rules"
1333 4 50       10 unless $CATEGORY{$type} eq 'param';
1334            
1335 4 50 66     14 croak "the value of 'alias' must be a string or a list ref"
1336             if ref $value and ref $value ne 'ARRAY';
1337            
1338 4 100       11 $rr->{alias} = ref $value ? $value : [ $value ];
1339             }
1340            
1341             elsif ( $key eq 'clean' )
1342             {
1343             croak "they attribute 'clean' is only allowed with parameter rules"
1344 4 50       7 unless $CATEGORY{$type} eq 'param';
1345            
1346 4   66     9 $rr->{cleaner} = $CLEANER_DEF{$value} || $value;
1347            
1348             croak "invalid value '$value' for 'clean'"
1349 4 50       10 unless ref $rr->{cleaner} eq 'CODE';
1350             }
1351            
1352             elsif ( $key eq 'default' )
1353             {
1354             croak "the attribute 'default' is only allowed with parameter rules"
1355 3 50       6 unless $CATEGORY{$type} eq 'param';
1356            
1357 3         6 $rr->{default} = $value;
1358             }
1359            
1360             elsif ( $key eq 'split' || $key eq 'list' )
1361             {
1362             croak "the attribute '$key' is only allowed with parameter rules"
1363 8 50       15 unless $CATEGORY{$type} eq 'param';
1364            
1365 8 50 66     18 croak "the value of '$key' must be a string or a regexp"
1366             if ref $value and ref $value ne 'Regexp';
1367            
1368 8         9 $rr->{multiple} = 1;
1369            
1370             # Make sure that we have a proper regular expression. If 'split'
1371             # was given with a string, surround it by \s* to ignore
1372             # whitespace.
1373            
1374 8 100       12 unless ( ref $value )
1375             {
1376 7         152 $value = qr{ \s* $value \s* }oxs;
1377             }
1378            
1379 8         13 $rr->{split} = $value;
1380 8 100       25 $rr->{warn} = 1 if $key eq 'list';
1381             }
1382            
1383             elsif ( $key eq 'error' || $key eq 'errmsg' )
1384             {
1385 7         9 $rr->{errmsg} = $value;
1386             }
1387            
1388             elsif ( $key ne $type )
1389             {
1390 24 50       45 croak "the value of '$key' must be a string" if ref $value;
1391            
1392 24         32 $rr->{$key} = $value;
1393             }
1394             }
1395            
1396 213 50       283 croak "each record must include a key that specifies the rule type, e.g. 'param' or 'allow'"
1397             unless $type;
1398            
1399             # If we have any documentation strings collected up, then they belong to the
1400             # previous rule. If the current rule is a parameter rule or inclusion rule, then
1401             # add the collected documentation to the previous rule and set this new rule as
1402             # the target for subsequent documentation.
1403            
1404 213 100       283 if ( $CATEGORY{$type} ne 'modifier' )
1405             {
1406 207         362 $self->add_doc($rs, $doc_rule, @doc_lines);
1407 207         170 $doc_rule = $rr;
1408 207         201 @doc_lines = ();
1409             }
1410            
1411             # Now process the rule according to its type.
1412            
1413 213         190 my $typevalue = $rule->{$type};
1414            
1415 213 100       312 if ( $CATEGORY{$type} eq 'param' )
    100          
    100          
    100          
    50          
1416             {
1417 160         166 $rr->{type} = 'param';
1418 160         151 $rr->{param} = $typevalue;
1419            
1420             # Do some basic sanity checking.
1421            
1422 160 100 66     557 croak "the value of '$type' must be a parameter name"
      66        
1423             unless defined $typevalue && !ref $typevalue && $typevalue ne '';
1424            
1425             # Check the validators.
1426            
1427 159 100       251 my @validators = ref $rule->{valid} eq 'ARRAY' ? @{$rule->{valid}} : $rule->{valid};
  3         5  
1428            
1429 159         148 foreach my $v (@validators)
1430             {
1431 161 100 100     411 if ( defined $v && $VALIDATOR_DEF{$v} )
    100          
1432             {
1433 2 50       6 $rr->{flag} = 1 if $v eq 'FLAG_VALUE';
1434 2 50       5 push @{$rr->{validators}}, \&boolean_value if $v eq 'FLAG_VALUE';
  2         6  
1435 2 50 33     11 push @{$rr->{validators}}, \&any_value if $v eq 'ANY_VALUE' && @validators > 1;
  0         0  
1436             }
1437            
1438             elsif ( defined $v )
1439             {
1440 104 100 100     387 croak "invalid validator '$v': must be a code ref"
1441             unless ref $v && reftype $v eq 'CODE';
1442            
1443 102         101 push @{$rr->{validators}}, $v;
  102         205  
1444             }
1445             }
1446            
1447 157 100 100     344 $rr->{$type} = 1 if $type eq 'optional' || $type eq 'mandatory';
1448            
1449 157 100       184 if ( $type eq 'param' )
1450             {
1451 120 50       171 push @{$rs->{fulfill_order}}, $typevalue unless $rs->{params}{$typevalue};
  120         161  
1452             }
1453            
1454 157         224 $rs->{params}{$typevalue} = 1;
1455            
1456             # If a default value was given, run it through all of the validators in turn
1457             # until it passes one of them. Store the resulting clean value. If the
1458             # default does not pass any of the validators, throw an exception.
1459            
1460 157 100       284 if ( defined $rr->{default} )
1461             {
1462 3 50       5 croak "default value must be a scalar\n" if ref $rr->{default};
1463            
1464             next RULE unless ref $rr->{validators} eq 'ARRAY' &&
1465 3 100 66     9 @{$rr->{validators}};
  2         12  
1466            
1467 2         3 foreach my $v ( @{$rr->{validators}} )
  2         2  
1468             {
1469 2         5 my $result = $v->($rr->{default}, {});
1470            
1471 2 50       5 next RULE unless defined $result;
1472            
1473 2 100       5 if ( exists $result->{value} )
1474             {
1475 1         1 $rr->{default} = $result->{value};
1476 1 50       2 croak "cleaned default value must be a scalar\n" if ref $rr->{default};
1477 1         3 next RULE;
1478             }
1479             }
1480            
1481 1         163 croak "the default value '$rr->{default}' failed all of the validators\n";
1482             }
1483             }
1484            
1485             elsif ( $CATEGORY{$type} eq 'modifier' )
1486             {
1487 6         8 $rr->{type} = $type;
1488 6         8 $rr->{param} = [];
1489            
1490 6 100       16 my @params = ref $typevalue eq 'ARRAY' ? @$typevalue : $typevalue;
1491            
1492 6         22 foreach my $arg (@params)
1493             {
1494 11         12 push @{$rr->{param}}, $arg;
  11         14  
1495             }
1496            
1497             croak "a rule of type '$type' requires at least one parameter name"
1498 6 50       34 unless @{$rr->{param}} > 0;
  6         16  
1499             }
1500            
1501             elsif ( $CATEGORY{$type} eq 'include' )
1502             {
1503 33         69 $rr->{type} = 'include';
1504 33 100       60 $rr->{require} = 1 if $type eq 'require';
1505 33         36 $rr->{ruleset} = $typevalue;
1506            
1507 33 100 100     303 croak "the value of '$type' must be a ruleset name"
      66        
1508             unless defined $typevalue && !ref $typevalue && $typevalue ne '';
1509            
1510 31 100       195 croak "ruleset '$typevalue' not found" unless defined $self->{RULESETS}{$typevalue};
1511            
1512 30         56 $rs->{includes}{$typevalue} = 1;
1513            
1514             # Every ruleset that was included in the named ruleset is also
1515             # recursively included in this one.
1516            
1517 30         29 my $included_rs = $self->{RULESETS}{$typevalue};
1518            
1519 30 50       53 if ( ref $included_rs->{includes} eq 'HASH' )
1520             {
1521 30         23 foreach my $key ( keys %{$included_rs->{includes}} )
  30         68  
1522             {
1523 0         0 $rs->{includes}{$key} = 1;
1524             }
1525             }
1526             }
1527            
1528             elsif ( $CATEGORY{$type} eq 'constraint' )
1529             {
1530 10         16 $rr->{type} = 'constraint';
1531 10         13 $rr->{constraint} = $type;
1532 10         12 $rr->{ruleset} = [];
1533            
1534 10 50 33     53 croak "the value of '$type' must be a list of ruleset names"
1535             unless defined $typevalue && ref $typevalue eq 'ARRAY';
1536            
1537 10         13 foreach my $arg (@$typevalue)
1538             {
1539 20 50 33     48 next unless defined $arg && $arg ne '';
1540            
1541             croak "ruleset '$arg' was not included by any rule"
1542 20 50       29 unless defined $rs->{includes}{$arg};
1543            
1544 20         14 push @{$rr->{ruleset}}, $arg;
  20         26  
1545             }
1546            
1547             croak "a rule of type '$type' requires at least one ruleset name"
1548 10 50       20 unless @{$rr->{ruleset}} > 0;
  10         20  
1549             }
1550            
1551             elsif ( $type eq 'content_type' )
1552             {
1553 4         8 $rr->{type} = 'content_type';
1554 4         7 $rr->{param} = $typevalue;
1555            
1556 4         6 my %map;
1557            
1558 4 50 33     41 croak "invalid parameter name '$typevalue'" if ref $typevalue || $typevalue !~ /\w/;
1559            
1560 4 50       13 my @types = ref $rule->{valid} eq 'ARRAY' ? @{$rule->{valid}} : $rule->{valid};
  4         10  
1561            
1562 4         7 foreach my $t (@types)
1563             {
1564 10 50       16 if ( $t eq '' )
1565             {
1566 0         0 carp "ignored empty value '$t' for 'content_type'";
1567 0         0 next;
1568             }
1569            
1570 10         32 my ($short, $long) = split /\s*=\s*/, $t;
1571 10   100     35 $long ||= $MEDIA_TYPE{$short};
1572            
1573 10 100       123 croak "unknown content type for '$short': you must specify a full content " .
1574             "type with '$short=some/type'" unless $long;
1575            
1576 9 50       17 croak "type '$short' cannot be specified twice" if defined $rr->{type_map}{$short};
1577            
1578 9         13 $rr->{type_map}{$short} = $long;
1579 9         8 push @{$rr->{type_list}}, $short;
  9         18  
1580             }
1581            
1582 3 50       11 croak "you must specify at least one value for 'content_type'" unless $rr->{type_map};
1583             }
1584            
1585             else
1586             {
1587 0         0 croak "invalid rule type '$type'\n";
1588             }
1589             }
1590            
1591             # If we have documentation strings collected up, then they belong to the
1592             # last-defined rule. So call add_doc to close any pending lists.
1593            
1594 77 100       134 $self->add_doc($rs, $doc_rule, @doc_lines) if @doc_lines;
1595             }
1596              
1597              
1598             # add_doc ( ruleset, rule_record, line... )
1599             #
1600             # Add the specified documentation lines to the specified ruleset. If
1601             # $rule_record is defined, it represents the rule to which this documentation
1602             # applies. Otherwise, the documentation represents header material to be
1603             # output before the documentation for the first rule. If the beginning of the
1604             # first documentation line is '!', then return without doing anything.
1605             #
1606             # Any line starting with = is, of course, taken to indicate a Pod command
1607             # paragraph. It will be preceded and followed by a blank line.
1608             #
1609             # If $rule_record is undefined, then close any pending lists and do nothing
1610             # else.
1611              
1612             sub add_doc {
1613              
1614 212     212 0 283 my ($self, $rs, $rr, @lines) = @_;
1615            
1616             # Don't do anything unless we were given either a rule record or some
1617             # documentation or both.
1618            
1619 212 100 100     392 return unless defined($rr) || @lines;
1620            
1621             # If the first documentation line starts with !, return without doing
1622             # anything. That character indicates that this rule should not be
1623             # documented.
1624            
1625 130 100 100     211 return if @lines && $lines[0] =~ /^[!]/;
1626            
1627             # Similarly, return without doing anything if the rule contains the
1628             # 'undocumented' attribute."
1629            
1630 129 100 100     273 return if defined $rr && $rr->{undocumented};
1631            
1632             # Otherwise, put the documentation lines together into a single string
1633             # (which may contain a series of POD paragraphs).
1634            
1635 128         104 my $body = '';
1636 128         117 my $last_pod;
1637             my $this_pod;
1638            
1639 128         172 foreach my $line (@lines)
1640             {
1641             # If this line starts with =, then it needs extra spacing.
1642            
1643 15         43 my $this_pod = $line =~ qr{ ^ = }x;
1644            
1645             # If $body already has something in it, add a newline first. Add
1646             # two if this line starts with =, or if the previously added line
1647             # did, so that we get a new paragraph.
1648            
1649 15 100       27 if ( $body ne '' )
1650             {
1651 4 50 33     14 $body .= "\n" if $last_pod || $this_pod;
1652 4         5 $body .= "\n";
1653             }
1654            
1655 15         23 $body .= $line;
1656 15         21 $last_pod = $this_pod;
1657             }
1658            
1659             # Then add the documentation to the ruleset record:
1660            
1661             # If there is no attached rule, then we add the body as an ordinary paragraph.
1662            
1663 128 100 66     345 unless ( defined $rr )
    100          
1664             {
1665 5         5 push @{$rs->{doc_items}}, "=ORDINARY";
  5         8  
1666 5 50       10 push @{$rs->{doc_items}}, process_doc($body) if defined $body;
  5         12  
1667             }
1668            
1669             # If the indicated rule is a parameter rule, then add its record to the list.
1670            
1671 0 50       0 elsif ( defined $rr and $rr->{type} eq 'param' )
1672             {
1673 97         78 push @{$rs->{doc_items}}, $rr;
  97         147  
1674 97         110 weaken $rs->{doc_items}[-1];
1675 97 50       119 if ( defined $body )
1676             {
1677 97         77 push @{$rs->{doc_items}}, process_doc($body, 1);
  97         138  
1678 97         136 $rr->{doc_ref} = \$rs->{doc_items}[-1];
1679 97         164 weaken $rr->{doc_ref};
1680             }
1681             }
1682            
1683             # If this is an include rule, then we add a special line to include the
1684             # specified ruleset(s).
1685            
1686             elsif ( defined $rr and $rr->{type} eq 'include' )
1687             {
1688             push @{$rs->{doc_items}}, "=INCLUDE $rr->{ruleset}";
1689            
1690             # If any body text was specified, then add it as an ordinary paragraph
1691             # after the inclusion.
1692            
1693             if ( $body ne '' )
1694             {
1695             push @{$rs->{doc_items}}, "=ORDINARY";
1696             push @{$rs->{doc_items}}, process_doc($body) if defined $body;
1697             }
1698             }
1699             }
1700              
1701              
1702             # process_doc ( docstring, item_body )
1703             #
1704             # Make sure that the indicated string is valid POD. In particular, if there
1705             # are any unclosed =over sections, close them at the end. Throw an exception
1706             # if we find an =item before the first =over or a =head inside an =over.
1707              
1708             sub process_doc {
1709              
1710 103     103 0 122 my ($docstring, $is_item_body) = @_;
1711            
1712 103         94 my ($list_level) = 0;
1713            
1714 103         154 while ( $docstring =~ / ^ (=[a-z]+) /gmx )
1715             {
1716 0 0       0 if ( $1 eq '=over' )
    0          
    0          
    0          
1717             {
1718 0         0 $list_level++;
1719             }
1720            
1721             elsif ( $1 eq '=back' )
1722             {
1723 0         0 $list_level--;
1724 0 0       0 croak "invalid POD string: =back does not match any =over" if $list_level < 0;
1725             }
1726            
1727             elsif ( $1 eq '=item' )
1728             {
1729 0 0       0 croak "invalid POD string: =item outside of =over" if $list_level == 0;
1730             }
1731            
1732             elsif ( $1 eq '=head' )
1733             {
1734 0 0 0     0 croak "invalid POD string: =head inside =over" if $list_level > 0 or $is_item_body;
1735             }
1736             }
1737            
1738 103         177 return $docstring, ('=back') x $list_level;
1739             }
1740              
1741              
1742             # generate_docstring ( ruleset )
1743             #
1744             # Generate the documentation string for the specified ruleset, recursively
1745             # evaluating all of the rulesets it includes. This will generate a series of
1746             # flat top-level lists describing all of the various parameters, potentially
1747             # with non-list paragraphs in between.
1748              
1749             sub generate_docstring {
1750              
1751 6     6 0 9 my ($self, $rs, $state) = @_;
1752            
1753             # Make sure that we process each ruleset only once, even if it is included
1754             # multiple times. Also keep track of our recursion level.
1755            
1756 6 50       15 return '' if $state->{processed}{$rs->{name}};
1757            
1758 6         11 $state->{processed}{$rs->{name}} = 1;
1759 6         7 $state->{level}++;
1760            
1761             # Start with an empty string. If there are no doc_items for this
1762             # ruleset, just return that.
1763            
1764 6         7 my $doc = '';
1765            
1766 6 50 33     22 return $doc unless ref $rs && ref $rs->{doc_items} eq 'ARRAY';
1767            
1768             # Go through each docstring, treating it as a POD paragraph. That means
1769             # that they will be separated from each other by a blank line.
1770            
1771 6         8 foreach my $item ( @{$rs->{doc_items}} )
  6         10  
1772             {
1773             # An item record starts a list if not already in one.
1774            
1775 38 100 66     174 if ( ref $item && defined $item->{param} )
    100          
    100          
1776             {
1777 10 100       18 unless ( $state->{in_list} )
1778             {
1779 5 100       9 $doc .= "\n\n" if $doc ne '';
1780 5         8 $doc .= "=over";
1781 5         6 $state->{in_list} = 1;
1782             }
1783            
1784 10         32 $doc .= "\n\n=item $item->{param}";
1785             }
1786            
1787             # A string starting with =ORDINARY closes any current list.
1788            
1789             elsif ( $item =~ qr{ ^ =ORDINARY }x )
1790             {
1791 8 100       17 if ( $state->{in_list} )
1792             {
1793 3 50       6 $doc .= "\n\n" if $doc ne '';
1794 3         3 $doc .= "=back";
1795 3         6 $state->{in_list} = 0;
1796             }
1797             }
1798            
1799             # A string starting with =INCLUDE inserts the specified ruleset.
1800            
1801             elsif ( $item =~ qr{ ^ =INCLUDE \s* (.*) }xs )
1802             {
1803 2         5 my $included_rs = $self->{RULESETS}{$1};
1804            
1805 2 50       5 if ( ref $included_rs eq 'HTTP::Validate::Ruleset' )
1806             {
1807 2         13 my $subdoc = $self->generate_docstring($included_rs, $state);
1808            
1809 2 50 33     8 $doc .= "\n\n" if $doc ne '' && $subdoc ne '';
1810 2 50       12 $doc .= $subdoc if $subdoc ne '';
1811             }
1812             }
1813            
1814             # All other strings are added as-is.
1815            
1816             else
1817             {
1818 18 100 100     45 $doc .= "\n\n" if $doc ne '' && $item ne '';
1819 18         34 $doc .= $item;
1820             }
1821             }
1822            
1823             # If we get to the end of the top-level ruleset and we are still in a
1824             # list, close it. Also make sure that our resulting documentation string
1825             # ends with a newline.
1826            
1827 6 100       9 if ( --$state->{level} == 0 )
1828             {
1829 4 100       9 $doc .= "\n\n=back" if $state->{in_list};
1830 4         5 $state->{in_list} = 0;
1831 4         5 $doc .= "\n";
1832             }
1833            
1834 6         15 return $doc;
1835             }
1836              
1837              
1838             # generate_param_list ( ruleset )
1839             #
1840             # Generate a list of unique parameter names for the ruleset and its included
1841             # rulesets if any.
1842              
1843             sub generate_param_list {
1844            
1845 3     3 0 3 my ($self, $rs_name, $uniq) = @_;
1846            
1847 3   100     7 $uniq ||= {};
1848            
1849 3 50       5 return if $uniq->{$rs_name}; $uniq->{$rs_name} = 1;
  3         4  
1850            
1851 3         2 my @params;
1852            
1853 3         4 foreach my $rule ( @{$self->{RULESETS}{$rs_name}{rules}} )
  3         5  
1854             {
1855 7 100       9 if ( $rule->{type} eq 'param' )
    50          
1856             {
1857 5         6 push @params, $rule->{param};
1858             }
1859            
1860             elsif ( $rule->{type} eq 'include' )
1861             {
1862 2         8 push @params, $self->generate_param_list($rule->{ruleset}, $uniq);
1863             }
1864             }
1865            
1866 3         10 return @params;
1867             }
1868              
1869              
1870             # generate_rule_list ( ruleset )
1871             #
1872             # Generate a list of the rules contained in this ruleset and its included
1873             # rulesets if any.
1874              
1875             sub generate_rule_list {
1876              
1877 2     2 0 3 my ($self, $rs_name, $uniq) = @_;
1878              
1879 2   100     5 $uniq ||= {};
1880              
1881 2 50       4 return if $uniq->{$rs_name}; $uniq->{$rs_name} = 1;
  2         3  
1882            
1883 2         1 my @rules;
1884            
1885 2         5 foreach my $item ( $self->{RULESETS}{$rs_name}{rules}->@* )
1886             {
1887 4 100 66     13 if ( ref $item && $item->{type} eq 'include' )
    50          
1888             {
1889 1         4 push @rules, $self->generate_rule_list($item->{ruleset}, $uniq);
1890             }
1891              
1892             elsif ( ref $item eq 'HASH' )
1893             {
1894 3         8 push @rules, { %$item };
1895             }
1896             }
1897            
1898 2         19 return @rules;
1899             }
1900              
1901              
1902             # new_execution ( context, params )
1903             #
1904             # Create a new validation-execution control record, using the given context
1905             # and input parameters.
1906              
1907             sub new_execution {
1908            
1909 78     78 0 122 my ($self, $context, $input_params) = @_;
1910            
1911             # First check the types of the arguments to this function.
1912            
1913 78 100 66     490 croak "the second parameter to check_params() must be a hashref if defined"
      33        
1914             if defined $context && (!ref $context || reftype $context ne 'HASH');
1915            
1916 77   50     157 $context ||= {};
1917            
1918 77 100       202 croak "the third parameter to check_params() must be a hashref or listref"
1919             unless ref $input_params;
1920            
1921             # If the parameters were given as a hashref, just use it straight.
1922            
1923 76         82 my $unpacked_params = {};
1924            
1925 76 100       172 if ( reftype $input_params eq 'HASH' )
    50          
1926             {
1927 40         90 %$unpacked_params = %$input_params;
1928             }
1929            
1930             # If the parameters were given as a listref, we need to look for hashrefs
1931             # at the front.
1932            
1933             elsif ( reftype $input_params eq 'ARRAY' )
1934             {
1935             # Look for hashrefs at the beginning of the list and unpack them.
1936              
1937 36         67 my @input_params = @$input_params;
1938            
1939 36   66     75 while ( ref $input_params[0] && reftype $input_params[0] eq 'HASH' )
1940             {
1941 4         5 my $p = shift @input_params;
1942            
1943 4         9 foreach my $x (keys %$p)
1944             {
1945 7         11 add_param($unpacked_params, $x, $p->{$x});
1946             }
1947             }
1948            
1949             # All other items must be name/value pairs.
1950            
1951 36         53 while ( @input_params )
1952             {
1953 96         86 my $p = shift @input_params;
1954            
1955 96 50       102 if ( ref $p )
1956             {
1957 0         0 croak "invalid parameter '$p'";
1958             }
1959            
1960             else
1961             {
1962 96         114 add_param($unpacked_params, $p, shift @input_params);
1963             }
1964             }
1965             }
1966            
1967             # Anything else is invalid.
1968            
1969             else
1970             {
1971 0         0 croak "the third parameter to check_params() must be a hashref or listref";
1972             }
1973            
1974             # Now create a new validation record
1975            
1976 76         69 my %settings = %{$self->{SETTINGS}};
  76         227  
1977            
1978 76         331 my $vr = { raw => $unpacked_params, # the raw parameters and values
1979             clean => { }, # the parameter keys and values
1980             clean_list => [ ], # the parameter keys in order of recognition
1981             context => $context, # context for the validators to use
1982             ps => { }, # the status (failed=0, passed=1, ignored=undef) of each parameter
1983             rs => { }, # the status (checked=1, fulfilled=2) of each ruleset
1984             settings => \%settings, # a copy of our current settings
1985             };
1986            
1987 76         208 return bless $vr, 'HTTP::Validate::Progress';
1988             }
1989              
1990              
1991             sub add_param {
1992              
1993 103     103 0 115 my ($hash, $param, $value) = @_;
1994            
1995             # If there is already more than one value for this parameter, add the new
1996             # value(s) to the array ref.
1997            
1998 103 100 66     284 if ( ref $hash->{$param} && reftype $hash->{$param} eq 'ARRAY' )
    100 100        
1999             {
2000 1 50 33     2 push @{$hash->{$param}},
  1         6  
2001             (ref $value && reftype $value eq 'ARRAY' ? @$value : $value);
2002             }
2003            
2004             # If there is already one value for this parameter, turn it into an array
2005             # ref.
2006            
2007             elsif ( defined $hash->{$param} && $hash->{$param} ne '' )
2008             {
2009 4 50 33     25 $hash->{$param} = [$hash->{$param},
2010             (ref $value && reftype $value eq 'ARRAY' ? @$value : $value)];
2011             }
2012            
2013             # Otherwise, set the value for this parameter to be the new value (which
2014             # could be either a scalar or a reference).
2015            
2016             else
2017             {
2018 98         192 $hash->{$param} = $value;
2019             }
2020             }
2021              
2022              
2023             # execute_validation ( validation_record, ruleset_name )
2024             #
2025             # This function performs a validation using the given validation-progress
2026             # record, starting with the given ruleset, and returns a hash with the
2027             # results.
2028              
2029             sub execute_validation {
2030              
2031 76     76 0 96 my ($self, $vr, $ruleset_name) = @_;
2032            
2033 76 50 33     207 croak "you must provide a ruleset name" unless defined $ruleset_name && $ruleset_name ne '';
2034 76 50 33     315 croak "invalid ruleset name: '$ruleset_name'" if ref $ruleset_name || $ruleset_name !~ /\w/;
2035            
2036             # First perform the specified validation against the specified ruleset.
2037             # This may trigger validations against additional rulesets if the intial
2038             # one contains 'allow' or 'require' rules.
2039            
2040 76         134 $self->validate_ruleset($vr, $ruleset_name);
2041            
2042             # Now, if this ruleset was not fulfilled, add an appropriate error
2043             # message.
2044            
2045 75 100       125 if ( $vr->{rs}{$ruleset_name} != 2 )
2046             {
2047 1         2 my @names = @{$self->{RULESETS}{$ruleset_name}{fulfill_order}};
  1         2  
2048 1 50       3 my $msg = @names == 1 ? 'ERR_REQ_SINGLE': 'ERR_REQ_MULT';
2049 1         4 add_error($vr, { key => $ruleset_name }, $msg, { param => \@names });
2050             }
2051            
2052             # Create an object to hold the result of this function.
2053            
2054 75         154 my $result = bless {}, 'HTTP::Validate::Result';
2055            
2056             # Add the clean-value hash and the raw-value hash
2057            
2058 75         134 $result->{clean} = $vr->{clean};
2059 75         79 $result->{clean_list} = $vr->{clean_list};
2060 75         79 $result->{raw} = $vr->{raw};
2061            
2062             # Put the clean-value hash under the old name, for backward compatibility
2063             # (it will be eventually removed).
2064            
2065 75         85 $result->{values} = $vr->{clean};
2066            
2067             # Add the content type, if one was specified.
2068            
2069             $result->{content_type} = $vr->{content_type}
2070             if defined $vr->{content_type} and
2071             $vr->{content_type} ne '' and
2072 75 100 66     132 $vr->{content_type} ne 'unknown';
      100        
2073            
2074             # Add any errors that were generated.
2075            
2076 75         69 $result->{ec} = $vr->{ec};
2077 75         99 $result->{er} = $vr->{er};
2078 75         110 $result->{wc} = $vr->{wc};
2079 75         78 $result->{wn} = $vr->{wn};
2080 75         71 $result->{ig} = $vr->{ig};
2081            
2082             # Unless the 'ignore_unrecognized' setting was specified, check for unrecognized
2083             # parameters, and generate errors or warnings for them.
2084            
2085 75 100       133 return $result if $self->{SETTINGS}{ignore_unrecognized};
2086            
2087 66         56 foreach my $key (keys %{$vr->{raw}})
  66         155  
2088             {
2089 145 100 66     210 next if exists $vr->{ps}{$key} or exists $vr->{ig}{$key};
2090            
2091 4 100       13 if ( $self->{SETTINGS}{permissive} )
2092             {
2093 2         2 unshift @{$result->{wn}}, [$key, "unknown parameter '$key'"];
  2         6  
2094 2         3 $result->{wc}{$key}++;
2095             }
2096             else
2097             {
2098 2         2 unshift @{$result->{er}}, [$key, "unknown parameter '$key'"];
  2         11  
2099 2         4 $result->{ec}{$key}++;
2100             }
2101             }
2102            
2103             # Now return the result object.
2104            
2105 66         314 return $result;
2106             }
2107              
2108              
2109             # This function does the actual work of validating. It takes two parameters:
2110             # a validation record and a ruleset name. It sets various subfields of the
2111             # validation record according to the results of the validation.
2112              
2113             sub validate_ruleset {
2114              
2115 99     99 0 106 my ($self, $vr, $ruleset_name) = @_;
2116            
2117 99 50       120 die "Missing ruleset" unless defined $ruleset_name;
2118            
2119 99         124 my $rs = $self->{RULESETS}{$ruleset_name};
2120            
2121             # Throw an error if this ruleset does not exist.
2122            
2123 99 100       209 croak "Unknown ruleset '$ruleset_name'" unless ref $rs;
2124            
2125             # Return immediately if we have already visited this ruleset. Otherwise,
2126             # mark it as visited.
2127            
2128 98 50       153 return if exists $vr->{rs}{$ruleset_name};
2129 98         142 $vr->{rs}{$ruleset_name} = 1;
2130            
2131             # Mark the ruleset as fulfilled if it has no non-optional parameters.
2132            
2133 98 100 66     177 $vr->{rs}{$ruleset_name} = 2 unless ref $rs->{fulfill_order} && @{$rs->{fulfill_order}};
  98         199  
2134            
2135             # Now check all of the rules in this ruleset against the parameter values
2136             # stored in $vr->{raw}.
2137            
2138             RULE:
2139 98         111 foreach my $rr (@{$rs->{rules}})
  98         138  
2140             {
2141 260         270 my $type = $rr->{type};
2142 260         259 my $param = $rr->{param};
2143 260   100     422 my $key = $rr->{key} || $param;
2144 260         201 my $default_used;
2145            
2146             # To evaluate a rule of type 'param' we check to see if a
2147             # corresponding parameter was specified.
2148            
2149 260 100 100     381 if ( $type eq 'param' )
    100          
    100          
    100          
    100          
    50          
2150             {
2151 214         189 my (%names_found, @names_found, @raw_values, $empty_string);
2152            
2153             # Skip this rule if a previous 'ignore' was encountered.
2154            
2155 214 50       291 next RULE if $vr->{ig}{$key};
2156            
2157             # Otherwise check to see if the parameter or any of its aliases were specified. If
2158             # so, then collect up their values.
2159            
2160 214         199 foreach my $name ( $rr->{param}, @{$rr->{alias}} )
  214         295  
2161             {
2162 222 100       291 next unless exists $vr->{raw}{$name};
2163            
2164 150         163 $names_found{$name} = 1;
2165            
2166 150         163 my $v = $vr->{raw}{$name};
2167            
2168 150 100       183 foreach my $subv ( ref $v eq 'ARRAY' ? @$v : $v )
2169             {
2170 155 100 100     324 if ( defined $subv && $subv ne '' )
    100 66        
2171             {
2172 140         181 push @raw_values, $subv;
2173             }
2174            
2175             elsif ( defined $subv && $subv eq '' )
2176             {
2177 12         13 $empty_string = 1;
2178             }
2179             }
2180            
2181             # Make sure this parameter exists in {ps}, but don't
2182             # change its status if any.
2183            
2184 150 50       290 $vr->{ps}{$name} = undef unless exists $vr->{ps}{$name};
2185             }
2186            
2187             # If more than one of the aliases for this parameter was specified, and the 'multiple'
2188             # option was not specified, then generate an error and go on to the next rule. We
2189             # mark the parameter status as "error" (0), and we also mark the ruleset as fulfilled
2190             # (2) if this was a 'param' rule. This last is done to avoid generating a spurious
2191             # error message if the ruleset is not fulfilled by any other parameters.
2192            
2193 214 100 66     775 if ( keys(%names_found) > 1 && ! $rr->{multiple} )
    50 66        
    100 33        
    100 100        
2194             {
2195 1         8 add_error($vr, $rr, 'ERR_MULT_NAMES', { param => [ sort keys %names_found ] });
2196 1         4 $vr->{ps}{$param} = 0;
2197 1 50 33     7 $vr->{rs}{$ruleset_name} = 2 unless $rr->{optional} || $rr->{mandatory};
2198 1         3 next RULE;
2199             }
2200            
2201             # If a clean value has already been determined for this parameter, then it was already
2202             # recognized by some other rule. Consequently, this rule can be ignored.
2203            
2204             elsif ( exists $vr->{clean}{$key} )
2205             {
2206 0         0 next RULE;
2207             }
2208            
2209             # If no values were specified for this parameter, check to see if the rule includes a
2210             # default value. If so, use that instead and go on to the next rule.
2211            
2212             elsif ( ! @raw_values && exists $rr->{default} )
2213             {
2214 1         3 $vr->{clean}{$key} = $rr->{default};
2215 1         1 push @{$vr->{clean_list}}, $key;
  1         2  
2216 1         3 next RULE;
2217             }
2218            
2219             # If more than one value was given and the rule does not include any of the
2220             # directives 'multiple', 'split', or 'list', signal an error. We mark the
2221             # parameter status as "error" (0), and we also mark the ruleset as fulfilled
2222             # (2) if this was a 'param' rule. This last is done to avoid generating a
2223             # spurious error message if the ruleset is not fulfilled by any other
2224             # parameters.
2225            
2226             elsif ( @raw_values > 1 && ! ($rr->{multiple} || $rr->{split} || $rr->{list}) )
2227             {
2228 2         11 add_error($vr, $rr, 'ERR_MULT_VALUES',
2229             { param => [ sort keys %names_found ], value => \@raw_values });
2230 2         27 $vr->{ps}{$param} = 0;
2231 2 50 33     11 $vr->{rs}{$ruleset_name} = 2 unless $rr->{optional} || $rr->{mandatory};
2232 2         40 next RULE;
2233             }
2234            
2235             # Now we can process the rule. If the 'split' directive was
2236             # given, split the value(s) using the specified regexp.
2237            
2238 210 100       273 if ( $rr->{split} )
2239             {
2240             # Split all of the raw string values, and discard empty strings.
2241             # Unpack arrayrefs into individual values. If there are raw
2242             # values but not any split values, we take the value to be the
2243             # empty string.
2244            
2245 22 50       79 my @new_values = grep { defined $_ && $_ ne '' }
2246 20         25 map { split $rr->{split}, $_ } @raw_values;
  9         60  
2247 20 100 100     41 $empty_string = 1 if @raw_values && ! @new_values;
2248 20         28 @raw_values = @new_values;
2249             }
2250            
2251             # If this is a 'flag' parameter and the parameter was present but
2252             # no values were given, assume the value '1'.
2253            
2254 210 100 100     306 if ( $rr->{flag} && keys(%names_found) && ! @raw_values )
      66        
2255             {
2256 2         4 @raw_values = (1);
2257             }
2258            
2259             # At this point, if there are no values then generate an error if
2260             # the parameter is mandatory. Otherwise just skip this rule unless
2261             # this rule has the 'allow_empty' attribute and at least one of the
2262             # parameter names was specified.
2263            
2264 210 100       211 unless ( @raw_values )
2265             {
2266 78 100       87 if ( $rr->{mandatory} )
2267             {
2268 2         8 add_error($vr, $rr, 'ERR_MANDATORY', { param => $rr->{param} });
2269 2         4 $vr->{ps}{$param} = 0;
2270             }
2271            
2272 78 100 100     196 next RULE unless $rr->{allow_empty} && keys %names_found;
2273             }
2274            
2275             # Now indicate that at least one value was found for this
2276             # parameter, even though we don't yet know if it is a good one.
2277             # This will be necessary for properly handling 'together' and
2278             # 'at_most_one' rules.
2279            
2280 134         165 $vr->{clean}{$key} = undef;
2281            
2282             # Now we process each value in turn.
2283            
2284 134         120 my @clean_values;
2285             my $error_flag;
2286            
2287             VALUE:
2288 134         127 foreach my $raw_val ( @raw_values )
2289             {
2290             # If no validators were defined, just pass all of the values
2291             # that are not empty.
2292            
2293 146 100       229 unless ( $rr->{validators} )
2294             {
2295 47 50 33     83 if ( defined $raw_val && $raw_val ne '' )
2296             {
2297 47 100       112 $raw_val = $rr->{cleaner}($raw_val) if ref $rr->{cleaner} eq 'CODE';
2298 47         49 push @clean_values, $raw_val;
2299             }
2300            
2301 47         57 next VALUE;
2302             }
2303            
2304             # Otherwise, check each value against the validators in turn until
2305             # one of them passes the value or until we have tried them
2306             # all.
2307            
2308 99         75 my $result;
2309            
2310             VALIDATOR:
2311 99         73 foreach my $validator ( @{$rr->{validators}} )
  99         102  
2312             {
2313 99         164 $result = $validator->($raw_val, $vr->{context});
2314            
2315             # If the result is not a hash ref, then the value passes
2316             # the test.
2317            
2318 99 100 66     235 last VALIDATOR unless ref $result && reftype $result eq 'HASH';
2319            
2320             # If the result contains an 'error' key, then we need to
2321             # try the next validator (if any). Otherwise, the value
2322             # passes the test.
2323            
2324 95 100       146 last VALIDATOR unless $result->{error};
2325             }
2326            
2327             # If the last validator to be tried generated an error, then
2328             # the value is bad. We must report it and skip to the next value.
2329            
2330 99 100 100     216 if ( ref $result and $result->{error} )
2331             {
2332             # If the rule contains a 'warn' directive, then generate a warning.
2333             # But the value is still bad, and will be ignored.
2334            
2335 27 100       58 if ( $rr->{warn} )
2336             {
2337             my $msg = $rr->{warn} ne '1' ? $rr->{warn} :
2338 8 50 33     47 $rr->{ERR_INVALID} || $rr->{errmsg} || $result->{error};
2339 8         28 add_warning($vr, $rr, $msg, { param => [ keys %names_found ],
2340             value => $raw_val });
2341             }
2342            
2343             # Otherwise, generate an error.
2344            
2345             else
2346             {
2347 19   33     76 my $msg = $rr->{ERR_INVALID} || $rr->{errmsg} || $result->{error};
2348 19         94 add_error($vr, $rr, $msg, { param => [ sort keys %names_found ],
2349             value => $raw_val });
2350             }
2351            
2352 27         56 $error_flag = 1;
2353 27         44 next VALUE;
2354             }
2355            
2356             # If the result contains a 'warn' field, then generate a warning. In
2357             # this case, the value is still assumed to be good.
2358            
2359 72 100 100     148 if ( ref $result and $result->{warn} )
2360             {
2361 1         6 add_warning($vr, $rr, $result->{warn}, { param => [ sort keys %names_found ],
2362             value => $raw_val });
2363             }
2364            
2365             # If we get here, then the value is good. If the result was a hash ref
2366             # with a 'value' field, we use that for the clean value. Otherwise, we
2367             # use the raw value.
2368            
2369 72 100 100     151 my $value = ref $result && exists $result->{value} ? $result->{value} : $raw_val;
2370            
2371             # If a cleaning subroutine was defined, pass the value through
2372             # it and save the cleaned value.
2373            
2374 72 50       95 $value = $rr->{cleaner}($value) if ref $rr->{cleaner} eq 'CODE';
2375            
2376 72         119 push @clean_values, $value;
2377             }
2378            
2379             # If no raw values were found, and this rule has the 'allow_empty'
2380             # attribute, add the empty string or the undefined value depending
2381             # on $empty_string.
2382            
2383 134 100 100     215 if ( $rr->{allow_empty} && ! @raw_values )
2384             {
2385 2 100       5 push @clean_values, $empty_string ? '' : undef;
2386             }
2387            
2388             # If clean values were found, store them. If multiple values are
2389             # allowed, then we store them as a list. Otherwise, there should
2390             # only be one clean value and so we just store it as a scalar.
2391            
2392 134 100       143 if ( @clean_values )
2393             {
2394 111         88 push @{$vr->{clean_list}}, $key;
  111         164  
2395            
2396 111 100       170 if ( $rr->{multiple} )
2397             {
2398 8         10 $vr->{clean}{$key} = \@clean_values;
2399             }
2400            
2401             else
2402             {
2403 103         118 $vr->{clean}{$key} = $clean_values[0];
2404             }
2405             }
2406            
2407             # If raw values were found for this parameter, but none of them
2408             # pass the validators, then we need to indicate this condition.
2409            
2410             else
2411             {
2412 23         18 push @{$vr->{clean_list}}, $key;
  23         32  
2413            
2414 23 100 100     51 if ( defined $rr->{bad_value} && $rr->{bad_value} eq 'ERROR' )
    100          
2415             {
2416 2         11 add_error($vr, $rr, 'ERR_BAD_VALUES',
2417             { param => [ sort keys %names_found ], value => \@raw_values });
2418 2         4 $vr->{clean}{$key} = undef;
2419 2         3 $error_flag = 1;
2420             }
2421            
2422             elsif ( defined $rr->{bad_value} )
2423             {
2424 1 50       3 $vr->{clean}{$key} = $rr->{multiple} ? [ $rr->{bad_value} ] : $rr->{bad_value};
2425             }
2426            
2427             else
2428             {
2429 20         26 $vr->{clean}{$key} = undef;
2430             }
2431             }
2432            
2433             # Set the status of this parameter to 1 (passed) unless an error
2434             # was generated, 0 (failed) otherwise.
2435            
2436 134 100       181 $vr->{ps}{$param} = $error_flag ? 0 : 1;
2437            
2438             # If this rule is not 'optional' or 'mandatory', then set the status of this
2439             # ruleset to 'fulfilled' (2). That does not mean that the validation passes,
2440             # because the parameter value may still have generated an error.
2441            
2442 134 100 100     331 unless ( $rr->{optional} || $rr->{mandatory} )
2443             {
2444 98         231 $vr->{rs}{$ruleset_name} = 2;
2445             }
2446             }
2447            
2448             # An 'ignore' directive causes the parameter to be recognized, but no cleaned
2449             # value is generated and the containing ruleset is not triggered. No error
2450             # messages will be generated for this parameter, either.
2451            
2452             elsif ( $rr->{type} eq 'ignore' )
2453             {
2454             # Make sure that the parameter is counted as having been recognized.
2455            
2456 1         1 foreach my $param ( @{$rr->{param}} )
  1         2  
2457             {
2458 2         3 $vr->{ps}{$param} = undef;
2459            
2460             # Make sure that errors, warnings, and cleaned values for this key
2461             # are ignored.
2462            
2463 2   33     5 my $key = $rr->{key} || $param;
2464 2         4 $vr->{ig}{$key} = 1;
2465 2         3 delete $vr->{clean}{$param};
2466             }
2467             }
2468            
2469             # A 'together' or 'at_most_one' rule requires checking the presence
2470             # of each of the specified parameters. This kind of rule does not
2471             # affect the status of any parameters or rulesets, but if violated
2472             # will generate an error message and cause the entire validation to
2473             # fail.
2474            
2475             elsif ( $rr->{type} eq 'together' or $rr->{type} eq 'at_most_one' )
2476             {
2477             # We start by listing those that are present in the parameter set.
2478            
2479 12         13 my @present = grep exists $vr->{clean}{$_}, @{$rr->{param}};
  12         53  
2480            
2481             # For a 'together' rule, the count must equal the number of
2482             # arguments to this rule, or must be zero. In other words, there
2483             # must be none present or all present.
2484            
2485 12 100 100     55 if ( $rr->{type} eq 'together' and @present > 0 and @present < @{$rr->{param}} )
  1 100 66     3  
      100        
2486             {
2487 1         3 add_error_warn($vr, $rr, 'ERR_TOGETHER', { param => $rr->{param} });
2488             }
2489            
2490             # For a 'at_most_one' rule, the count must be less than or equal
2491             # to one (i.e. not more than one must have been specified).
2492            
2493             elsif ( $rr->{type} eq 'at_most_one' and @present > 1 )
2494             {
2495 2         6 add_error_warn($vr, $rr, 'ERR_AT_MOST', { param => \@present });
2496             }
2497             }
2498            
2499             # For an 'include' rule, we immediately check the given ruleset
2500             # (unless it has already been checked). This statement essentially
2501             # includes one ruleset within another. It is very powerful, because
2502             # it allows different route handlers to to validate their parameters
2503             # using common rulesets.
2504            
2505             elsif ( $rr->{type} eq 'include' )
2506             {
2507 23         32 my $rs_name = $rr->{ruleset};
2508            
2509             # First try to validate the given ruleset.
2510            
2511 23         90 $self->validate_ruleset($vr, $rs_name);
2512            
2513             # If it was a 'require' rule, check to see if the ruleset was
2514             # fulfilled.
2515            
2516 23 100 100     72 if ( $rr->{require} and not $vr->{rs}{$rs_name} == 2 )
2517             {
2518 1         1 my (@missing, %found);
2519            
2520 1         1 @missing = grep { unique($_, \%found) } @{$self->{RULESETS}{$rs_name}{fulfill_order}};
  2         4  
  1         8  
2521            
2522 1 50       3 my $msg = @missing == 1 ? 'ERR_REQ_SINGLE' : 'ERR_REQ_MULT';
2523 1         3 add_error_warn($vr, $rr, $msg, { param => \@missing });
2524             }
2525             }
2526            
2527             elsif ( $rr->{type} eq 'constraint' )
2528             {
2529             # From the list of rulesets specified in this rule, check how many
2530             # were and were not fulfilled.
2531            
2532 6         7 my @fulfilled = grep { $vr->{rs}{$_} == 2 } @{$rr->{ruleset}};
  12         21  
  6         7  
2533 6         6 my @not_fulfilled = grep { $vr->{rs}{$_} != 2 } @{$rr->{ruleset}};
  12         17  
  6         7  
2534            
2535             # For a 'require_one' or 'require_any' rule, generate an error if
2536             # not enough of the rulesets are fulfilled. List all of the
2537             # parameters which could be given in order to fulfill these
2538             # rulesets.
2539            
2540 6 100 66     48 if ( @fulfilled == 0 and ( $rr->{constraint} eq 'require_one' or
    50 66        
      33        
      33        
2541             $rr->{constraint} eq 'require_any' ) )
2542             {
2543 4         5 my (@missing, %found);
2544            
2545 12         15 @missing = grep { unique($_, \%found) }
2546 4         6 map { @{$self->{RULESETS}{$_}{fulfill_order}} } @not_fulfilled;
  8         7  
  8         16  
2547            
2548 4 50       15 my $msg = @missing == 1 ? 'ERR_REQ_SINGLE' : 'ERR_REQ_MULT';
2549 4         8 add_error_warn($vr, $rr, $msg, { param => \@missing });
2550             }
2551            
2552             # For an 'allow_one' or 'require_one' rule, generate an error if
2553             # more than one of the rulesets was fulfilled.
2554            
2555             elsif ( @fulfilled > 1 and ($rr->{constraint} eq 'allow_one' or
2556             $rr->{constraint} eq 'require_one') )
2557             {
2558 2         7 my @params;
2559 2         3 my ($label) = "A";
2560            
2561 2         3 foreach my $rs ( @fulfilled )
2562             {
2563 4         6 push @params, "($label)"; $label++;
  4         5  
2564 4         8 push @params, @{$self->{RULESETS}{$rs}{fulfill_order}}
2565 4 50       7 if ref $self->{RULESETS}{$rs}{fulfill_order} eq 'ARRAY';
2566             }
2567            
2568 2         2 my $message = 'ERR_REQ_ONE';
2569            
2570 2         6 add_error_warn($vr, $rr, 'ERR_REQ_ONE', { param => \@params });
2571             }
2572             }
2573            
2574             # For a 'content_type' rule, we set the content type of the response
2575             # according to the given parameter.
2576            
2577             elsif ( $type eq 'content_type' )
2578             {
2579 4         4 my $param = $rr->{param};
2580 4   100     10 my $value = $vr->{raw}{$param} || '';
2581 4   33     11 my $clean_name = $rr->{key} || $rr->{param};
2582 4         5 my ($selected, $selected_type);
2583            
2584 4         3 push @{$vr->{clean_list}}, $key;
  4         6  
2585            
2586 4 100       9 if ( $rr->{type_map}{$value} )
2587             {
2588 3         4 $vr->{content_type} = $rr->{type_map}{$value};
2589 3         4 $vr->{clean}{$clean_name} = $value;
2590 3         6 $vr->{ps}{$param} = 1;
2591             }
2592            
2593             else
2594             {
2595 1         1 $vr->{content_type} = 'unknown';
2596 1         2 $vr->{clean}{$clean_name} = undef;
2597 1         2 $vr->{ps}{$param} = 1;
2598 1   50     4 $rr->{key} ||= '_content_type';
2599 1         3 add_error_warn($vr, $rr, 'ERR_MEDIA_TYPE', { param => $param, value => $rr->{type_list} });
2600             }
2601             }
2602             }
2603             }
2604              
2605              
2606             # Helper function - given a hashref to use as a scratchpad, returns true the
2607             # first time a given argument is encountered and false each subsequent time.
2608             # This can be reset by calling it with a newly emptied scratchpad.
2609              
2610             sub unique {
2611            
2612 14     14 0 16 my ($arg, $scratch) = @_;
2613            
2614 14 50       17 return if exists $scratch->{$arg};
2615 14         35 $scratch->{$arg} = 1;
2616             }
2617              
2618              
2619             # Add an error message to the current validation.
2620              
2621             sub add_error {
2622              
2623 36     36 0 67 my ($vr, $rr, $msg, $subst) = @_;
2624            
2625             # If no message was given, use a default one. It's not a very good
2626             # message, but what can we do?
2627            
2628 36   50     47 $msg ||= 'ERR_DEFAULT';
2629            
2630             # If the given message starts with 'ERR_', assume it is an error code. If
2631             # the code is present as an attribute of the rule record, use the
2632             # corresponding value as the message. Otherwise, use the global value.
2633            
2634 36 100       234 if ( $msg =~ qr{^ERR_} )
2635             {
2636 17   33     67 $msg = $rr->{$msg} || $vr->{settings}{$msg} || $ERROR_MSG{$msg} || $ERROR_MSG{ERR_DEFAULT};
2637             }
2638            
2639             # Next, figure out the error key. If the rule has a 'key' directive, use that. If
2640             # the rule is a parameter rule, use the parameter name. Otherwise use the
2641             # stringified rule reference as a key.
2642            
2643             my $err_key = $rr->{key} ? $rr->{key}
2644             : $rr->{type} eq 'param' ? $rr->{param}
2645 36 50       112 : $rr->{type} eq 'content_type' ? '_content_type'
    100          
    100          
2646             : "_$rr";
2647            
2648             # Record the error message under the key, and add the key to the error
2649             # list. Other rules might later remove or alter the error
2650             # message.
2651            
2652 36         30 push @{$vr->{er}}, [$err_key, subst_error($msg, $subst)];
  36         73  
2653 36         131 $vr->{ec}{$err_key}++;
2654             }
2655              
2656              
2657             # Add a warning message to the current validation. The $subst hash if
2658             # given specifies placeholder substitutions.
2659              
2660             sub add_warning {
2661              
2662 11     11 0 15 my ($vr, $rr, $msg, $subst) = @_;
2663            
2664             # If no message was given, use a default one. It's not a very good
2665             # message, but what can we do?
2666            
2667 11   50     16 $msg ||= 'ERR_DEFAULT';
2668            
2669             # If the given message starts with 'ERR_', assume it is an error code. If
2670             # the code is present as an attribute of the rule record, use the
2671             # corresponding value as the message. Otherwise, use the global value.
2672            
2673 11 100       63 if ( $msg =~ qr{^ERR_} )
2674             {
2675 1   0     7 $msg = $rr->{$msg} || $vr->{settings}{$msg} || $ERROR_MSG{$msg} || $ERROR_MSG{ERR_DEFAULT};
2676             }
2677            
2678             # Next, figure out the warning key. If the rule has a 'key' directive, use that. If
2679             # the rule is a parameter rule, use the parameter name. Otherwise use the
2680             # stringified rule reference as a key.
2681            
2682             my $warn_key = $rr->{key} ? $rr->{key}
2683             : $rr->{type} eq 'param' ? $rr->{param}
2684 11 50       36 : $rr->{type} eq 'content_type' ? '_content_type'
    100          
    50          
2685             : "_$rr";
2686            
2687             # Record the warning message under the key. Other rules might later
2688             # alter the warning message if they use the same key.
2689            
2690 11         11 push @{$vr->{wn}}, [$warn_key, subst_error($msg, $subst)];
  11         21  
2691 11         31 $vr->{wc}{$warn_key}++;
2692             }
2693              
2694              
2695             # Add an error or warning message to the current validation. If the rule has
2696             # a 'warn' attribute, add a warning. Otherwise, add an error. If the rule
2697             # has an 'errmsg' attribute, use its value instead of the error message given.
2698              
2699             sub add_error_warn {
2700            
2701 11     11 0 16 my ($vr, $rr, $msg, $subst) = @_;
2702            
2703 11 50       30 $msg = $rr->{errmsg} if $rr->{errmsg};
2704            
2705 11 100       17 if ( $rr->{warn} )
2706             {
2707 2 100       5 $msg = $rr->{warn} if $rr->{warn} ne '1';
2708 2         3 return add_warning($vr, $rr, $msg, $subst);
2709             }
2710            
2711             else
2712             {
2713 9         14 return add_error($vr, $rr, $msg, $subst);
2714             }
2715             }
2716              
2717              
2718             # Substitute placeholders in an error or warning message.
2719              
2720             sub subst_error {
2721              
2722 47     47 0 54 my ($message, $subst) = @_;
2723            
2724 47         171 while ( $message =~ /^(.*)\{(\w+)\}(.*)$/ )
2725             {
2726 48         101 my $value = $subst->{$2};
2727            
2728 48 100 33     62 if ( ref $value )
    50          
2729             {
2730 44 50       58 if ( reftype $value eq 'ARRAY' )
    0          
2731             {
2732 44         70 $value = name_list(@$value);
2733             }
2734             elsif ( reftype $value eq 'HASH' )
2735             {
2736 0         0 $value = name_list(sort keys %$value);
2737             }
2738             }
2739            
2740             elsif ( defined $value && $value !~ /^'/ )
2741             {
2742 4         7 $value = "'$value'";
2743             }
2744            
2745             else
2746             {
2747 0         0 $value = "''";
2748             }
2749            
2750 8     8   61 no warnings 'uninitialized';
  8         10  
  8         15684  
2751            
2752 48 50 33     237 $message = "$1$value$3" if defined $value and $value ne '';
2753             }
2754            
2755 47         108 return $message;
2756             }
2757              
2758              
2759             # Generate a list of quoted strings from the specified values.
2760              
2761             sub name_list {
2762            
2763 44     44 0 60 my @names = @_;
2764            
2765 44 50       50 return unless @names;
2766 44         114 return "'" . join("', '", @names) . "'";
2767             };
2768              
2769              
2770             package HTTP::Validate::Result;
2771              
2772             =head1 OTHER METHODS
2773              
2774             The result object returned by L provides the following
2775             methods:
2776              
2777             =head3 passed
2778              
2779             Returns true if the validation passed, false otherwise.
2780              
2781             =cut
2782              
2783             sub passed {
2784            
2785 19     19   1032 my ($self) = @_;
2786            
2787             # If any errors occurred, then the validation failed.
2788            
2789 19 100 66     54 return if ref $self->{er} eq 'ARRAY' && @{$self->{er}};
  4         25  
2790            
2791             # Otherwise, it passed.
2792            
2793 15         46 return 1;
2794             }
2795              
2796              
2797             =head3 errors
2798              
2799             =head3 errors(key)
2800              
2801             In a scalar context, this returns the number of errors generated by this
2802             validation. In a list context, it returns a list of error messages. If an
2803             argument is given, only messages whose key equals the argument are returned.
2804              
2805             =cut
2806              
2807             sub errors {
2808              
2809 53     53   1733 my ($self, $key) = @_;
2810            
2811             # In scalar context, just return the count.
2812            
2813 53 100       104 if ( ! wantarray )
    100          
2814             {
2815 21 100       80 return 0 unless defined $key ? ref $self->{ec} : ref $self->{er};
    100          
2816 8 100 50     20 return defined $key ? ($self->{ec}{$key} || 0) : scalar @{$self->{er}};
  5         18  
2817             }
2818            
2819             # In list context, if a key is given then return just the matching error
2820             # messages or an empty list if there are none.
2821            
2822             elsif ( defined $key )
2823             {
2824 4 100       11 return unless ref $self->{ec};
2825 3         3 return map { $_->[1] } grep { $_->[0] eq $key } @{$self->{er}};
  3         7  
  3         8  
  3         4  
2826             }
2827            
2828             # If no key is given, just return all of the messages.
2829            
2830             else
2831             {
2832 28         27 return map { $_->[1] } @{$self->{er}};
  19         59  
  28         50  
2833             }
2834             }
2835              
2836             =head3 error_keys
2837              
2838             Returns the list of keys for which error messages were generated.
2839              
2840             =cut
2841              
2842             sub error_keys {
2843            
2844 6     6   769 my ($self) = @_;
2845 6         6 return keys %{$self->{ec}};
  6         48  
2846             }
2847              
2848              
2849             =head3 warnings
2850              
2851             =head3 warnings(key)
2852              
2853             In a scalar context, this returns the number of warnings generated by the
2854             validation. In a list context, it returns a list of warning messages. If an
2855             argument is given, only messages whose key equals the argument are returned.
2856              
2857             =cut
2858              
2859             sub warnings {
2860              
2861 29     29   3360 my ($self, $key) = @_;
2862            
2863             # In scalar context, just return the count.
2864            
2865 29 100       55 if ( ! wantarray )
    100          
2866             {
2867 18 100       71 return 0 unless defined $key ? ref $self->{wc} : ref $self->{wn};
    100          
2868 6 100 50     17 return defined $key ? ($self->{wc}{$key} || 0) : scalar @{$self->{wn}};
  4         13  
2869             }
2870            
2871             # In list context, if a key is given then return just the matching warning
2872             # messages or an empty list if there are none.
2873            
2874             elsif ( defined $key )
2875             {
2876 2 50       6 return unless ref $self->{wn};
2877 2         3 return map { $_->[1] } grep { $_->[0] eq $key } @{$self->{wn}};
  2         8  
  2         36  
  2         4  
2878             }
2879            
2880             # If no key is given, just return all of the messages.
2881            
2882             else
2883             {
2884 9         30 return map { $_->[1] } @{$self->{wn}};
  6         25  
  9         21  
2885             }
2886             }
2887              
2888              
2889             =head3 warning_keys
2890              
2891             Returns the list of keys for which warning messages were generated.
2892              
2893             =cut
2894              
2895             sub warning_keys {
2896            
2897 1     1   2 my ($self) = @_;
2898 1         1 return keys %{$self->{wc}};
  1         8  
2899             }
2900              
2901              
2902             =head3 keys
2903              
2904             In a scalar context, this returns the number of parameters that had valid values. In a list
2905             context, it returns a list of parameter names in the order they were recognized. Individual
2906             parameter values can be gotten by using either L or L.
2907              
2908             =cut
2909              
2910             sub keys {
2911              
2912 6     6   14 my ($self) = @_;
2913            
2914             # Return the list of parameter keys in the order they were recognized.
2915            
2916 6         15 return @{$self->{clean_list}};
  6         23  
2917             }
2918              
2919              
2920             =head3 values
2921              
2922             Returns the hash of clean parameter values. This is not a copy, so any
2923             modifications you make to it will be reflected in subsequent calls to L.
2924              
2925             =cut
2926              
2927             sub values {
2928            
2929 2     2   6 my ($self) = @_;
2930            
2931             # Return the clean value hash.
2932            
2933 2         3 return $self->{clean};
2934             }
2935              
2936              
2937             =head3 value(parameter_name)
2938              
2939             Returns the cleaned value of the specified parameter, or undef if that parameter was not
2940             specified in the request or if its value was invalid. If the parameter can take multiple
2941             values, then the result of this function will be a listref.
2942              
2943             =cut
2944              
2945             sub value {
2946              
2947 69     69   5815 my ($self, $param) = @_;
2948            
2949 69         289 return $self->{clean}{$param};
2950             }
2951              
2952              
2953             =head3 value_list(parameter_name)
2954              
2955             If called in list context, this method returns the cleaned value(s) of the specified
2956             parameter, or the empty list if that parameter was not specified in the request or if no
2957             valid values were given. If called in scalar context, it returns the number of values.
2958             You would typically use this with a parameter whose rule hashref includes either 'list',
2959             'split', or 'multiple'.
2960              
2961             =cut
2962              
2963             sub value_list {
2964              
2965 4     4   1403 my ($self, $param) = @_;
2966            
2967 4 100       11 return () unless exists $self->{clean}{$param};
2968            
2969 3 100       8 if ( ref $self->{clean}{$param} eq 'ARRAY' )
2970             {
2971 2         3 return @{$self->{clean}{$param}};
  2         6  
2972             }
2973            
2974             else
2975             {
2976 1         3 return ($self->{clean}{$param});
2977             }
2978             }
2979              
2980              
2981             =head3 specified(parameter_name)
2982              
2983             Returns true if the specified parameter was specified in the request with at least
2984             one value, whether or not that value was valid. Returns false otherwise.
2985              
2986             =cut
2987              
2988             sub specified {
2989            
2990 8     8   694 my ($self, $param) = @_;
2991            
2992 8         33 return exists $self->{clean}{$param};
2993             }
2994              
2995              
2996             =head3 raw
2997              
2998             Returns a hash of the raw parameter values as originally provided to
2999             L. Multiple values are represented by array refs. The
3000             result of this method can be used, for example, to redisplay a web form if the
3001             submission resulted in errors.
3002              
3003             =cut
3004              
3005             sub raw {
3006            
3007 1     1   3 my ($self) = @_;
3008            
3009 1         2 return $self->{raw};
3010             }
3011              
3012              
3013             =head3 content_type
3014              
3015             This returns the content type specified by the request parameters. If none
3016             was specified, or if no content_type rule was included in the validation, it
3017             returns undef.
3018              
3019             =cut
3020              
3021             sub content_type {
3022              
3023 3     3   337 my ($self) = @_;
3024            
3025 3         11 return $self->{content_type};
3026             }
3027              
3028              
3029             package HTTP::Validate;
3030              
3031             # At the very end, we have the validator functions
3032             # ================================================
3033              
3034             =head1 VALIDATORS
3035              
3036             Parameter rules can each include one or more validator functions under the key
3037             C. The job of these functions is two-fold: first to check for good
3038             parameter values, and second to generate cleaned values.
3039              
3040             There are a number of validators provided by this module, or you can specify a
3041             reference to a function of your own.
3042              
3043             =head2 Predefined validators
3044              
3045             =head3 INT_VALUE
3046              
3047             This validator accepts any integer, and rejects all other values. It
3048             returns a numeric value, generated by adding 0 to the raw parameter value.
3049              
3050             =head3 INT_VALUE(min,max)
3051              
3052             This validator accepts any integer between C and C (inclusive). If either C
3053             or C is undefined, that bound will not be tested.
3054              
3055             =head3 POS_VALUE
3056              
3057             This is an alias for C.
3058              
3059             =head3 POS_ZERO_VALUE
3060              
3061             This is an alias for C.
3062              
3063             =cut
3064              
3065             sub int_value {
3066              
3067 47     47 0 68 my ($value, $context, $min, $max) = @_;
3068            
3069 47 100       149 unless ( $value =~ /^([+-]?\d+)$/ )
3070             {
3071 9         23 return { error => "bad value '$value' for {param}: must be an integer" };
3072             }
3073            
3074 38 100 100     85 if ( defined $min and $value < $min )
3075             {
3076 7 50       22 my $criterion = defined $max ? "between $min and $max"
    100          
    100          
3077             : $min == 0 ? "nonnegative"
3078             : $min == 1 ? "positive"
3079             : "at least $min";
3080            
3081 7         20 return { error => "bad value '$value' for {param}: must be $criterion" };
3082             }
3083            
3084 31 100 100     54 if ( defined $max and $value > $max )
3085             {
3086 1 50       4 my $criterion = defined $min ? "between $min and $max" : "at most $max";
3087            
3088 1         3 return { error => "bad value '$value' for {param} must be $criterion" };
3089             }
3090            
3091 30         58 return { value => $value + 0 };
3092             }
3093              
3094             sub INT_VALUE {
3095            
3096 15     15 1 142624 my ($min, $max) = @_;
3097            
3098 15 100 100     143 croak "lower bound must be an integer (was '$min')" unless !defined $min || $min =~ /^[+-]?\d+$/;
3099 14 50 66     40 croak "upper bound must be an integer (was '$max')" unless !defined $max || $max =~ /^[+-]?\d+$/;
3100            
3101 14 100 66     89 return \&int_value unless defined $min or defined $max;
3102 6     5   24 return sub { return int_value(shift, shift, $min, $max) };
  5         7  
3103             };
3104              
3105             sub POS_VALUE {
3106            
3107 18     18 1 5445 return sub { return int_value(shift, shift, 1) };
  33     33   50  
3108             };
3109              
3110             sub POS_ZERO_VALUE {
3111              
3112 4     4 1 14 return sub { return int_value(shift, shift, 0) };
  3     3   4  
3113             };
3114              
3115              
3116             =head3 DECI_VALUE
3117              
3118             This validator accepts any decimal number, including exponential notation, and
3119             rejects all other values. It returns a numeric value, generated by adding 0
3120             to the parameter value.
3121              
3122             =head3 DECI_VALUE(min,max)
3123              
3124             This validator accepts any real number between C and C (inclusive).
3125             Specify these bounds in quotes (i.e. as string arguments) if non-zero so that
3126             they will appear properly in error messages. If either C or C is
3127             undefined, that bound will not be tested.
3128              
3129             =cut
3130              
3131             sub deci_value {
3132            
3133 14     14 0 19 my ($value, $context, $min, $max) = @_;
3134            
3135 14 100       53 unless ( $value =~ /^[+-]?(?:\d+\.\d*|\d*\.\d+|\d+)(?:[eE][+-]?\d+)?$/ )
3136             {
3137 1         3 return { error => "bad value '$value' for {param}: must be a decimal number" };
3138             }
3139            
3140 13 100 66     53 if ( defined $min and defined $max and ($value < $min or $value > $max) )
      100        
      100        
3141             {
3142 4         28 return { error => "bad value '$value' for {param}: must be between $min and $max" };
3143             }
3144            
3145 9 50 66     16 if ( defined $min and $value < $min )
3146             {
3147 0         0 return { error => "bad value '$value' for {param}: must be at least $min" };
3148             }
3149            
3150 9 50 66     14 if ( defined $max and $value > $max )
3151             {
3152 0         0 return { error => "bad value '$value' for {param}: must be at most $max" };
3153             }
3154            
3155 9         29 return { value => $value + 0 };
3156             }
3157              
3158             sub DECI_VALUE {
3159            
3160 15     15 1 667 my ($min, $max) = @_;
3161            
3162 15 100 100     124 croak "lower bound must be numeric" if defined $min && !looks_like_number($min);
3163 14 50 66     31 croak "upper bound must be numeric" if defined $max && !looks_like_number($max);
3164            
3165 14 100 66     65 return \&deci_value unless defined $min or defined $max;
3166 6     8   19 return sub { return deci_value(shift, shift, $min, $max) };
  8         11  
3167             };
3168              
3169              
3170             =head3 MATCH_VALUE(pattern)
3171              
3172             This validator accepts any string that matches the specified pattern, and
3173             rejects any that does not. If you specify the pattern as a string, it will be
3174             converted into a regexp and will have ^ prepended and $ appended, and also the
3175             modifier "i". If you specify the pattern using C, then it is used unchanged.
3176             Any rule that uses this validator should be provided with an error directive, since the
3177             default error message is by necessity not very informative. The value is not
3178             cleaned in any way.
3179              
3180             =cut
3181              
3182             sub match_value {
3183              
3184 7     7 0 10 my ($value, $context, $pattern) = @_;
3185            
3186 7 100       36 return if $value =~ $pattern;
3187 3         8 return { error => "bad value '$value' for {param}: did not match the proper pattern" };
3188             }
3189              
3190             sub MATCH_VALUE {
3191              
3192 10     10 1 7960 my ($pattern) = @_;
3193            
3194 10 100 100     186 croak "MATCH_VALUE requires a regular expression" unless
      100        
3195             defined $pattern && (!ref $pattern || ref $pattern eq 'Regexp');
3196            
3197 8 100       159 my $re = ref $pattern ? $pattern : qr{^$pattern$}oi;
3198            
3199 8     7   44 return sub { return match_value(shift, shift, $re) };
  7         13  
3200             };
3201              
3202              
3203             =head3 ENUM_VALUE(string,...)
3204              
3205             This validator accepts any of the specified string values, and rejects all
3206             others. Comparisons are case insensitive. If the version of Perl is 5.016 or
3207             greater, or if the module C is available and has been
3208             required, then the C function will be used instead of the usual C when
3209             comparing values. The cleaned value will be the matching string value from
3210             this call.
3211              
3212             If any of the strings is '#', then subsequent values will be accepted but not
3213             reported in the standard error message as allowable values. This allows for
3214             undocumented values to be accepted.
3215              
3216             =cut
3217              
3218             sub enum_value {
3219            
3220 8     8 0 14 my ($value, $context, $accepted, $good_list) = @_;
3221            
3222 8         138 my $folded = $case_fold->($value);
3223            
3224             # If the value is found in the $accepted hash, then we're good. Return
3225             # the value as originally given, not the case-folded version.
3226            
3227 8 100       29 return { value => $accepted->{$folded} } if exists $accepted->{$folded};
3228            
3229             # Otherwise, then we have an error.
3230            
3231 2         8 return { error => "bad value '$value' for {param}: must be one of $good_list" };
3232             }
3233              
3234             sub ENUM_VALUE {
3235            
3236 6     6 1 4164 my (%accepted, @documented, $undoc);
3237            
3238 6         9 foreach my $k ( @_ )
3239             {
3240 14 50 33     87 next unless defined $k && $k ne '';
3241            
3242 14 100       42 if ( $k eq '#' )
3243             {
3244 1         2 $undoc = 1;
3245 1         1 next;
3246             }
3247            
3248 13         236 $accepted{ $case_fold->($k) } = $k;
3249 13 100       28 push @documented, $k unless $undoc;
3250             }
3251            
3252 6 100       90 croak "ENUM_VALUE requires at least one value" unless keys %accepted;
3253            
3254 5         15 my $good_list = "'" . join("', '", @documented) . "'";
3255            
3256 5     8   44 return sub { return enum_value(shift, shift, \%accepted, $good_list) };
  8         19  
3257             };
3258              
3259              
3260             =head3 BOOLEAN_VALUE
3261              
3262             This validator is used for parameters that take a true/false value. It
3263             accepts any of the following values: "yes", "no", "true", "false", "on",
3264             "off", "1", "0", compared case insensitively. It returns an error if any
3265             other value is specified. The cleaned value will be 1 or 0.
3266              
3267             =cut
3268              
3269             sub boolean_value {
3270              
3271 19     19 0 24 my ($value, $context) = @_;
3272            
3273 19 50       37 if ( ref($value) =~ /boolean/i )
    50          
3274             {
3275 0         0 return { value => $value };
3276             }
3277            
3278             elsif ( ! ref $value )
3279             {
3280 19 100       62 if ( $value =~ /^(?:1|yes|true|on)$/i )
    100          
3281             {
3282 10         16 return { value => 1 };
3283             }
3284            
3285             elsif ( $value =~ /^(?:0|no|false|off)$/i )
3286             {
3287 8         12 return { value => 0 };
3288             }
3289             }
3290            
3291 1         6 return { error => "the value of {param} must be one of: yes, no, true, false, on, off, 1, 0" };
3292             }
3293              
3294 9     9 1 28 sub BOOLEAN_VALUE { return \&boolean_value; };
3295              
3296              
3297             =head3 FLAG_VALUE
3298              
3299             This validator should be used for parameters that are considered to be "true"
3300             if present with an empty value. The validator returns a value of 1 in this case,
3301             and behaves like 'BOOLEAN_VALUE' otherwise.
3302              
3303             =cut
3304              
3305 2     2 1 31 sub FLAG_VALUE { return 'FLAG_VALUE'; };
3306              
3307              
3308             =head3 ANY_VALUE
3309              
3310             This validator accepts any non-empty value. Using this validator
3311             is equivalent to not specifying any validator at all.
3312              
3313             =cut
3314              
3315             sub any_value {
3316              
3317 4     4 0 8 my ($value, $context) = @_;
3318            
3319 4         10 return { value => $value };
3320             }
3321              
3322             sub ANY_VALUE {
3323            
3324 7     7 1 2840 return \&any_value;
3325             };
3326              
3327              
3328             =head2 Reusing validators
3329              
3330             Every time you use a parametrized validator such as C, a new
3331             closure is generated. If you are repeating a particular set of parameters
3332             many times, to save space you may want to instantiate the validator just once:
3333              
3334             my $zero_to_ten = INT_VALUE(0,10);
3335            
3336             define_ruleset( 'foo' =>
3337             { param => 'bar', valid => $zero_to_ten },
3338             { param => 'baz', valid => $zero_to_ten });
3339              
3340             =head2 Writing your own validator functions
3341              
3342             If you wish to validate parameters which do not match any of the validators
3343             described above, you can write your own validator function. Validator
3344             functions are called with two arguments:
3345              
3346             ($value, $context)
3347              
3348             Where $value is the raw parameter value and $context is a hash ref provided
3349             when the validation process is initiated (or an empty hashref if none is
3350             provided). This allows the passing of information such as database handles to
3351             the validator functions.
3352              
3353             If your function decides that the parameter value is valid and does not need
3354             to be cleaned, it can indicate this by returning an empty result.
3355              
3356             Otherwise, it must return a hash reference with one or more of the following
3357             keys:
3358              
3359             =over 4
3360              
3361             =item error
3362              
3363             If the parameter value is not valid, the value of this key should be an error
3364             message that states I. This message should
3365             contain the placeholder {param}, which will be substituted with the parameter
3366             name. Use this placeholder, and do not hard-code the parameter name.
3367              
3368             Here is an example of a good message:
3369              
3370             "the value of {param} must be a positive integer (was {value})".
3371              
3372             Here is an example of a bad message:
3373              
3374             "bad value for 'foo'".
3375              
3376             =item warn
3377              
3378             If the parameter value is acceptable but questionable in some way, the value
3379             of this key should be a message that states what a good value should look
3380             like. All such messages will be made available through the result object that
3381             is returned by the validation routine. The code that handles the request may
3382             then choose to display these messages as part of the response. Your code may
3383             also make use of this information during the process of responding to the
3384             request.
3385              
3386             =item value
3387              
3388             If the parameter value represents anything other than a simple string (i.e. a
3389             number, list, or more complicated data structure), then the value of this key
3390             should be the converted or "cleaned" form of the parameter value. For
3391             example, a numeric parameter might be converted into an actual number by
3392             adding zero to it, or a pair of values might be split apart and converted into
3393             an array ref. The value of this key will be returned as the "cleaned" value
3394             of the parameter, in place of the raw parameter value provided in the request.
3395              
3396             =back
3397              
3398             =head3 Parametrized validators
3399              
3400             If you want to write your own parametrized validator, write a function that
3401             generates and returns a closure. For example:
3402              
3403             sub integer_multiple {
3404              
3405             my ($value, $context, $base) = @_;
3406            
3407             return { value => $value + 0 } if $value % $base == 0;
3408             return { error => "the value of {param} must be a multiple of $base (was {value})" };
3409             }
3410            
3411             sub INTEGER_MULTIPLE {
3412              
3413             my ($base) = $_[0] + 0;
3414            
3415             croak "INTEGER_MULTIPLE requires a numeric parameter greater than zero"
3416             unless defined $base and $base > 0;
3417            
3418             return sub { return integer_multiple(shift, shift, $base) };
3419             }
3420            
3421             define_ruleset( 'foo' =>
3422             { param => foo, valid => INTEGER_MULTIPLE(3) });
3423              
3424             =cut
3425              
3426              
3427              
3428             =head1 AUTHOR
3429              
3430             Michael McClennen, C<< >>
3431              
3432             =head1 SUPPORT
3433              
3434             Please report any bugs or feature requests to C, or through
3435             the web interface at L. I will be notified, and then you'll
3436             automatically be notified of progress on your bug as I make changes.
3437              
3438             =head1 LICENSE AND COPYRIGHT
3439              
3440             Copyright 2014 Michael McClennen.
3441              
3442             This program is free software; you can redistribute it and/or modify it
3443             under the terms of either: the GNU General Public License as published
3444             by the Free Software Foundation; or the Artistic License.
3445              
3446             See http://dev.perl.org/licenses/ for more information.
3447              
3448              
3449             =cut
3450              
3451             1; # End of HTTP::Validate