File Coverage

lib/HTTP/Validate.pm
Criterion Covered Total %
statement 619 648 95.5
branch 386 504 76.5
condition 191 293 65.1
subroutine 60 60 100.0
pod 16 37 43.2
total 1272 1542 82.4


line stmt bran cond sub pod time code
1             package HTTP::Validate;
2              
3 7     7   101174 use strict;
  7         9  
  7         179  
4 7     7   21 use warnings;
  7         8  
  7         159  
5              
6 7     7   20 use Exporter qw( import );
  7         9  
  7         208  
7 7     7   22 use Carp qw( carp croak );
  7         7  
  7         379  
8 7     7   23 use Scalar::Util qw( reftype weaken looks_like_number );
  7         7  
  7         1735  
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.98';
18              
19             =head1 NAME
20              
21             HTTP::Validate - validate and clean HTTP parameter values according to a set of rules
22              
23             Version 0.98
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   30 @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         20 @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         42729 %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 2829 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         23 my $self = bless { RULESETS => {}, SETTINGS => {} }, $class;
735            
736             # Set the requested settings
737            
738 9         20 $self->validation_settings(@settings);
739            
740             # Return the new object
741            
742 7         16 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 13247 my $self = ref $_[0] eq 'HTTP::Validate' ? shift : $DEFAULT_INSTANCE;
761              
762 77         106 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         155 my ($package, $filename, $line) = caller;
768            
769             # Check the arguments, then create a new ruleset object.
770            
771 77 100 100     875 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         165 my $rs = $self->create_ruleset($ruleset_name, $filename, $line);
775            
776             # Then add the rules.
777            
778 72         117 $self->add_rules($rs, @rules);
779            
780             # If we get here without any errors, install the ruleset and return.
781            
782 61         94 $self->{RULESETS}{$ruleset_name} = $rs;
783 61         107 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 18464 my $self = ref $_[0] eq 'HTTP::Validate' ? shift : $DEFAULT_INSTANCE;
834            
835 60         93 my ($ruleset_name, $context, $parameters) = @_;
836            
837             # Create a new validation-execution object using the specified context
838             # and parameters.
839            
840 60         110 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         107 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 1758 my $self = ref $_[0] eq 'HTTP::Validate' ? shift : $DEFAULT_INSTANCE;
891            
892 15         30 while (@_)
893             {
894 34         28 my $key = shift;
895 34         23 my $value = shift;
896            
897 34 100       66 if ( $key eq 'allow_unrecognized' )
    100          
    100          
898             {
899 5 50       22 $self->{SETTINGS}{permissive} = $value ? 1 : 0;
900             }
901            
902             elsif ( $key eq 'ignore_unrecognized' )
903             {
904 2 50       6 $self->{SETTINGS}{ignore_unrecognized} = $value ? 1 : 0;
905             }
906            
907             elsif ( $ERROR_MSG{$key} )
908             {
909 24         50 $self->{SETTINGS}{$key} = $value;
910             }
911            
912             else
913             {
914 3         404 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 753 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         6 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 632 my $self = ref $_[0] eq 'HTTP::Validate' ? shift : $DEFAULT_INSTANCE;
977            
978 4         5 my ($ruleset_name) = @_;
979            
980             # Make sure we have a valid ruleset, or else return false.
981            
982 4 50       10 return unless defined $ruleset_name;
983            
984 4         4 my $rs = $self->{RULESETS}{$ruleset_name};
985 4 50       7 return unless $rs;
986            
987             # Now generate the requested documentation.
988            
989 4         17 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 5 my $self = ref $_[0] eq 'HTTP::Validate' ? shift : $DEFAULT_INSTANCE;
1012            
1013 1         2 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       3 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 77 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       110 croak "you must provide a non-empty name for the ruleset" if $ruleset_name eq '';
1044            
1045 74 100       142 if ( exists $validator->{RULESETS}{$ruleset_name} )
1046             {
1047 2         3 my $filename = $validator->{RULESETS}{$ruleset_name}{filename};
1048 2         2 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         361 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         158 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 65 my ($self) = shift;
1111 72         77 my ($rs) = shift;
1112            
1113 72         73 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         98 foreach my $rule (@_)
1120             {
1121             # If the item is a scalar, then it is a documentation string.
1122            
1123 200 100       516 unless ( ref $rule )
    50          
1124             {
1125             # If the string starts with >, !, ^, or ? then treat it specially.
1126            
1127 16 100       74 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       17 if ( $1 eq '>>' )
    100          
    100          
    100          
1135             {
1136 1 50 33     8 $self->add_doc($rs, $doc_rule, @doc_lines) if $doc_rule || @doc_lines;
1137 1         4 @doc_lines = $2;
1138 1         1 $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         1 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         2 $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         13 push @doc_lines, $rule;
1186             }
1187            
1188 16         21 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         129 my $rr = { rs => $rs, rn => scalar(@{$rs->{rules}}) + 1 };
  184         363  
1202 184         123 push @{$rs->{rules}}, $rr;
  184         208  
1203            
1204 184         281 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         113 my $type;
1210            
1211             KEY:
1212 184         333 foreach my $key (keys %$rule)
1213             {
1214 325 100 66     588 croak "unknown attribute '$key' found in rule" unless $DIRECTIVE{$key} || $ERROR_MSG{$key};
1215            
1216 323 100 100     885 if ( defined $DIRECTIVE{$key} && $DIRECTIVE{$key} == 2 )
1217             {
1218 184 100       309 croak "a rule definition cannot contain the attributes '$key' and '$type' together, because they indicate different rule types"
1219             if $type;
1220 183         146 $type = $key;
1221 183         179 $rr->{$type} = $rule->{$type};
1222 183         202 next KEY;
1223             }
1224             }
1225            
1226             # Then process the other keys.
1227            
1228 181         243 foreach my $key (keys %$rule)
1229             {
1230 320         244 my $value = $rule->{$key};
1231            
1232 320 100 100     1520 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     205 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     15 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       9 $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       7 unless $CATEGORY{$type} eq 'param';
1253            
1254 4   66     12 $rr->{cleaner} = $CLEANER_DEF{$value} || $value;
1255            
1256             croak "invalid value '$value' for 'clean'"
1257 4 50       9 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       13 unless $CATEGORY{$type} eq 'param';
1264            
1265 3         8 $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       13 unless $CATEGORY{$type} eq 'param';
1272            
1273 8 50 66     23 croak "the value of '$key' must be a string or a regexp"
1274             if ref $value and ref $value ne 'Regexp';
1275            
1276 8         9 $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       15 unless ( ref $value )
1283             {
1284 7         80 $value = qr{ \s* $value \s* }oxs;
1285             }
1286            
1287 8         11 $rr->{split} = $value;
1288 8 100       26 $rr->{warn} = 1 if $key eq 'list';
1289             }
1290            
1291             elsif ( $key eq 'error' || $key eq 'errmsg' )
1292             {
1293 7         7 $rr->{errmsg} = $value;
1294             }
1295            
1296             elsif ( $key ne $type )
1297             {
1298 19 50       28 croak "the value of '$key' must be a string" if ref $value;
1299            
1300 19         32 $rr->{$key} = $value;
1301             }
1302             }
1303            
1304 181 50       232 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       257 if ( $CATEGORY{$type} ne 'modifier' )
1312             {
1313 176         229 $self->add_doc($rs, $doc_rule, @doc_lines);
1314 176         151 $doc_rule = $rr;
1315 176         184 @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         147 my $typevalue = $rule->{$type};
1334            
1335 181 100       284 if ( $CATEGORY{$type} eq 'param' )
    100          
    100          
    100          
    50          
1336             {
1337 131         132 $rr->{type} = 'param';
1338 131         106 $rr->{param} = $typevalue;
1339            
1340             # Do some basic sanity checking.
1341            
1342 131 100 66     650 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       229 my @validators = ref $rule->{valid} eq 'ARRAY' ? @{$rule->{valid}} : $rule->{valid};
  3         6  
1348            
1349 130         125 foreach my $v (@validators)
1350             {
1351 132 100 66     353 if ( defined $v && $VALIDATOR_DEF{$v} )
    100          
1352             {
1353 6 100       11 $rr->{flag} = 1 if $v eq 'FLAG_VALUE';
1354 6 100       17 push @{$rr->{validators}}, \&boolean_value if $v eq 'FLAG_VALUE';
  2         6  
1355             }
1356            
1357             elsif ( defined $v )
1358             {
1359 87 100 100     446 croak "invalid validator '$v': must be a code ref"
1360             unless ref $v && reftype $v eq 'CODE';
1361            
1362 85         46 push @{$rr->{validators}}, $v;
  85         169  
1363             }
1364             }
1365            
1366 128 100 100     344 $rr->{$type} = 1 if $type eq 'optional' || $type eq 'mandatory';
1367            
1368 128 100       162 if ( $type ne 'optional' )
1369             {
1370 104 50       149 push @{$rs->{fulfill_order}}, $typevalue unless $rs->{params}{$typevalue};
  104         126  
1371             }
1372            
1373 128         166 $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       240 if ( defined $rr->{default} )
1381             {
1382 3 50       8 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     11 @{$rr->{validators}};
  2         6  
1386            
1387 2         3 foreach my $v ( @{$rr->{validators}} )
  2         3  
1388             {
1389 2         5 my $result = $v->($rr->{default}, {});
1390            
1391 2 50       5 next RULE unless defined $result;
1392            
1393 2 100       6 if ( exists $result->{value} )
1394             {
1395 1         1 $rr->{default} = $result->{value};
1396 1 50       2 croak "cleaned default value must be a scalar\n" if ref $rr->{default};
1397 1         3 next RULE;
1398             }
1399             }
1400            
1401 1         238 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       13 my @params = ref $typevalue eq 'ARRAY' ? @$typevalue : $typevalue;
1411            
1412 5         9 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         14  
1417             }
1418            
1419             croak "a rule of type '$type' requires at least one parameter name"
1420 5 50       5 unless @{$rr->{param}} > 0;
  5         15  
1421             }
1422            
1423             elsif ( $CATEGORY{$type} eq 'include' )
1424             {
1425 31         32 $rr->{type} = 'include';
1426 31 100       52 $rr->{require} = 1 if $type eq 'require';
1427 31         47 $rr->{ruleset} = $typevalue;
1428            
1429 31 100 100     320 croak "the value of '$type' must be a ruleset name"
      66        
1430             unless defined $typevalue && !ref $typevalue && $typevalue ne '';
1431            
1432 29 100       124 croak "ruleset '$typevalue' not found" unless defined $self->{RULESETS}{$typevalue};
1433            
1434 28         67 $rs->{includes}{$typevalue} = 1;
1435             }
1436            
1437             elsif ( $CATEGORY{$type} eq 'constraint' )
1438             {
1439 10         13 $rr->{type} = 'constraint';
1440 10         8 $rr->{constraint} = $type;
1441 10         17 $rr->{ruleset} = [];
1442            
1443 10 50 33     40 croak "the value of '$type' must be a list of ruleset names"
1444             unless defined $typevalue && ref $typevalue eq 'ARRAY';
1445            
1446 10         12 foreach my $arg (@$typevalue)
1447             {
1448 20 50 33     56 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       19 unless @{$rr->{ruleset}} > 0;
  10         34  
1456             }
1457            
1458             elsif ( $type eq 'content_type' )
1459             {
1460 4         7 $rr->{type} = 'content_type';
1461 4         7 $rr->{param} = $typevalue;
1462            
1463 4         3 my %map;
1464            
1465 4 50 33     38 croak "invalid parameter name '$typevalue'" if ref $typevalue || $typevalue !~ /\w/;
1466            
1467 4 50       14 my @types = ref $rule->{valid} eq 'ARRAY' ? @{$rule->{valid}} : $rule->{valid};
  4         12  
1468            
1469 4         6 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         30 my ($short, $long) = split /\s*=\s*/, $t;
1478 10   100     29 $long ||= $MEDIA_TYPE{$short};
1479            
1480 10 100       111 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         9 $rr->{type_map}{$short} = $long;
1486 9         11 push @{$rr->{type_list}}, $short;
  9         18  
1487             }
1488            
1489 3 50       11 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         99 $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 231 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     488 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     291 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     359 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         132 my $body = '';
1544 170         96 my $last_pod;
1545             my $this_pod;
1546            
1547 170         167 foreach my $line (@lines)
1548             {
1549             # If this line starts with =, then it needs extra spacing.
1550            
1551 15         39 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       22 if ( $body ne '' )
1558             {
1559 4 50 33     12 $body .= "\n" if $last_pod || $this_pod;
1560 4         4 $body .= "\n";
1561             }
1562            
1563 15         18 $body .= $line;
1564 15         15 $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     614 unless ( defined $rr )
    100 66        
    100          
1572             {
1573 5         4 push @{$rs->{doc_items}}, "=ORDINARY";
  5         9  
1574 5 50       7 push @{$rs->{doc_items}}, process_doc($body) if defined $body;
  5         7  
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         83 push @{$rs->{doc_items}}, $rr;
  125         160  
1582 125         190 weaken $rs->{doc_items}[-1];
1583 125 50       161 push @{$rs->{doc_items}}, process_doc($body, 1) if defined $body;
  125         158  
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         23 push @{$rs->{doc_items}}, "=INCLUDE $rr->{ruleset}";
  27         55  
1592            
1593             # If any body text was specified, then add it as an ordinary paragraph
1594             # after the inclusion.
1595            
1596 27 100       51 if ( $body ne '' )
1597             {
1598 1         1 push @{$rs->{doc_items}}, "=ORDINARY";
  1         1  
1599 1 50       5 push @{$rs->{doc_items}}, process_doc($body) if defined $body;
  1         4  
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 111 my ($docstring, $item_body) = @_;
1614            
1615 131         97 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         257 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 5 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       11 return '' if $state->{processed}{$rs->{name}};
1660            
1661 6         6 $state->{processed}{$rs->{name}} = 1;
1662 6         6 $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         6 foreach my $item ( @{$rs->{doc_items}} )
  6         8  
1675             {
1676             # An item record starts a list if not already in one.
1677            
1678 38 100 66     144 if ( ref $item && defined $item->{param} )
    100          
    100          
1679             {
1680 10 100       13 unless ( $state->{in_list} )
1681             {
1682 5 100       10 $doc .= "\n\n" if $doc ne '';
1683 5         4 $doc .= "=over";
1684 5         5 $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       7 $doc .= "\n\n" if $doc ne '';
1697 3         3 $doc .= "=back";
1698 3         4 $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         3 my $included_rs = $self->{RULESETS}{$1};
1707            
1708 2 50       4 if ( ref $included_rs eq 'HTTP::Validate::Ruleset' )
1709             {
1710 2         10 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       4 $doc .= $subdoc if $subdoc ne '';
1714             }
1715             }
1716            
1717             # All other strings are added as-is.
1718            
1719             else
1720             {
1721 18 100 100     46 $doc .= "\n\n" if $doc ne '' && $item ne '';
1722 18         28 $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       6 $doc .= "\n\n=back" if $state->{in_list};
1733 4         3 $state->{in_list} = 0;
1734 4         4 $doc .= "\n";
1735             }
1736            
1737 6         10 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     8 $uniq ||= {};
1751            
1752 3 50       7 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         9  
1757             {
1758 7 100       13 if ( $rule->{type} eq 'param' )
    50          
1759             {
1760 5         9 push @params, $rule->{param};
1761             }
1762            
1763             elsif ( $rule->{type} eq 'include' )
1764             {
1765 2         6 push @params, $self->generate_param_list($rule->{ruleset}, $uniq);
1766             }
1767             }
1768            
1769 3         9 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 49 my ($self, $context, $input_params) = @_;
1781            
1782             # First check the types of the arguments to this function.
1783            
1784 60 50 33     379 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       86 $context = {} unless defined $context;
1788            
1789 60 50       92 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         53 my $unpacked_params = {};
1795            
1796 60 100       145 if ( reftype $input_params eq 'HASH' )
    50          
1797             {
1798 29         69 %$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     76 while ( ref $input_params->[0] && reftype $input_params->[0] eq 'HASH' )
1809             {
1810 3         2 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         51 while ( @$input_params )
1821             {
1822 72         51 my $p = shift @$input_params;
1823            
1824 72 50       70 if ( ref $p )
1825             {
1826 0         0 croak "invalid parameter '$p'";
1827             }
1828            
1829             else
1830             {
1831 72         81 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         53 my %settings = %{$self->{SETTINGS}};
  60         194  
1846            
1847 60         229 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         112 return bless $vr, 'HTTP::Validate::Progress';
1857             }
1858              
1859              
1860             sub add_param {
1861              
1862 78     78 0 62 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     207 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     19 $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         162 $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 59 my ($self, $vr, $ruleset_name) = @_;
1899            
1900 60 50 33     195 croak "you must provide a ruleset name" unless defined $ruleset_name && $ruleset_name ne '';
1901 60 50 33     293 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         88 $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       109 if ( $vr->{rs}{$ruleset_name} != 2 )
1913             {
1914 1         2 my @names = @{$self->{RULESETS}{$ruleset_name}{fulfill_order}};
  1         5  
1915 1 50       5 my $msg = @names == 1 ? 'ERR_REQ_SINGLE': 'ERR_REQ_MULT';
1916 1         7 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         89 my $result = bless {}, 'HTTP::Validate::Result';
1922            
1923             # Add the clean-value hash and the raw-value hash
1924            
1925 60         86 $result->{clean} = $vr->{clean};
1926 60         60 $result->{clean_list} = $vr->{clean_list};
1927 60         55 $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         62 $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         67 $result->{ec} = $vr->{ec};
1944 60         63 $result->{er} = $vr->{er};
1945 60         55 $result->{wc} = $vr->{wc};
1946 60         91 $result->{wn} = $vr->{wn};
1947 60         49 $result->{ig} = $vr->{ig};
1948            
1949             # Now check for unrecognized parameters, and generate errors or warnings
1950             # for them.
1951            
1952 60 100       119 return $result if $self->{SETTINGS}{ignore_unrecognized};
1953            
1954 52         38 foreach my $key (keys %{$vr->{raw}})
  52         105  
1955             {
1956 109 100 66     192 next if exists $vr->{ps}{$key} or exists $vr->{ig}{$key};
1957            
1958 4 100       7 if ( $self->{SETTINGS}{permissive} )
1959             {
1960 2         2 unshift @{$result->{wn}}, [$key, "unknown parameter '$key'"];
  2         7  
1961 2         4 $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         212 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 77 my ($self, $vr, $ruleset_name) = @_;
1983            
1984 82 50       106 die "Missing ruleset" unless defined $ruleset_name;
1985            
1986 82         85 my $rs = $self->{RULESETS}{$ruleset_name};
1987            
1988             # Throw an error if this ruleset does not exist.
1989            
1990 82 50       129 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       130 return if exists $vr->{rs}{$ruleset_name};
1996 82         105 $vr->{rs}{$ruleset_name} = 1;
1997            
1998             # Mark the ruleset as fulfilled if it has no non-optional parameters.
1999            
2000 82 100 66     169 $vr->{rs}{$ruleset_name} = 2 unless ref $rs->{fulfill_order} && @{$rs->{fulfill_order}};
  82         244  
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         75 foreach my $rr (@{$rs->{rules}})
  82         132  
2007             {
2008 214         190 my $type = $rr->{type};
2009 214         195 my $param = $rr->{param};
2010 214   100     457 my $key = $rr->{key} || $param;
2011 214         118 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     380 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       279 next RULE if $vr->{ig}{$key};
2023            
2024             # Otherwise check to see if the parameter or any of its aliases were
2025             # specified. If so, then collect up their values.
2026            
2027 170         130 foreach my $name ( $rr->{param}, @{$rr->{alias}} )
  170         222  
2028             {
2029 177 100       269 next unless exists $vr->{raw}{$name};
2030 115         100 $names_found{$name} = 1;
2031 115         96 my $v = $vr->{raw}{$name};
2032 115 100       167 push @raw_values, grep { defined $_ && $_ ne '' } ref $v eq 'ARRAY' ? @$v : $v;
  118 100       406  
2033             # Make sure this parameter exists in {ps}, but don't
2034             # change its status if any.
2035 115 50       249 $vr->{ps}{$name} = undef unless exists $vr->{ps}{$name};
2036             }
2037            
2038             # If more than one of the aliases for this parameter was
2039             # specified, and the 'multiple' option was not specified,
2040             # then generate an error and go on to the next rule.
2041            
2042 170 100 66     849 if ( keys(%names_found) > 1 && ! $rr->{multiple} )
    50 66        
    100 100        
    100          
2043             {
2044 1         9 add_error($vr, $rr, 'ERR_MULT_NAMES', { param => [ sort keys %names_found ] });
2045 1         4 next RULE;
2046             }
2047            
2048             # If a clean value has already been determined for this parameter,
2049             # then it was already recognized by some other rule.
2050             # Consequently, this rule can be ignored.
2051            
2052             elsif ( exists $vr->{clean}{$key} )
2053             {
2054 0         0 next RULE;
2055             }
2056            
2057             # If no values were specified for this parameter, check
2058             # to see if the rule includes a default value. If so, use that
2059             # instead and go on to the next rule.
2060            
2061             elsif ( ! @raw_values && exists $rr->{default} )
2062             {
2063 1         3 $vr->{clean}{$key} = $rr->{default};
2064 1         2 push @{$vr->{clean_list}}, $key;
  1         2  
2065 1         4 next RULE;
2066             }
2067            
2068             # If more than one value was given and the rule does not include
2069             # the 'multiple' directive, signal an error.
2070            
2071             elsif ( @raw_values > 1 && ! $rr->{multiple} )
2072             {
2073 2         12 add_error($vr, $rr, 'ERR_MULT_VALUES',
2074             { param => [ sort keys %names_found ], value => \@raw_values });
2075 2         7 next RULE;
2076             }
2077            
2078             # Now we can process the rule. If the 'split' directive was
2079             # given, split the value(s) using the specified regexp.
2080            
2081 166 100       222 if ( $rr->{split} )
2082             {
2083             # Split all of the raw values, and discard empty strings.
2084            
2085 22 50       64 my @new_values = grep { defined $_ && $_ ne '' }
2086 20         23 map { split $rr->{split}, $_ } @raw_values;
  9         53  
2087 20         26 @raw_values = @new_values;
2088             }
2089            
2090             # If this is a 'flag' parameter and the parameter was present but
2091             # no values were given, assume the value '1'.
2092            
2093 166 100 100     246 if ( $rr->{flag} && keys(%names_found) && ! @raw_values )
      66        
2094             {
2095 2         3 @raw_values = (1);
2096             }
2097            
2098             # At this point, if there are no values then generate an error if
2099             # the parameter is mandatory. Otherwise just skip this rule.
2100            
2101 166 100       206 unless ( @raw_values )
2102             {
2103             add_error($vr, $rr, 'ERR_MANDATORY', { param => $rr->{param} })
2104 67 100       111 if $rr->{mandatory};
2105            
2106 67         115 next RULE;
2107             }
2108            
2109             # Now we process each value in turn.
2110            
2111 99         73 my @clean_values;
2112             my $error_flag;
2113            
2114             VALUE:
2115 99         96 foreach my $raw_val ( @raw_values )
2116             {
2117             # If no validators were defined, just pass all of the values
2118             # that are not empty.
2119            
2120 111 100       153 unless ( $rr->{validators} )
2121             {
2122 40 50 33     109 if ( defined $raw_val && $raw_val ne '' )
2123             {
2124 40 100       98 $raw_val = $rr->{cleaner}($raw_val) if ref $rr->{cleaner} eq 'CODE';
2125 40         41 push @clean_values, $raw_val;
2126             }
2127            
2128 40         41 next VALUE;
2129             }
2130            
2131             # Otherwise, check each value against the validators in turn until
2132             # one of them passes the value or until we have tried them
2133             # all.
2134            
2135 71         49 my $result;
2136            
2137             VALIDATOR:
2138 71         39 foreach my $validator ( @{$rr->{validators}} )
  71         87  
2139             {
2140 71         111 $result = $validator->($raw_val, $vr->{context});
2141            
2142             # If the result is not a hash ref, then the value passes
2143             # the test.
2144            
2145 71 100 66     300 last VALIDATOR unless ref $result && reftype $result eq 'HASH';
2146            
2147             # If the result contains an 'error' key, then we need to
2148             # try the next validator (if any). Otherwise, the value
2149             # passes the test.
2150            
2151 67 100       110 last VALIDATOR unless $result->{error};
2152             }
2153            
2154             # If the last validator to be tried generated an error, then
2155             # the value is bad. We must report it and skip to the next value.
2156            
2157 71 100 66     172 if ( ref $result and $result->{error} )
2158             {
2159             # If the rule contains a 'warn' directive, then generate a
2160             # warning. But the value is still bad, and will be
2161             # ignored.
2162            
2163 25 100       34 if ( $rr->{warn} )
2164             {
2165             my $msg = $rr->{warn} ne '1' ? $rr->{warn} :
2166 8 50 33     41 $rr->{ERR_INVALID} || $rr->{errmsg} || $result->{error};
2167 8         33 add_warning($vr, $rr, $msg, { param => [ keys %names_found ], value => $raw_val });
2168             }
2169            
2170             # Otherwise, generate an error.
2171            
2172             else
2173             {
2174 17   33     48 my $msg = $rr->{ERR_INVALID} || $rr->{errmsg} || $result->{error};
2175 17         67 add_error($vr, $rr, $msg, { param => [ sort keys %names_found ], value => $raw_val });
2176             }
2177            
2178 25         42 $error_flag = 1;
2179 25         38 next VALUE;
2180             }
2181            
2182             # If the result contains a 'warn' field, then generate a
2183             # warning. In this case, the value is still assumed to be
2184             # good.
2185            
2186 46 100 66     98 if ( ref $result and $result->{warn} )
2187             {
2188 1         6 add_warning($vr, $rr, $result->{warn}, { param => [ sort keys %names_found ], value => $raw_val });
2189             }
2190            
2191             # If we get here, then the value is good. If the result was a
2192             # hash ref with a 'value' field, we use that for the clean
2193             # value. Otherwise, we use the raw value.
2194            
2195 46 100 66     105 my $value = ref $result && exists $result->{value} ? $result->{value} : $raw_val;
2196            
2197             # If a cleaning subroutine was defined, pass the value through
2198             # it and save the cleaned value.
2199            
2200 46 50       64 $value = $rr->{cleaner}($value) if ref $rr->{cleaner} eq 'CODE';
2201            
2202 46         77 push @clean_values, $value;
2203             }
2204            
2205             # If clean values were found, store them. If multiple values are
2206             # allowed, then we store them as a list. Otherwise, there should
2207             # only be one clean value and so we just store it as a scalar.
2208            
2209 99 100       113 if ( @clean_values )
2210             {
2211 78         55 push @{$vr->{clean_list}}, $key;
  78         105  
2212            
2213 78 100       95 if ( $rr->{multiple} )
2214             {
2215 7         10 $vr->{clean}{$key} = \@clean_values;
2216             }
2217            
2218             else
2219             {
2220 71         91 $vr->{clean}{$key} = $clean_values[0];
2221             }
2222             }
2223            
2224             # If raw values were found for this parameter, but none of them
2225             # pass the validators, then we need to indicate this condition.
2226            
2227             else
2228             {
2229 21         17 push @{$vr->{clean_list}}, $key;
  21         30  
2230            
2231 21 100 100     60 if ( defined $rr->{bad_value} && $rr->{bad_value} eq 'ERROR' )
    100          
2232             {
2233 2         12 add_error($vr, $rr, 'ERR_BAD_VALUES',
2234             { param => [ sort keys %names_found ], value => \@raw_values });
2235 2         5 $vr->{clean}{$key} = undef;
2236 2         4 $error_flag = 1;
2237             }
2238            
2239             elsif ( defined $rr->{bad_value} )
2240             {
2241 1 50       8 $vr->{clean}{$key} = $rr->{multiple} ? [ $rr->{bad_value} ] : $rr->{bad_value};
2242             }
2243            
2244             else
2245             {
2246 18         22 $vr->{clean}{$key} = undef;
2247             }
2248             }
2249            
2250             # Set the status of this parameter to 1 (passed) unless an error
2251             # was generated, 0 (failed) otherwise.
2252            
2253 99 100       136 $vr->{ps}{$param} = $error_flag ? 0 : 1;
2254            
2255             # If this rule is not 'optional', then set the status of this
2256             # ruleset to 'fulfilled' (2). That does not mean that the validation
2257             # passes, because the parameter value may still have generated an
2258             # error.
2259            
2260 99 100       163 unless ( $rr->{optional} )
2261             {
2262 77         152 $vr->{rs}{$ruleset_name} = 2;
2263             }
2264             }
2265            
2266             # An 'ignore' directive causes the parameter to be recognized, but no
2267             # cleaned value is generated and the containing ruleset is not
2268             # triggered. No error messages will be generated for this parameter,
2269             # either.
2270            
2271             elsif ( $rr->{type} eq 'ignore' )
2272             {
2273             # Make sure that the parameter is counted as having been
2274             # recognized.
2275            
2276 0         0 foreach my $param ( @{$rr->{param}} )
  0         0  
2277             {
2278 0         0 $vr->{ps}{$param} = undef;
2279            
2280             # Make sure that errors, warnings, and cleaned values for this key
2281             # are ignored.
2282            
2283 0   0     0 my $key = $rr->{key} || $param;
2284 0         0 $vr->{ig}{$key} = 1;
2285 0         0 delete $vr->{clean}{$param};
2286             }
2287             }
2288            
2289             # A 'together' or 'at_most_one' rule requires checking the presence
2290             # of each of the specified parameters. This kind of rule does not
2291             # affect the status of any parameters or rulesets, but if violated
2292             # will generate an error message and cause the entire validation to
2293             # fail.
2294            
2295             elsif ( $rr->{type} eq 'together' or $rr->{type} eq 'at_most_one' )
2296             {
2297             # We start by listing those that are present in the parameter set.
2298            
2299 12         8 my @present = grep exists $vr->{raw}{$_}, @{$rr->{param}};
  12         34  
2300            
2301             # For a 'together' rule, the count must equal the number of
2302             # arguments to this rule, or must be zero. In other words, there
2303             # must be none present or all present.
2304            
2305 12 100 100     71 if ( $rr->{type} eq 'together' and @present > 0 and @present < @{$rr->{param}} )
  1 100 66     4  
      100        
2306             {
2307 1         7 add_error_warn($vr, $rr, 'ERR_TOGETHER', { param => $rr->{param} });
2308             }
2309            
2310             # For a 'at_most_one' rule, the count must be less than or equal
2311             # to one (i.e. not more than one must have been specified).
2312            
2313             elsif ( $rr->{type} eq 'at_most_one' and @present > 1 )
2314             {
2315 2         6 add_error_warn($vr, $rr, 'ERR_AT_MOST', { param => \@present });
2316             }
2317             }
2318            
2319             # For an 'include' rule, we immediately check the given ruleset
2320             # (unless it has already been checked). This statement essentially
2321             # includes one ruleset within another. It is very powerful, because
2322             # it allows different route handlers to to validate their parameters
2323             # using common rulesets.
2324            
2325             elsif ( $rr->{type} eq 'include' )
2326             {
2327 22         24 my $rs_name = $rr->{ruleset};
2328            
2329             # First try to validate the given ruleset.
2330            
2331 22         60 $self->validate_ruleset($vr, $rs_name);
2332            
2333             # If it was a 'require' rule, check to see if the ruleset was
2334             # fulfilled.
2335            
2336 22 100 100     62 if ( $rr->{require} and not $vr->{rs}{$rs_name} == 2 )
2337             {
2338 1         2 my (@missing, %found);
2339            
2340 1         1 @missing = grep { unique($_, \%found) } @{$self->{RULESETS}{$rs_name}{fulfill_order}};
  2         3  
  1         3  
2341            
2342 1 50       5 my $msg = @missing == 1 ? 'ERR_REQ_SINGLE' : 'ERR_REQ_MULT';
2343 1         9 add_error_warn($vr, $rr, $msg, { param => \@missing });
2344             }
2345             }
2346            
2347             elsif ( $rr->{type} eq 'constraint' )
2348             {
2349             # From the list of rulesets specified in this rule, check how many
2350             # were and were not fulfilled.
2351            
2352 6         4 my @fulfilled = grep { $vr->{rs}{$_} == 2 } @{$rr->{ruleset}};
  12         27  
  6         10  
2353 6         5 my @not_fulfilled = grep { $vr->{rs}{$_} != 2 } @{$rr->{ruleset}};
  12         22  
  6         15  
2354            
2355             # For a 'require_one' or 'require_any' rule, generate an error if
2356             # not enough of the rulesets are fulfilled. List all of the
2357             # parameters which could be given in order to fulfill these
2358             # rulesets.
2359            
2360 6 100 66     45 if ( @fulfilled == 0 and ( $rr->{constraint} eq 'require_one' or
    50 66        
      33        
      33        
2361             $rr->{constraint} eq 'require_any' ) )
2362             {
2363 4         4 my (@missing, %found);
2364            
2365 12         15 @missing = grep { unique($_, \%found) }
2366 4         4 map { @{$self->{RULESETS}{$_}{fulfill_order}} } @not_fulfilled;
  8         5  
  8         16  
2367            
2368 4 50       7 my $msg = @missing == 1 ? 'ERR_REQ_SINGLE' : 'ERR_REQ_MULT';
2369 4         9 add_error_warn($vr, $rr, $msg, { param => \@missing });
2370             }
2371            
2372             # For an 'allow_one' or 'require_one' rule, generate an error if
2373             # more than one of the rulesets was fulfilled.
2374            
2375             elsif ( @fulfilled > 1 and ($rr->{constraint} eq 'allow_one' or
2376             $rr->{constraint} eq 'require_one') )
2377             {
2378 2         2 my @params;
2379 2         3 my ($label) = "A";
2380            
2381 2         4 foreach my $rs ( @fulfilled )
2382             {
2383 4         8 push @params, "($label)"; $label++;
  4         4  
2384 4         9 push @params, @{$self->{RULESETS}{$rs}{fulfill_order}}
2385 4 50       17 if ref $self->{RULESETS}{$rs}{fulfill_order} eq 'ARRAY';
2386             }
2387            
2388 2         4 my $message = 'ERR_REQ_ONE';
2389            
2390 2         7 add_error_warn($vr, $rr, 'ERR_REQ_ONE', { param => \@params });
2391             }
2392             }
2393            
2394             # For a 'content_type' rule, we set the content type of the response
2395             # according to the given parameter.
2396            
2397             elsif ( $type eq 'content_type' )
2398             {
2399 4         5 my $param = $rr->{param};
2400 4   100     14 my $value = $vr->{raw}{$param} || '';
2401 4   33     12 my $clean_name = $rr->{key} || $rr->{param};
2402 4         8 my ($selected, $selected_type);
2403            
2404 4         3 push @{$vr->{clean_list}}, $key;
  4         7  
2405            
2406 4 100       9 if ( $rr->{type_map}{$value} )
2407             {
2408 3         6 $vr->{content_type} = $rr->{type_map}{$value};
2409 3         3 $vr->{clean}{$clean_name} = $value;
2410 3         8 $vr->{ps}{$param} = 1;
2411             }
2412            
2413             else
2414             {
2415 1         3 $vr->{content_type} = 'unknown';
2416 1         2 $vr->{clean}{$clean_name} = undef;
2417 1         2 $vr->{ps}{$param} = 1;
2418 1   50     5 $rr->{key} ||= '_content_type';
2419 1         9 add_error_warn($vr, $rr, 'ERR_MEDIA_TYPE', { param => $param, value => $rr->{type_list} });
2420             }
2421             }
2422             }
2423             };
2424              
2425              
2426             # Helper function - given a hashref to use as a scratchpad, returns true the
2427             # first time a given argument is encountered and false each subsequent time.
2428             # This can be reset by calling it with a newly emptied scratchpad.
2429              
2430             sub unique {
2431            
2432 14     14 0 13 my ($arg, $scratch) = @_;
2433            
2434 14 50       20 return if exists $scratch->{$arg};
2435 14         22 $scratch->{$arg} = 1;
2436             }
2437              
2438              
2439             # Add an error message to the current validation.
2440              
2441             sub add_error {
2442              
2443 34     34 0 35 my ($vr, $rr, $msg, $subst) = @_;
2444            
2445             # If no message was given, use a default one. It's not a very good
2446             # message, but what can we do?
2447            
2448 34   50     56 $msg ||= 'ERR_DEFAULT';
2449            
2450             # If the given message starts with 'ERR_', assume it is an error code. If
2451             # the code is present as an attribute of the rule record, use the
2452             # corresponding value as the message. Otherwise, use the global value.
2453            
2454 34 100       173 if ( $msg =~ qr{^ERR_} )
2455             {
2456 17   33     67 $msg = $rr->{$msg} || $vr->{settings}{$msg} || $ERROR_MSG{$msg} || $ERROR_MSG{ERR_DEFAULT};
2457             }
2458            
2459             # Next, figure out the error key. If the rule has a 'key' directive, use
2460             # that. Otherwise determine it according to the rule type, ruleset name,
2461             # and rule number.
2462            
2463             my $err_key = $rr->{key} ? $rr->{key}
2464             : $rr->{type} eq 'param' ? $rr->{param}
2465 34 50       118 : $rr->{type} eq 'content_type' ? '_content_type'
    100          
    100          
2466             : "_$rr->{rs}{name}_$rr->{rn}";
2467            
2468             # Record the error message under the key, and add the key to the error
2469             # list. Other rules might later remove or alter the error
2470             # message.
2471            
2472 34         18 push @{$vr->{er}}, [$err_key, subst_error($msg, $subst)];
  34         72  
2473 34         111 $vr->{ec}{$err_key}++;
2474             }
2475              
2476              
2477             # Add a warning message to the current validation. The $subst hash if
2478             # given specifies placeholder substitutions.
2479              
2480             sub add_warning {
2481              
2482 11     11 0 14 my ($vr, $rr, $msg, $subst) = @_;
2483            
2484             # If no message was given, use a default one. It's not a very good
2485             # message, but what can we do?
2486            
2487 11   50     20 $msg ||= 'ERR_DEFAULT';
2488            
2489             # If the given message starts with 'ERR_', assume it is an error code. If
2490             # the code is present as an attribute of the rule record, use the
2491             # corresponding value as the message. Otherwise, use the global value.
2492            
2493 11 100       54 if ( $msg =~ qr{^ERR_} )
2494             {
2495 1   0     6 $msg = $rr->{$msg} || $vr->{settings}{$msg} || $ERROR_MSG{$msg} || $ERROR_MSG{ERR_DEFAULT};
2496             }
2497            
2498             # Next, figure out the warning key. If the rule has a 'key' directive, use
2499             # that. Otherwise determine it according to the rule type, ruleset name,
2500             # and rule number.
2501            
2502             my $warn_key = $rr->{key} ? $rr->{key}
2503             : $rr->{type} eq 'param' ? $rr->{param}
2504 11 50       42 : $rr->{type} eq 'content_type' ? '_content_type'
    100          
    50          
2505             : "_$rr->{rs}{name}_$rr->{rn}";
2506            
2507             # Record the warning message under the key. Other rules might later
2508             # alter the warning message if they use the same key.
2509            
2510 11         12 push @{$vr->{wn}}, [$warn_key, subst_error($msg, $subst)];
  11         24  
2511 11         31 $vr->{wc}{$warn_key}++;
2512             }
2513              
2514              
2515             # Add an error or warning message to the current validation. If the rule has
2516             # a 'warn' attribute, add a warning. Otherwise, add an error. If the rule
2517             # has an 'errmsg' attribute, use its value instead of the error message given.
2518              
2519             sub add_error_warn {
2520            
2521 11     11 0 12 my ($vr, $rr, $msg, $subst) = @_;
2522            
2523 11 50       20 $msg = $rr->{errmsg} if $rr->{errmsg};
2524            
2525 11 100       18 if ( $rr->{warn} )
2526             {
2527 2 100       5 $msg = $rr->{warn} if $rr->{warn} ne '1';
2528 2         4 return add_warning($vr, $rr, $msg, $subst);
2529             }
2530            
2531             else
2532             {
2533 9         12 return add_error($vr, $rr, $msg, $subst);
2534             }
2535             }
2536              
2537              
2538             # Substitute placeholders in an error or warning message.
2539              
2540             sub subst_error {
2541              
2542 45     45 0 44 my ($message, $subst) = @_;
2543            
2544 45         159 while ( $message =~ /^(.*)\{(\w+)\}(.*)$/ )
2545             {
2546 46         73 my $value = $subst->{$2};
2547            
2548 46 100 33     86 if ( ref $value )
    50          
2549             {
2550 42 50       83 if ( reftype $value eq 'ARRAY' )
    0          
2551             {
2552 42         53 $value = name_list(@$value);
2553             }
2554             elsif ( reftype $value eq 'HASH' )
2555             {
2556 0         0 $value = name_list(sort keys %$value);
2557             }
2558             }
2559            
2560             elsif ( defined $value && $value !~ /^'/ )
2561             {
2562 4         9 $value = "'$value'";
2563             }
2564            
2565             else
2566             {
2567 0         0 $value = "''";
2568             }
2569            
2570 46 50 33     284 $message = "$1$value$3" if defined $value and $value ne '';
2571             }
2572            
2573 45         76 return $message;
2574             }
2575              
2576              
2577             # Generate a list of quoted strings from the specified values.
2578              
2579             sub name_list {
2580            
2581 42     42 0 53 my @names = @_;
2582            
2583 42 50       66 return unless @names;
2584 42         111 return "'" . join("', '", @names) . "'";
2585             };
2586              
2587              
2588             package HTTP::Validate::Result;
2589              
2590             =head1 OTHER METHODS
2591              
2592             The result object returned by L provides the following
2593             methods:
2594              
2595             =head3 passed
2596              
2597             Returns true if the validation passed, false otherwise.
2598              
2599             =cut
2600              
2601             sub passed {
2602            
2603 7     7   265 my ($self) = @_;
2604            
2605             # If any errors occurred, then the validation failed.
2606            
2607 7 100 66     22 return if ref $self->{er} eq 'ARRAY' && @{$self->{er}};
  2         14  
2608            
2609             # Otherwise, it passed.
2610            
2611 5         14 return 1;
2612             }
2613              
2614              
2615             =head3 errors
2616              
2617             In a scalar context, this returns the number of errors generated by this
2618             validation. In a list context, it returns a list of error messages. If an
2619             argument is given, only messages whose key equals the argument are returned.
2620              
2621             =cut
2622              
2623             sub errors {
2624              
2625 50     50   885 my ($self, $key) = @_;
2626            
2627             # In scalar context, just return the count.
2628            
2629 50 100       92 if ( ! wantarray )
    100          
2630             {
2631 20 100       86 return 0 unless defined $key ? ref $self->{ec} : ref $self->{er};
    100          
2632 8 100 50     19 return defined $key ? ($self->{ec}{$key} || 0) : scalar @{$self->{er}};
  5         18  
2633             }
2634            
2635             # In list context, if a key is given then return just the matching error
2636             # messages or an empty list if there are none.
2637            
2638             elsif ( defined $key )
2639             {
2640 4 100       10 return unless ref $self->{ec};
2641 3         3 return map { $_->[1] } grep { $_->[0] eq $key } @{$self->{er}};
  3         8  
  3         7  
  3         5  
2642             }
2643            
2644             # If no key is given, just return all of the messages.
2645            
2646             else
2647             {
2648 26         22 return map { $_->[1] } @{$self->{er}};
  17         78  
  26         47  
2649             }
2650             }
2651              
2652             =head3 error_keys
2653              
2654             Returns the list of keys for which error messages were generated.
2655              
2656             =cut
2657              
2658             sub error_keys {
2659            
2660 6     6   547 my ($self) = @_;
2661 6         5 return keys %{$self->{ec}};
  6         46  
2662             }
2663              
2664              
2665             =head3 warnings
2666              
2667             In a scalar context, this returns the number of warnings generated by the
2668             validation. In a list context, it returns a list of warning messages. If an
2669             argument is given, only messages whose key equals the argument are returned.
2670              
2671             =cut
2672              
2673             sub warnings {
2674              
2675 29     29   1538 my ($self, $key) = @_;
2676            
2677             # In scalar context, just return the count.
2678            
2679 29 100       56 if ( ! wantarray )
    100          
2680             {
2681 18 100       75 return 0 unless defined $key ? ref $self->{wc} : ref $self->{wn};
    100          
2682 6 100 50     15 return defined $key ? ($self->{wc}{$key} || 0) : scalar @{$self->{wn}};
  4         12  
2683             }
2684            
2685             # In list context, if a key is given then return just the matching warning
2686             # messages or an empty list if there are none.
2687            
2688             elsif ( defined $key )
2689             {
2690 2 50       7 return unless ref $self->{wn};
2691 2         1 return map { $_->[1] } grep { $_->[0] eq $key } @{$self->{wn}};
  2         5  
  2         4  
  2         5  
2692             }
2693            
2694             # If no key is given, just return all of the messages.
2695            
2696             else
2697             {
2698 9         5 return map { $_->[1] } @{$self->{wn}};
  6         14  
  9         18  
2699             }
2700             }
2701              
2702              
2703             =head3 warning_keys
2704              
2705             Returns the list of keys for which warning messages were generated.
2706              
2707             =cut
2708              
2709             sub warning_keys {
2710            
2711 1     1   2 my ($self) = @_;
2712 1         2 return keys %{$self->{wc}};
  1         8  
2713             }
2714              
2715              
2716             =head3 keys
2717              
2718             In a scalar context, this returns the number of parameters that had valid values. In a list
2719             context, it returns a list of parameter names in the order they were recognized. Individual
2720             parameter values can be gotten by using either L or L.
2721              
2722             =cut
2723              
2724             sub keys {
2725              
2726 5     5   12 my ($self) = @_;
2727            
2728             # Return the list of parameter keys in the order they were recognized.
2729            
2730 5         5 return @{$self->{clean_list}};
  5         16  
2731             }
2732              
2733              
2734             =head3 values
2735              
2736             Returns the hash of clean parameter values. This is not a copy, so any
2737             modifications you make to it will be reflected in subsequent calls to L.
2738              
2739             =cut
2740              
2741             sub values {
2742            
2743 2     2   7 my ($self) = @_;
2744            
2745             # Return the clean value hash.
2746            
2747 2         3 return $self->{clean};
2748             }
2749              
2750             =head3 value
2751              
2752             Returns the value of the specified parameter, or undef if that parameter was
2753             not specified in the request or if its value was invalid.
2754              
2755             =cut
2756              
2757             sub value {
2758              
2759 54     54   4064 my ($self, $param) = @_;
2760            
2761 54         171 return $self->{clean}{$param};
2762             }
2763              
2764              
2765             =head3 specified
2766              
2767             Returns true if the specified parameter was specified in the request with at least
2768             one value, whether or not that value was valid. Returns false otherwise.
2769              
2770             =cut
2771              
2772             sub specified {
2773            
2774 5     5   320 my ($self, $param) = @_;
2775            
2776 5         13 return exists $self->{clean}{$param};
2777             }
2778              
2779              
2780             =head3 raw
2781              
2782             Returns a hash of the raw parameter values as originally provided to
2783             L. Multiple values are represented by array refs. The
2784             result of this method can be used, for example, to redisplay a web form if the
2785             submission resulted in errors.
2786              
2787             =cut
2788              
2789             sub raw {
2790            
2791 1     1   3 my ($self, $param) = @_;
2792            
2793 1         3 return $self->{raw};
2794             }
2795              
2796              
2797             =head3 content_type
2798              
2799             This returns the content type specified by the request parameters. If none
2800             was specified, or if no content_type rule was included in the validation, it
2801             returns undef.
2802              
2803             =cut
2804              
2805             sub content_type {
2806              
2807 3     3   262 my ($self) = @_;
2808            
2809 3         12 return $self->{content_type};
2810             }
2811              
2812              
2813             package HTTP::Validate;
2814              
2815             # At the very end, we have the validator functions
2816             # ================================================
2817              
2818             =head1 VALIDATORS
2819              
2820             Parameter rules can each include one or more validator functions under the key
2821             C. The job of these functions is two-fold: first to check for good
2822             parameter values, and second to generate cleaned values.
2823              
2824             There are a number of validators provided by this module, or you can specify a
2825             reference to a function of your own.
2826              
2827             =head2 Predefined validators
2828              
2829             =head3 INT_VALUE
2830              
2831             This validator accepts any integer, and rejects all other values. It
2832             returns a numeric value, generated by adding 0 to the raw parameter value.
2833              
2834             =head3 INT_VALUE(min,max)
2835              
2836             This validator accepts any integer between C and C (inclusive). If either C
2837             or C is undefined, that bound will not be tested.
2838              
2839             =head3 POS_VALUE
2840              
2841             This is an alias for C.
2842              
2843             =head3 POS_ZERO_VALUE
2844              
2845             This is an alias for C.
2846              
2847             =cut
2848              
2849             sub int_value {
2850              
2851 43     43 0 48 my ($value, $context, $min, $max) = @_;
2852            
2853 43 100       143 unless ( $value =~ /^([+-]?\d+)$/ )
2854             {
2855 9         31 return { error => "bad value '$value' for {param}: must be an integer" };
2856             }
2857            
2858 34 100 100     125 if ( defined $min and $value < $min )
2859             {
2860 7 50       29 my $criterion = defined $max ? "between $min and $max"
    100          
    100          
2861             : $min == 0 ? "nonnegative"
2862             : $min == 1 ? "positive"
2863             : "at least $min";
2864            
2865 7         24 return { error => "bad value '$value' for {param}: must be $criterion" };
2866             }
2867            
2868 27 100 100     52 if ( defined $max and $value > $max )
2869             {
2870 1 50       5 my $criterion = defined $min ? "between $min and $max" : "at most $max";
2871            
2872 1         4 return { error => "bad value '$value' for {param} must be $criterion" };
2873             }
2874            
2875 26         59 return { value => $value + 0 };
2876             }
2877              
2878             sub INT_VALUE {
2879            
2880 15     15 1 1886 my ($min, $max) = @_;
2881            
2882 15 100 100     133 croak "lower bound must be an integer (was '$min')" unless !defined $min || $min =~ /^[+-]?\d+$/;
2883 14 50 66     40 croak "upper bound must be an integer (was '$max')" unless !defined $max || $max =~ /^[+-]?\d+$/;
2884            
2885 14 100 66     72 return \&int_value unless defined $min or defined $max;
2886 6     5   24 return sub { return int_value(shift, shift, $min, $max) };
  5         7  
2887             };
2888              
2889             sub POS_VALUE {
2890            
2891 17     17 1 2534 return sub { return int_value(shift, shift, 1) };
  29     29   44  
2892             };
2893              
2894             sub POS_ZERO_VALUE {
2895              
2896 4     4 1 21 return sub { return int_value(shift, shift, 0) };
  3     3   4  
2897             };
2898              
2899              
2900             =head3 DECI_VALUE
2901              
2902             This validator accepts any decimal number, including exponential notation, and
2903             rejects all other values. It returns a numeric value, generated by adding 0
2904             to the parameter value.
2905              
2906             =head3 DECI_VALUE(min,max)
2907              
2908             This validator accepts any real number between C and C (inclusive).
2909             Specify these bounds in quotes (i.e. as string arguments) if non-zero so that
2910             they will appear properly in error messages. If either C or C is
2911             undefined, that bound will not be tested.
2912              
2913             =cut
2914              
2915             sub deci_value {
2916            
2917 14     14 0 15 my ($value, $context, $min, $max) = @_;
2918            
2919 14 100       70 unless ( $value =~ /^[+-]?(?:\d+\.\d*|\d*\.\d+|\d+)(?:[eE][+-]?\d+)?$/ )
2920             {
2921 1         3 return { error => "bad value '$value' for {param}: must be a decimal number" };
2922             }
2923            
2924 13 100 66     55 if ( defined $min and defined $max and ($value < $min or $value > $max) )
      100        
      66        
2925             {
2926 4         24 return { error => "bad value '$value' for {param}: must be between $min and $max" };
2927             }
2928            
2929 9 50 66     17 if ( defined $min and $value < $min )
2930             {
2931 0         0 return { error => "bad value '$value' for {param}: must be at least $min" };
2932             }
2933            
2934 9 50 66     19 if ( defined $max and $value > $max )
2935             {
2936 0         0 return { error => "bad value '$value' for {param}: must be at most $max" };
2937             }
2938            
2939 9         21 return { value => $value + 0 };
2940             }
2941              
2942             sub DECI_VALUE {
2943            
2944 15     15 1 368 my ($min, $max) = @_;
2945            
2946 15 100 100     107 croak "lower bound must be numeric" if defined $min && !looks_like_number($min);
2947 14 50 66     35 croak "upper bound must be numeric" if defined $max && !looks_like_number($max);
2948            
2949 14 100 66     57 return \&deci_value unless defined $min or defined $max;
2950 6     8   18 return sub { return deci_value(shift, shift, $min, $max) };
  8         11  
2951             };
2952              
2953              
2954             =head3 MATCH_VALUE
2955              
2956             This validator accepts any string that matches the specified pattern, and
2957             rejects any that does not. If you specify the pattern as a string, it will be
2958             converted into a regexp and will have ^ prepended and $ appended, and also the
2959             modifier "i". If you specify the pattern using C, then it is used unchanged.
2960             Any rule that uses this validator should be provided with an error directive, since the
2961             default error message is by necessity not very informative. The value is not
2962             cleaned in any way.
2963              
2964             =cut
2965              
2966             sub match_value {
2967              
2968 7     7 0 10 my ($value, $context, $pattern) = @_;
2969            
2970 7 100       41 return if $value =~ $pattern;
2971 3         12 return { error => "bad value '$value' for {param}: did not match the proper pattern" };
2972             }
2973              
2974             sub MATCH_VALUE {
2975              
2976 10     10 1 3146 my ($pattern) = @_;
2977            
2978 10 100 100     185 croak "MATCH_VALUE requires a regular expression" unless
      66        
2979             defined $pattern && (!ref $pattern || ref $pattern eq 'Regexp');
2980            
2981 8 100       84 my $re = ref $pattern ? $pattern : qr{^$pattern$}oi;
2982            
2983 8     7   40 return sub { return match_value(shift, shift, $re) };
  7         13  
2984             };
2985              
2986              
2987             =head3 ENUM_VALUE(string,...)
2988              
2989             This validator accepts any of the specified string values, and rejects all
2990             others. Comparisons are case insensitive. If the version of Perl is 5.016 or
2991             greater, or if the module C is available and has been
2992             required, then the C function will be used instead of the usual C when
2993             comparing values. The cleaned value will be the matching string value from
2994             this call.
2995              
2996             If any of the strings is '#', then subsequent values will be accepted but not
2997             reported in the standard error message as allowable values. This allows for
2998             undocumented values to be accepted.
2999              
3000             =cut
3001              
3002             sub enum_value {
3003            
3004 5     5 0 7 my ($value, $context, $accepted, $good_list) = @_;
3005            
3006 5         87 my $folded = $case_fold->($value);
3007            
3008             # If the value is found in the $accepted hash, then we're good. Return
3009             # the value as originally given, not the case-folded version.
3010            
3011 5 100       21 return { value => $accepted->{$folded} } if exists $accepted->{$folded};
3012            
3013             # Otherwise, then we have an error.
3014            
3015 1         4 return { error => "bad value '$value' for {param}: must be one of $good_list" };
3016             }
3017              
3018             sub ENUM_VALUE {
3019            
3020 5     5 1 1888 my (%accepted, @documented, $undoc);
3021            
3022 5         10 foreach my $k ( @_ )
3023             {
3024 9 50 33     40 next unless defined $k && $k ne '';
3025            
3026 9 50       18 if ( $k eq '#' )
3027             {
3028 0         0 $undoc = 1;
3029 0         0 next;
3030             }
3031            
3032 9         117 $accepted{ $case_fold->($k) } = $k;
3033 9 50       24 push @documented, $k unless $undoc;
3034             }
3035            
3036             #my @non_empty = grep { defined $_ && $_ ne '' } @_;
3037 5 100       77 croak "ENUM_VALUE requires at least one value" unless keys %accepted;
3038            
3039             # my %accepted = map { $case_fold->($_) => $_ } @non_empty;
3040 4         12 my $good_list = "'" . join("', '", @documented) . "'";
3041            
3042 4     5   23 return sub { return enum_value(shift, shift, \%accepted, $good_list) };
  5         11  
3043             };
3044              
3045              
3046             =head3 BOOLEAN_VALUE
3047              
3048             This validator is used for parameters that take a true/false value. It
3049             accepts any of the following values: "yes", "no", "true", "false", "on",
3050             "off", "1", "0", compared case insensitively. It returns an error if any
3051             other value is specified. The cleaned value will be 1 or 0.
3052              
3053             =cut
3054              
3055             sub boolean_value {
3056              
3057 2     2 0 2 my ($value, $context) = @_;
3058            
3059 2 50       6 unless ( ref $value )
3060             {
3061 2 50       8 if ( $value =~ /^(?:1|yes|true|on)$/i )
    0          
3062             {
3063 2         5 return { value => 1 };
3064             }
3065            
3066             elsif ( $value =~ /^(?:0|no|false|off)$/i )
3067             {
3068 0         0 return { value => 0 };
3069             }
3070             }
3071            
3072 0         0 return { error => "the value of {param} must be one of: yes, no, true, false, on, off, 1, 0" };
3073             }
3074              
3075 1     1 1 3 sub BOOLEAN_VALUE { return \&boolean_value; };
3076              
3077              
3078             =head3 FLAG_VALUE
3079              
3080             This validator should be used for parameters that are considered to be "true"
3081             if present with an empty value. The validator returns a value of 1 in this case,
3082             and behaves like 'BOOLEAN_VALUE' otherwise.
3083              
3084             =cut
3085              
3086 2     2 1 11 sub FLAG_VALUE { return 'FLAG_VALUE'; };
3087              
3088              
3089             # =head3 EMPTY_VALUE
3090              
3091             # This validator accepts only the empty value. You can use this when you want a
3092             # ruleset to be fulfilled even if the specified parameter is given an empty
3093             # value. This will typically be used along with at least one other validator for the
3094             # same parameter. For example:
3095              
3096             # define_ruleset foo =>
3097             # { param => 'bar', valid => [EMPTY_VALUE, POS_VALUE] };
3098              
3099             # This rule would be satisfied if the parameter 'bar' is given either an empty
3100             # value or a value that is a positive integer. The ruleset will be fulfilled in
3101             # either case, but will not be fulfilled if 'bar' is not mentioned at all. For
3102             # best results EMPTY_VALUE should not be the last validator in the list, because
3103             # if a value fails all of the validators then the last error message is reported
3104             # and its error message is by necessity not very helpful.
3105              
3106             # =cut
3107              
3108             # sub empty_value {
3109            
3110             # my ($value, $context) = @_;
3111            
3112             # return if !defined $value || $value eq '';
3113             # return { error => "parameter {param} must be empty unless it is given a valid value" };
3114             # }
3115              
3116             # sub EMPTY_VALUE {
3117              
3118             # return 'EMPTY_VALUE';
3119             # };
3120              
3121              
3122             =head3 ANY_VALUE
3123              
3124             This validator accepts any non-empty value. Using this validator
3125             is equivalent to not specifying any validator at all.
3126              
3127             =cut
3128              
3129             sub ANY_VALUE {
3130            
3131 4     4 1 13 return 'ANY_VALUE';
3132             };
3133              
3134              
3135             =head2 Reusing validators
3136              
3137             Every time you use a parametrized validator such as C, a new
3138             closure is generated. If you are repeating a particular set of parameters
3139             many times, to save space you may want to instantiate the validator just once:
3140              
3141             my $zero_to_ten = INT_VALUE(0,10);
3142            
3143             define_ruleset( 'foo' =>
3144             { param => 'bar', valid => $zero_to_ten },
3145             { param => 'baz', valid => $zero_to_ten });
3146              
3147             =head2 Writing your own validator functions
3148              
3149             If you wish to validate parameters which do not match any of the validators
3150             described above, you can write your own validator function. Validator
3151             functions are called with two arguments:
3152              
3153             ($value, $context)
3154              
3155             Where $value is the raw parameter value and $context is a hash ref provided
3156             when the validation process is initiated (or an empty hashref if none is
3157             provided). This allows the passing of information such as database handles to
3158             the validator functions.
3159              
3160             If your function decides that the parameter value is valid and does not need
3161             to be cleaned, it can indicate this by returning an empty result.
3162              
3163             Otherwise, it must return a hash reference with one or more of the following
3164             keys:
3165              
3166             =over 4
3167              
3168             =item error
3169              
3170             If the parameter value is not valid, the value of this key should be an error
3171             message that states I. This message should
3172             contain the placeholder {param}, which will be substituted with the parameter
3173             name. Use this placeholder, and do not hard-code the parameter name.
3174              
3175             Here is an example of a good message:
3176              
3177             "the value of {param} must be a positive integer (was {value})".
3178              
3179             Here is an example of a bad message:
3180              
3181             "bad value for 'foo'".
3182              
3183             =item warn
3184              
3185             If the parameter value is acceptable but questionable in some way, the value
3186             of this key should be a message that states what a good value should look
3187             like. All such messages will be made available through the result object that
3188             is returned by the validation routine. The code that handles the request may
3189             then choose to display these messages as part of the response. Your code may
3190             also make use of this information during the process of responding to the
3191             request.
3192              
3193             =item value
3194              
3195             If the parameter value represents anything other than a simple string (i.e. a
3196             number, list, or more complicated data structure), then the value of this key
3197             should be the converted or "cleaned" form of the parameter value. For
3198             example, a numeric parameter might be converted into an actual number by
3199             adding zero to it, or a pair of values might be split apart and converted into
3200             an array ref. The value of this key will be returned as the "cleaned" value
3201             of the parameter, in place of the raw parameter value provided in the request.
3202              
3203             =back
3204              
3205             =head3 Parametrized validators
3206              
3207             If you want to write your own parametrized validator, write a function that
3208             generates and returns a closure. For example:
3209              
3210             sub integer_multiple {
3211              
3212             my ($value, $context, $base) = @_;
3213            
3214             return { value => $value + 0 } if $value % $base == 0;
3215             return { error => "the value of {param} must be a multiple of $base (was {value})" };
3216             }
3217            
3218             sub INTEGER_MULTIPLE {
3219              
3220             my ($base) = $_[0] + 0;
3221            
3222             croak "INTEGER_MULTIPLE requires a numeric parameter greater than zero"
3223             unless defined $base and $base > 0;
3224            
3225             return sub { return integer_multiple(shift, shift, $base) };
3226             }
3227            
3228             define_ruleset( 'foo' =>
3229             { param => foo, valid => INTEGER_MULTIPLE(3) });
3230              
3231             =cut
3232              
3233              
3234              
3235             =head1 AUTHOR
3236              
3237             Michael McClennen, C<< >>
3238              
3239             =head1 SUPPORT
3240              
3241             Please report any bugs or feature requests to C, or through
3242             the web interface at L. I will be notified, and then you'll
3243             automatically be notified of progress on your bug as I make changes.
3244              
3245             =head1 LICENSE AND COPYRIGHT
3246              
3247             Copyright 2014 Michael McClennen.
3248              
3249             This program is free software; you can redistribute it and/or modify it
3250             under the terms of either: the GNU General Public License as published
3251             by the Free Software Foundation; or the Artistic License.
3252              
3253             See http://dev.perl.org/licenses/ for more information.
3254              
3255              
3256             =cut
3257              
3258             1; # End of HTTP::Validate