File Coverage

lib/HTTP/Validate.pm
Criterion Covered Total %
statement 627 656 95.5
branch 389 510 76.2
condition 191 293 65.1
subroutine 60 60 100.0
pod 16 37 43.2
total 1283 1556 82.4


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