File Coverage

blib/lib/XML/AppleConfigProfile/Payload/Types/Validation.pm
Criterion Covered Total %
statement 33 109 30.2
branch 0 62 0.0
condition 0 9 0.0
subroutine 11 23 47.8
pod 9 9 100.0
total 53 212 25.0


line stmt bran cond sub pod time code
1             package XML::AppleConfigProfile::Payload::Types::Validation;
2              
3 1     1   12 use 5.14.4;
  1         4  
  1         47  
4 1     1   6 use strict;
  1         1  
  1         37  
5 1     1   5 use warnings FATAL => 'all';
  1         2  
  1         54  
6              
7             our $VERSION = '0.00_001';
8              
9 1     1   2435 use DateTime;
  1         187032  
  1         46  
10 1     1   1230 use DateTime::Format::Flexible;
  1         108734  
  1         15  
11 1     1   100 use Encode qw(encode);
  1         2  
  1         156  
12             use Exporter::Easy (
13 1         14 OK => [qw(
14             validate
15             validate_string validate_number validate_real validate_date
16             validate_boolean validate_data validate_identifier validate_uuid
17             )],
18             TAGS => [
19             'all' => [qw(
20             validate
21             validate_string validate_number validate_real validate_date
22             validate_boolean validate_data validate_identifier validate_uuid
23             )],
24             'types' => [qw(
25             validate_string validate_number validate_real validate_date
26             validate_boolean validate_data validate_identifier validate_uuid
27             )],
28             ],
29 1     1   7 );
  1         2  
30 1     1   2178 use Regexp::Common;
  1         2913  
  1         5  
31 1     1   78114 use Scalar::Util qw(openhandle);
  1         4  
  1         76  
32 1     1   7 use Try::Tiny;
  1         2  
  1         62  
33 1     1   7 use XML::AppleConfigProfile::Payload::Types qw(:all);
  1         3  
  1         1349  
