line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
|
2
|
|
|
|
|
|
|
package TUWF::Misc; |
3
|
|
|
|
|
|
|
# Yeah, just put all miscellaneous functions in one module! |
4
|
|
|
|
|
|
|
# Geez, talk about being sloppy... |
5
|
|
|
|
|
|
|
|
6
|
1
|
|
|
1
|
|
73642
|
use strict; |
|
1
|
|
|
|
|
12
|
|
|
1
|
|
|
|
|
34
|
|
7
|
1
|
|
|
1
|
|
5
|
use warnings; |
|
1
|
|
|
|
|
1
|
|
|
1
|
|
|
|
|
32
|
|
8
|
1
|
|
|
1
|
|
5
|
use Carp 'croak'; |
|
1
|
|
|
|
|
1
|
|
|
1
|
|
|
|
|
52
|
|
9
|
1
|
|
|
1
|
|
5
|
use Exporter 'import'; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
47
|
|
10
|
1
|
|
|
1
|
|
12
|
use Scalar::Util 'looks_like_number'; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
54
|
|
11
|
1
|
|
|
1
|
|
437
|
use TUWF::Validate; |
|
1
|
|
|
|
|
3
|
|
|
1
|
|
|
|
|
2150
|
|
12
|
|
|
|
|
|
|
|
13
|
|
|
|
|
|
|
|
14
|
|
|
|
|
|
|
our $VERSION = '1.5'; |
15
|
|
|
|
|
|
|
our @EXPORT_OK = ('uri_escape', 'kv_validate'); |
16
|
|
|
|
|
|
|
|
17
|
|
|
|
|
|
|
|
18
|
|
|
|
|
|
|
sub uri_escape { |
19
|
0
|
|
|
0
|
1
|
0
|
utf8::encode(local $_ = shift); |
20
|
0
|
|
|
|
|
0
|
s/([^A-Za-z0-9._~-])/sprintf '%%%02X', ord $1/eg; |
|
0
|
|
|
|
|
0
|
|
21
|
0
|
|
|
|
|
0
|
return $_; |
22
|
|
|
|
|
|
|
} |
23
|
|
|
|
|
|
|
|
24
|
|
|
|
|
|
|
|
25
|
|
|
|
|
|
|
|
26
|
|
|
|
|
|
|
|
27
|
|
|
|
|
|
|
sub _template_validate_num { |
28
|
13
|
|
|
13
|
|
32
|
$_[0] *= 1; # Normalize to perl number |
29
|
13
|
100
|
100
|
|
|
62
|
return 0 if defined($_[1]{min}) && $_[0] < $_[1]{min}; |
30
|
9
|
100
|
100
|
|
|
48
|
return 0 if defined($_[1]{max}) && $_[0] > $_[1]{max}; |
31
|
6
|
|
|
|
|
18
|
return 1; |
32
|
|
|
|
|
|
|
} |
33
|
|
|
|
|
|
|
|
34
|
|
|
|
|
|
|
|
35
|
|
|
|
|
|
|
my %default_templates = ( |
36
|
|
|
|
|
|
|
# JSON number format, regex from http://stackoverflow.com/questions/13340717/json-numbers-regular-expression |
37
|
|
|
|
|
|
|
num => { func => \&_template_validate_num, regex => qr/^-?(?:0|[1-9]\d*)(?:\.\d+)?(?:[eE][+-]?\d+)?$/, inherit => ['min','max'] }, |
38
|
|
|
|
|
|
|
int => { func => \&_template_validate_num, regex => qr/^-?(?:0|[1-9]\d*)$/, inherit => ['min','max'] }, |
39
|
|
|
|
|
|
|
uint => { func => \&_template_validate_num, regex => qr/^(?:0|[1-9]\d*)$/, inherit => ['min','max'] }, |
40
|
|
|
|
|
|
|
ascii => { regex => qr/^[\x20-\x7E]*$/ }, |
41
|
|
|
|
|
|
|
email => { regex => $TUWF::Validate::re_email, maxlength => 254 }, |
42
|
|
|
|
|
|
|
weburl => { regex => $TUWF::Validate::re_weburl, maxlength => 65536 }, # the maxlength is a bit arbitrary, but better than unlimited |
43
|
|
|
|
|
|
|
); |
44
|
|
|
|
|
|
|
|
45
|
|
|
|
|
|
|
|
46
|
|
|
|
|
|
|
sub kv_validate { |
47
|
92
|
|
|
92
|
1
|
68459
|
my($sources, $templates, $params) = @_; |
48
|
92
|
|
|
|
|
614
|
$templates = { %default_templates, %$templates }; |
49
|
|
|
|
|
|
|
|
50
|
92
|
|
|
|
|
208
|
my @err; |
51
|
|
|
|
|
|
|
my %ret; |
52
|
|
|
|
|
|
|
|
53
|
92
|
|
|
|
|
165
|
for my $f (@$params) { |
54
|
|
|
|
|
|
|
# Inherit some options from templates. |
55
|
|
|
|
|
|
|
!exists($f->{$_}) && _val_from_tpl($f, $_, $templates, $f) |
56
|
92
|
|
100
|
|
|
352
|
for(qw|required default rmwhitespace multi mincount maxcount|); |
57
|
|
|
|
|
|
|
|
58
|
92
|
|
|
|
|
303
|
my $src = (grep $f->{$_}, keys %$sources)[0]; |
59
|
92
|
|
|
|
|
249
|
my @values = $sources->{$src}->($f->{$src}); |
60
|
92
|
100
|
|
|
|
1025
|
@values = ($values[0]) if !$f->{multi}; |
61
|
|
|
|
|
|
|
|
62
|
|
|
|
|
|
|
# check each value and add it to %ret |
63
|
92
|
|
|
|
|
153
|
for (@values) { |
64
|
97
|
|
100
|
|
|
154
|
my $errfield = _validate_early($_, $f) || _validate($_, $templates, $f); |
65
|
97
|
100
|
100
|
|
|
335
|
next if !$errfield || $errfield eq 'default'; |
66
|
47
|
|
|
|
|
131
|
push @err, [ $f->{$src}, $errfield, $f->{$errfield} ]; |
67
|
47
|
|
|
|
|
83
|
last; |
68
|
|
|
|
|
|
|
} |
69
|
92
|
100
|
|
|
|
260
|
$ret{$f->{$src}} = $f->{multi} ? \@values : $values[0]; |
70
|
|
|
|
|
|
|
|
71
|
|
|
|
|
|
|
# check mincount/maxcount |
72
|
92
|
100
|
100
|
|
|
197
|
push @err, [ $f->{$src}, 'mincount', $f->{mincount} ] if $f->{mincount} && @values < $f->{mincount}; |
73
|
92
|
100
|
100
|
|
|
234
|
push @err, [ $f->{$src}, 'maxcount', $f->{maxcount} ] if $f->{maxcount} && @values > $f->{maxcount}; |
74
|
|
|
|
|
|
|
} |
75
|
|
|
|
|
|
|
|
76
|
92
|
100
|
|
|
|
195
|
$ret{_err} = \@err if @err; |
77
|
92
|
|
|
|
|
398
|
return \%ret; |
78
|
|
|
|
|
|
|
} |
79
|
|
|
|
|
|
|
|
80
|
|
|
|
|
|
|
|
81
|
|
|
|
|
|
|
sub _val_from_tpl { |
82
|
896
|
|
|
896
|
|
1283
|
my($top_rules, $field, $tpls, $rules) = @_; |
83
|
896
|
100
|
|
|
|
2019
|
return if !$rules->{template}; |
84
|
379
|
|
|
|
|
539
|
my $tpl = $tpls->{$rules->{template}}; |
85
|
379
|
100
|
|
|
|
571
|
if(exists $tpl->{$field}) { |
86
|
17
|
|
|
|
|
62
|
$top_rules->{$field} = $tpl->{$field}; |
87
|
|
|
|
|
|
|
} else { |
88
|
362
|
|
|
|
|
563
|
_val_from_tpl($top_rules, $field, $tpls, $tpl); |
89
|
|
|
|
|
|
|
} |
90
|
|
|
|
|
|
|
} |
91
|
|
|
|
|
|
|
|
92
|
|
|
|
|
|
|
|
93
|
|
|
|
|
|
|
# Initial validation of a value. Same as _validate() below, but this one |
94
|
|
|
|
|
|
|
# validates options that need to be checked only once. (The checks in |
95
|
|
|
|
|
|
|
# _validate() may run several times when templates are used). |
96
|
|
|
|
|
|
|
sub _validate_early { # value, \%rules |
97
|
97
|
|
|
97
|
|
160
|
my($v, $r) = @_; |
98
|
|
|
|
|
|
|
|
99
|
97
|
100
|
|
|
|
226
|
$r->{required}++ if not exists $r->{required}; |
100
|
97
|
100
|
|
|
|
199
|
$r->{rmwhitespace}++ if not exists $r->{rmwhitespace}; |
101
|
|
|
|
|
|
|
|
102
|
|
|
|
|
|
|
# remove whitespace |
103
|
97
|
100
|
100
|
|
|
340
|
if($v && $r->{rmwhitespace}) { |
104
|
75
|
|
|
|
|
170
|
$_[0] =~ s/\r//g; |
105
|
75
|
|
|
|
|
234
|
$_[0] =~ s/^[\s\n]+//; |
106
|
75
|
|
|
|
|
165
|
$_[0] =~ s/[\s\n]+$//; |
107
|
75
|
|
|
|
|
104
|
$v = $_[0] |
108
|
|
|
|
|
|
|
} |
109
|
|
|
|
|
|
|
|
110
|
|
|
|
|
|
|
# empty |
111
|
97
|
100
|
100
|
|
|
374
|
if(!defined($v) || length($v) < 1) { |
112
|
9
|
100
|
|
|
|
32
|
return 'required' if $r->{required}; |
113
|
3
|
100
|
|
|
|
8
|
$_[0] = $r->{default} if exists $r->{default}; |
114
|
3
|
|
|
|
|
9
|
return 'default'; |
115
|
|
|
|
|
|
|
} |
116
|
88
|
|
|
|
|
235
|
return undef; |
117
|
|
|
|
|
|
|
} |
118
|
|
|
|
|
|
|
|
119
|
|
|
|
|
|
|
|
120
|
|
|
|
|
|
|
# Internal function used by kv_validate, checks one value on the validation |
121
|
|
|
|
|
|
|
# rules, the name of the failed rule on error, undef otherwise |
122
|
|
|
|
|
|
|
sub _validate { # value, \%templates, \%rules |
123
|
155
|
|
|
155
|
|
244
|
my($v, $t, $r) = @_; |
124
|
|
|
|
|
|
|
|
125
|
155
|
50
|
66
|
|
|
443
|
croak "Template $r->{template} not defined." if $r->{template} && !$t->{$r->{template}}; |
126
|
|
|
|
|
|
|
|
127
|
|
|
|
|
|
|
# length |
128
|
155
|
100
|
100
|
|
|
311
|
return 'minlength' if $r->{minlength} && length $v < $r->{minlength}; |
129
|
153
|
100
|
100
|
|
|
338
|
return 'maxlength' if $r->{maxlength} && length $v > $r->{maxlength}; |
130
|
|
|
|
|
|
|
# enum |
131
|
150
|
100
|
100
|
|
|
297
|
return 'enum' if $r->{enum} && !grep $_ eq $v, @{$r->{enum}}; |
|
3
|
|
|
|
|
26
|
|
132
|
|
|
|
|
|
|
# regex |
133
|
149
|
100
|
100
|
|
|
855
|
return 'regex' if $r->{regex} && (ref($r->{regex}) eq 'ARRAY' ? ($v !~ m/$r->{regex}[0]/) : ($v !~ m/$r->{regex}/)); |
|
|
100
|
|
|
|
|
|
134
|
|
|
|
|
|
|
# template |
135
|
124
|
100
|
|
|
|
233
|
if($r->{template}) { |
136
|
67
|
|
|
|
|
121
|
my $in = $t->{$r->{template}}{inherit}; |
137
|
67
|
100
|
|
|
|
152
|
my %r = (($in ? (map exists($r->{$_}) ? ($_,$r->{$_}) : (), @$in) : ()), %{$t->{$r->{template}}}); |
|
67
|
100
|
|
|
|
217
|
|
138
|
67
|
100
|
|
|
|
153
|
return 'template' if _validate($_[0], $t, \%r); |
139
|
|
|
|
|
|
|
} |
140
|
|
|
|
|
|
|
# function |
141
|
92
|
100
|
100
|
|
|
251
|
return 'func' if $r->{func} && (ref($r->{func}) eq 'ARRAY' ? !$r->{func}[0]->($_[0], $r) : !$r->{func}->($_[0], $r)); |
|
|
100
|
|
|
|
|
|
142
|
|
|
|
|
|
|
# passed validation |
143
|
82
|
|
|
|
|
228
|
return undef; |
144
|
|
|
|
|
|
|
} |
145
|
|
|
|
|
|
|
|
146
|
|
|
|
|
|
|
|
147
|
|
|
|
|
|
|
|
148
|
|
|
|
|
|
|
|
149
|
|
|
|
|
|
|
sub TUWF::Object::formValidate { |
150
|
0
|
|
|
0
|
|
|
my($self, @fields) = @_; |
151
|
|
|
|
|
|
|
return kv_validate( |
152
|
0
|
|
|
0
|
|
|
{ post => sub { $self->reqPosts(shift) }, |
153
|
0
|
|
|
0
|
|
|
get => sub { $self->reqGets(shift) }, |
154
|
0
|
|
|
0
|
|
|
param => sub { $self->reqParams(shift) }, |
155
|
0
|
|
|
0
|
|
|
cookie => sub { $self->reqCookie(shift) }, |
156
|
|
|
|
|
|
|
}, $self->{_TUWF}{validate_templates} || {}, |
157
|
|
|
|
|
|
|
\@fields |
158
|
0
|
|
0
|
|
|
|
); |
159
|
|
|
|
|
|
|
} |
160
|
|
|
|
|
|
|
|
161
|
|
|
|
|
|
|
|
162
|
|
|
|
|
|
|
|
163
|
|
|
|
|
|
|
# A simple mail function, body and headers as arguments. Usage: |
164
|
|
|
|
|
|
|
# $self->mail('body', header1 => 'value of header 1', ..); |
165
|
|
|
|
|
|
|
sub TUWF::Object::mail { |
166
|
0
|
|
|
0
|
|
|
my $self = shift; |
167
|
0
|
|
|
|
|
|
my $body = shift; |
168
|
0
|
|
|
|
|
|
my %hs = @_; |
169
|
|
|
|
|
|
|
|
170
|
0
|
0
|
|
|
|
|
croak "No To: specified!\n" if !$hs{To}; |
171
|
0
|
0
|
|
|
|
|
croak "No Subject: specified!\n" if !$hs{Subject}; |
172
|
0
|
|
0
|
|
|
|
$hs{'Content-Type'} ||= 'text/plain; charset=\'UTF-8\''; |
173
|
0
|
|
0
|
|
|
|
$hs{From} ||= $self->{_TUWF}{mail_from}; |
174
|
0
|
|
|
|
|
|
$body =~ s/\r?\n/\n/g; |
175
|
|
|
|
|
|
|
|
176
|
0
|
|
|
|
|
|
my $mail = ''; |
177
|
0
|
|
|
|
|
|
foreach (keys %hs) { |
178
|
0
|
|
|
|
|
|
$hs{$_} =~ s/[\r\n]//g; |
179
|
0
|
|
|
|
|
|
$mail .= sprintf "%s: %s\n", $_, $hs{$_}; |
180
|
|
|
|
|
|
|
} |
181
|
0
|
|
|
|
|
|
$mail .= sprintf "\n%s", $body; |
182
|
|
|
|
|
|
|
|
183
|
0
|
0
|
|
|
|
|
if($self->{_TUWF}{mail_sendmail} eq 'log') { |
|
|
0
|
|
|
|
|
|
184
|
0
|
|
|
|
|
|
$self->log("tuwf->mail(): The following mail would have been sent:\n$mail"); |
185
|
|
|
|
|
|
|
} elsif(open(my $mailer, '|-:utf8', "$self->{_TUWF}{mail_sendmail} -t -f '$hs{From}'")) { |
186
|
0
|
|
|
|
|
|
print $mailer $mail; |
187
|
0
|
0
|
|
|
|
|
croak "Error running sendmail ($!)" |
188
|
|
|
|
|
|
|
if !close($mailer); |
189
|
|
|
|
|
|
|
} else { |
190
|
0
|
|
|
|
|
|
croak "Error opening sendail ($!)"; |
191
|
|
|
|
|
|
|
} |
192
|
|
|
|
|
|
|
} |
193
|
|
|
|
|
|
|
|
194
|
|
|
|
|
|
|
|
195
|
|
|
|
|
|
|
sub TUWF::Object::compile { |
196
|
0
|
|
|
0
|
|
|
TUWF::Validate::compile($_[0]{_TUWF}{custom_validations}, $_[1]); |
197
|
|
|
|
|
|
|
} |
198
|
|
|
|
|
|
|
|
199
|
|
|
|
|
|
|
|
200
|
|
|
|
|
|
|
sub _compile { |
201
|
0
|
0
|
|
0
|
|
|
ref $_[0] eq 'TUWF::Validate' ? $_[0] : $TUWF::OBJ->compile($_[0]); |
202
|
|
|
|
|
|
|
} |
203
|
|
|
|
|
|
|
|
204
|
|
|
|
|
|
|
|
205
|
|
|
|
|
|
|
sub TUWF::Object::validate { |
206
|
0
|
|
|
0
|
|
|
my $self = shift; |
207
|
0
|
|
|
|
|
|
my $what = shift; |
208
|
|
|
|
|
|
|
|
209
|
0
|
0
|
|
|
|
|
return _compile($_[0])->validate($self->reqJSON) if $what eq 'json'; |
210
|
|
|
|
|
|
|
|
211
|
|
|
|
|
|
|
# 'param' is special, and not really encouraged. Create a new hash based on |
212
|
|
|
|
|
|
|
# reqParam() and cache the result. |
213
|
|
|
|
|
|
|
$self->{_TUWF}{Req}{PARAM} ||= { |
214
|
0
|
0
|
0
|
|
|
|
map { my @v = $self->reqParams($_); +($_, @v > 1 ? \@v : $v[0]) } $self->reqParams() |
|
0
|
0
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
215
|
|
|
|
|
|
|
} if $what eq 'param'; |
216
|
|
|
|
|
|
|
|
217
|
|
|
|
|
|
|
my $source = |
218
|
|
|
|
|
|
|
$what eq 'get' ? $self->{_TUWF}{Req}{GET} : |
219
|
|
|
|
|
|
|
$what eq 'post' ? $self->{_TUWF}{Req}{POST} : |
220
|
|
|
|
|
|
|
$what eq 'param' ? $self->{_TUWF}{Req}{PARAM} |
221
|
0
|
0
|
|
|
|
|
: croak "Invalid source type '$what'"; |
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
222
|
|
|
|
|
|
|
|
223
|
|
|
|
|
|
|
# Multi-value, schema hash or object |
224
|
0
|
0
|
|
|
|
|
return _compile($_[0])->validate($source) if @_ == 1; |
225
|
|
|
|
|
|
|
|
226
|
|
|
|
|
|
|
# Single value |
227
|
0
|
0
|
|
|
|
|
return _compile($_[1])->validate($source->{$_[0]}) if @_ == 2; |
228
|
|
|
|
|
|
|
|
229
|
|
|
|
|
|
|
# Multi-value, separate params |
230
|
0
|
|
|
|
|
|
_compile({ type => 'hash', keys => { @_ } })->validate($source); |
231
|
|
|
|
|
|
|
} |
232
|
|
|
|
|
|
|
|
233
|
|
|
|
|
|
|
|
234
|
|
|
|
|
|
|
# Internal function used by other TUWF modules to find an appropriate JSON |
235
|
|
|
|
|
|
|
# module. Kinda like JSON::MaybeXS, but without an extra dependency. |
236
|
|
|
|
|
|
|
sub _JSON { |
237
|
0
|
0
|
|
0
|
|
|
return 'JSON::XS' if $INC{'JSON/XS.pm'}; |
238
|
0
|
0
|
|
|
|
|
return 'Cpanel::JSON::XS' if $INC{'Cpanel/JSON/XS.pm'}; |
239
|
0
|
0
|
|
|
|
|
return 'JSON::PP' if $INC{'JSON/PP.pm'}; |
240
|
0
|
0
|
|
|
|
|
return 'JSON::XS' if eval { require JSON::XS; 1 }; |
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
241
|
0
|
0
|
|
|
|
|
return 'Cpanel::JSON::XS' if eval { require Cpanel::JSON::XS; 1 }; |
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
242
|
0
|
0
|
|
|
|
|
die "Unable to load a suitable JSON module: $@" if !eval { require JSON::PP; 1 }; |
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
243
|
0
|
|
|
|
|
|
'JSON::PP' |
244
|
|
|
|
|
|
|
} |
245
|
|
|
|
|
|
|
|
246
|
|
|
|
|
|
|
1; |