34              
35              
36             =head1 NAME
37              
38             XML::AppleConfigProfile::Payload::Types::Validation - Validation of common
39             payload types.
40              
41             =head1 DESCRIPTION
42              
43             Apple Configuration Profiles contain one or more I. Each payload
44             contains a dictionary, which can be thought of like a Perl hash. Within a
45             payload's dictionary, each key's value is restricted to a specific type.
46             One key might require a number; a different key might require a string, or some
47             binary data.
48              
49             Provided in this module are a number of Readonly scalars that will be used
50             (instead of strings) to identify the data types for configuration profile keys.
51             The scalars are all OK for import into your local namespace, or you can simply
52             import C<:all> to get all of them at once.
53              
54              
55             =head1 METHODS
56              
57             =head2 validate
58              
59             my $validated_value = validate($type, $value);
60            
61             Validates C<$value> as a valid C<$type>. If valid, returns the de-tainted
62             C<$value>. If invalid, returns C.
63              
64             C<$type> is one of the values from L.
65             C<$value> is the value to be validated.
66              
67             IF C<$type> is not valid, or C<$value> is C, then C is returned.
68              
69             This is a convenience method for when you can specify the type of data as a
70             parameter.
71              
72             =cut
73              
74             sub validate {
75 0     0 1   my ($type, $value) = @_;
76            
77             # We recognize String types
78 0 0 0       if ($type == $ProfileString) {
    0          
    0          
    0          
    0          
    0          
    0          
    0          
79 0           return validate_string($value);
80             }
81            
82             # We recognize Number types
83             elsif ($type == $ProfileNumber) {
84 0           return validate_number($value);
85             }
86            
87             # We recognize Real (floating-point number) types
88             elsif ($type == $ProfileReal) {
89 0           return validate_real($value);
90             }
91            
92             # We recognize Boolean types
93             elsif ($type == $ProfileBool) {
94 0           return validate_boolean($value);
95             }
96            
97             # We recognize Data types
98             elsif ( ($type == $ProfileData)
99             || ($type == $ProfileNSDataBlob)
100             ) {
101 0           return validate_data($value);
102             }
103            
104             # We recognize Date types
105             elsif ($type == $ProfileDate) {
106 0           return validate_date ($value);
107             }
108            
109             # We recognize Identifier types
110             elsif ($type == $ProfileIdentifier) {
111 0           return validate_identifier($value);
112             }
113            
114             # We recognize UUID types
115             elsif ($type == $ProfileUUID) {
116 0           return validate_uuid($value);
117             } # Done checking the UUID type
118             }
119              
120              
121             =head2 validate_string
122              
123             my $valid_string = validate_string($value);
124              
125             Returns a de-tained C<$value> if it is a defined, non-empty scalar, and can be
126             UTF-8 encoded.
127              
128             =cut
129              
130             sub validate_string {
131 0     0 1   my ($value) = @_;
132            
133             # References aren't allowed here
134             ## no critic (ProhibitExplicitReturnUndef)
135 0 0         return undef if ref($value);
136             ##use critic
137            
138             # Empty strings aren't allowed, either.
139 0 0         if ($value =~ m/^(.+)$/s) {
140 0           $value = $1;
141            
142             }
143             else {
144             ## no critic (ProhibitExplicitReturnUndef)
145 0           return undef;
146             ##use critic
147             }
148            
149             # Try to encode as UTF-8, to make sure it's safe
150             try {
151 0     0     encode('UTF-8', $value, Encode::FB_CROAK | Encode::LEAVE_SRC);
152             }
153             catch {
154 0     0     $value = undef;
155 0           };
156            
157             # If we're here, then we are valid!
158 0           return $value;
159             }
160              
161              
162             =head2 validate_number
163              
164             my $number = validate_number($value)
165              
166             Returns a de-tained C<$value> if it is an integer. A leading C<+> is OK.
167             Any other input returns C.
168              
169             =cut
170              
171             sub validate_number {
172 0     0 1   my ($value) = @_;
173            
174             # References aren't allowed here
175             ## no critic (ProhibitExplicitReturnUndef)
176 0 0         return undef if ref($value);
177             ##use critic
178            
179             # Numbers must be integers, positive or negative (or zero).
180 0 0         if ($value =~ /^$RE{num}{int}{-keep}$/) {
181 0           return $1;
182             }
183            
184             # If we're here, the matching failed, so return undef
185             ## no critic (ProhibitExplicitReturnUndef)
186 0           return undef;
187             ##use critic
188             }
189              
190              
191             =head2 validate_real
192              
193             my $number = validate_real($value)
194              
195             Returns a de-tainted C<$value> if it is an integer or a floating-point number.
196             A loading C<+> is OK. An exponent, positive or negative, is also OK.
197             Any other input returns C.
198              
199             =cut
200              
201             sub validate_real {
202 0     0 1   my ($value) = @_;
203            
204             # References aren't allowed here
205             ## no critic (ProhibitExplicitReturnUndef)
206 0 0         return undef if ref($value);
207             ##use critic
208            
209             # Numbers must be floating-point, positive or negative (or zero).
210 0 0         if ($value =~ /^$RE{num}{real}{-keep}$/i) {
211 0           return $1;
212             }
213            
214             # If we're here, the matching failed, so return undef
215             ## no critic (ProhibitExplicitReturnUndef)
216 0           return undef;
217             ##use critic
218             }
219              
220              
221             =head2 validate_boolean
222              
223             my $boolean = validate_boolean($value);
224              
225             If C<$value> can be evaluated as true or false, returns a C<1> or a C<0>. Will
226             return C if a reference is passed.
227              
228             =cut
229              
230             sub validate_boolean {
231 0     0 1   my ($value) = @_;
232            
233             # References aren't allowed here
234             ## no critic (ProhibitExplicitReturnUndef)
235 0 0         return undef if ref($value);
236             ##use critic
237            
238             # A simple evaluation!
239 0 0         if ($value) {
240 0           return 1;
241             }
242 0 0         if (!$value) {
243 0           return 0;
244             }
245             }
246              
247              
248             =head2 validate_date
249              
250             my $date_object = validate_date($value);
251              
252             If C<$value> is already a finite C object, it is returned immediately.
253             If C<$value> is a string, and can be parsed by L,
254             the resulting C object will be returned.
255              
256             Unparseable strings, infinite C objects, and any other input will
257             return C.
258              
259             =cut
260              
261             sub validate_date {
262 0     0 1   my ($value) = @_;
263            
264             # If we have a blessed ref, which is a DateTime object, and it represents
265             # a finite time, then we're good!
266 0 0         if (ref $value) {
267             ## no critic (ProhibitExplicitReturnUndef)
268 0 0         return undef unless blessed($value);
269 0 0         return undef unless $value->isa('DateTime');
270 0 0         return undef unless $value->is_finite;
271             ##use critic
272            
273 0           return $value
274             }
275            
276             # At this point, we have a scalar, so let's see if it can be parsed
277             try {
278 0     0     $value = DateTime::Format::Flexible->parse_datetime($value);
279 0           return $value;
280             }
281            
282             # I give up!
283             ## no critic (ProhibitExplicitReturnUndef)
284 0           return undef;
285             ##use critic
286             }
287              
288              
289             =head2 validate_data
290              
291             # With a file handle
292             my $handle = validate_data($handle);
293              
294             # With binary data
295             my $handle = validate_data($bytes);
296              
297             If passed an already-open file handle, or any object that represents a file
298             (such as an C object), will return what was passed.
299              
300             If passed a scalar, it will be checked to make sure it is not empty, and that
301             is not a utf8 string (which it would be if it was a string). The contents of
302             the string will be placed into an in-memory file, and an IO::File object will
303             be returned.
304              
305             =cut
306              
307             sub validate_data {
308 0     0 1   my ($value) = @_;
309            
310             # How we act here depends if we have a string or a filehandle
311             # Let's first check if we were given an open filehandle
312 0 0         if (openhandle($value)) {
313             # We've got an open file, so we're good!
314 0           return $value;
315             }
316            
317             # If we don't have an open handle, then make sure it's not an object
318 0 0         unless (ref($value)) {
319             # We have a string. Let's make sure it's binary, and non-empty.
320 0 0 0       if ( (!utf8::is_utf8($value))
321             && (length($value) > 0)
322             ) {
323             # Pull the string into an in-memory file, and return that.
324 0           my ($memory, $file);
325 0           $file = IO::File::new(\$memory, 'w+');
326 0           binmode($file);
327 0           $file->print($value);
328 0           $file->seek(0, 0);
329 0           return $file;
330             }
331             }
332            
333             # If we're here, then we are dealing with something unknown to us.
334             ## no critic (ProhibitExplicitReturnUndef)
335 0 0         return undef if ref($value);
336             ##use critic
337             }
338              
339              
340             =head2 validate_identifier
341              
342             my $valid_identifier = validate_identifier($value);
343              
344             Returns a de-tained C<$value> if it is a single-line string that matches the
345             format of a domain name (without spaces). Otherwise, returns C.
346              
347             =cut
348              
349             sub validate_identifier {
350 0     0 1   my ($value) = @_;
351            
352             # References aren't allowed here
353             ## no critic (ProhibitExplicitReturnUndef)
354 0 0         return undef if ref($value);
355             ##use critic
356            
357             # Empty strings aren't allowed, either.
358 0 0         if ($value =~ m/^(.+)$/s) {
359 0           my $matched_string = $1;
360             # Identifiers are one-line strings
361 0 0 0       if ( ($matched_string !~ m/\n/s)
362             && ($matched_string =~ m/^$RE{net}{domain}{-nospace}$/)
363             ) {
364 0           return $matched_string;
365             }
366             }
367            
368             # If we're here, the matching failed, so return undef
369             ## no critic (ProhibitExplicitReturnUndef)
370 0           return undef;
371             ##use critic
372             }
373              
374             =head2 validate_uuid
375              
376             my $guid = validate_uuid($value);
377              
378             Returns a Data::GUID object if C<$value> can be parsed as a UUID.
379              
380             =cut
381              
382             sub validate_uuid {
383 0     0 1   my ($value) = @_;
384            
385 0           my $class = ref($value);
386 0           my $uuid;
387            
388             # We accept Data::UUID objects
389 0 0         if ($class eq 'Data::UUID') {
390 0           $uuid = Data::GUID::from_data_uuid($value);
391 0           return $uuid;
392             }
393            
394             # We accept Data::GUID objects
395 0 0         if ($class eq 'Data::GUID') {
396 0           return $value;
397             }
398            
399             # We don't accept other kinds of objects
400 0 0         if ($class ne '') {
401             ## no critic (ProhibitExplicitReturnUndef)
402 0           return undef;
403             ## use critic
404             }
405            
406             # Have Data::GUID try to parse the input
407             # If from_any_string doesn't die, then it wored OK
408 0           eval {
409 0           $uuid = Data::GUID->from_any_string($value);
410             };
411              
412             # If parsing went OK, then we have our object! Otherwise, we have undef.
413 0           return $uuid;
414             }
415              
416              
417             =head1 ACKNOWLEDGEMENTS
418              
419             Refer to L for acknowledgements.
420              
421             =head1 AUTHOR
422              
423             A. Karl Kornel, C<< >>
424              
425             =head1 COPYRIGHT AND LICENSE
426              
427             Copyright © 2014 A. Karl Kornel.
428              
429             This program is free software; you can redistribute it and/or modify it
430             under the terms of either: the GNU General Public License as published
431             by the Free Software Foundation; or the Artistic License.
432              
433             See L for more information.
434              
435             =cut
436              
437             1;