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
|
|
59097
|
use strict; |
|
1
|
|
|
|
|
1
|
|
|
1
|
|
|
|
|
26
|
|
7
|
1
|
|
|
1
|
|
4
|
use warnings; |
|
1
|
|
|
|
|
1
|
|
|
1
|
|
|
|
|
23
|
|
8
|
1
|
|
|
1
|
|
4
|
use Carp 'croak'; |
|
1
|
|
|
|
|
1
|
|
|
1
|
|
|
|
|
36
|
|
9
|
1
|
|
|
1
|
|
4
|
use Exporter 'import'; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
30
|
|
10
|
1
|
|
|
1
|
|
494
|
use Encode 'encode_utf8'; |
|
1
|
|
|
|
|
8457
|
|
|
1
|
|
|
|
|
57
|
|
11
|
1
|
|
|
1
|
|
7
|
use Scalar::Util 'looks_like_number'; |
|
1
|
|
|
|
|
1
|
|
|
1
|
|
|
|
|
1580
|
|
12
|
|
|
|
|
|
|
|
13
|
|
|
|
|
|
|
|
14
|
|
|
|
|
|
|
our $VERSION = '1.2'; |
15
|
|
|
|
|
|
|
our @EXPORT = ('formValidate', 'mail'); |
16
|
|
|
|
|
|
|
our @EXPORT_OK = ('uri_escape', 'kv_validate'); |
17
|
|
|
|
|
|
|
|
18
|
|
|
|
|
|
|
|
19
|
|
|
|
|
|
|
sub uri_escape { |
20
|
0
|
|
|
0
|
1
|
0
|
local $_ = encode_utf8 shift; |
21
|
0
|
|
|
|
|
0
|
s/([^A-Za-z0-9._~-])/sprintf '%%%02X', ord $1/eg; |
|
0
|
|
|
|
|
0
|
|
22
|
0
|
|
|
|
|
0
|
return $_; |
23
|
|
|
|
|
|
|
} |
24
|
|
|
|
|
|
|
|
25
|
|
|
|
|
|
|
|
26
|
|
|
|
|
|
|
|
27
|
|
|
|
|
|
|
|
28
|
|
|
|
|
|
|
sub _template_validate_num { |
29
|
13
|
|
|
13
|
|
27
|
$_[0] *= 1; # Normalize to perl number |
30
|
13
|
100
|
100
|
|
|
49
|
return 0 if defined($_[1]{min}) && $_[0] < $_[1]{min}; |
31
|
9
|
100
|
100
|
|
|
38
|
return 0 if defined($_[1]{max}) && $_[0] > $_[1]{max}; |
32
|
6
|
|
|
|
|
13
|
return 1; |
33
|
|
|
|
|
|
|
} |
34
|
|
|
|
|
|
|
|
35
|
|
|
|
|
|
|
my $re_fqdn = qr/(?:[a-zA-Z0-9][\w-]*\.)+[a-zA-Z][a-zA-Z0-9-]{1,25}\.?/; |
36
|
|
|
|
|
|
|
my $re_ip4_digit = qr/(?:0|[1-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])/; |
37
|
|
|
|
|
|
|
my $re_ip4 = qr/($re_ip4_digit\.){3}$re_ip4_digit/; |
38
|
|
|
|
|
|
|
# This monstrosity is based on http://stackoverflow.com/questions/53497/regular-expression-that-matches-valid-ipv6-addresses |
39
|
|
|
|
|
|
|
# Doesn't allow IPv4-mapped-IPv6 addresses or other fancy stuff. |
40
|
|
|
|
|
|
|
my $re_ip6 = qr/(?:[0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|(?:[0-9a-fA-F]{1,4}:){1,7}:|(?:[0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|(?:[0-9a-fA-F]{1,4}:){1,5}(?::[0-9a-fA-F]{1,4}){1,2}|(?:[0-9a-fA-F]{1,4}:){1,4}(?::[0-9a-fA-F]{1,4}){1,3}|(?:[0-9a-fA-F]{1,4}:){1,3}(?::[0-9a-fA-F]{1,4}){1,4}|(?:[0-9a-fA-F]{1,4}:){1,2}(?::[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:(?:(?::[0-9a-fA-F]{1,4}){1,6})|:(?:(?::[0-9a-fA-F]{1,4}){1,7}|:)/; |
41
|
|
|
|
|
|
|
my $re_domain = qr/(?:$re_fqdn|$re_ip4|\[$re_ip6\])/; |
42
|
|
|
|
|
|
|
|
43
|
|
|
|
|
|
|
my %default_templates = ( |
44
|
|
|
|
|
|
|
# JSON number format, regex from http://stackoverflow.com/questions/13340717/json-numbers-regular-expression |
45
|
|
|
|
|
|
|
num => { func => \&_template_validate_num, regex => qr/^-?(?:0|[1-9]\d*)(?:\.\d+)?(?:[eE][+-]?\d+)?$/, inherit => ['min','max'] }, |
46
|
|
|
|
|
|
|
int => { func => \&_template_validate_num, regex => qr/^-?(?:0|[1-9]\d*)$/, inherit => ['min','max'] }, |
47
|
|
|
|
|
|
|
uint => { func => \&_template_validate_num, regex => qr/^(?:0|[1-9]\d*)$/, inherit => ['min','max'] }, |
48
|
|
|
|
|
|
|
ascii => { regex => qr/^[\x20-\x7E]*$/ }, |
49
|
|
|
|
|
|
|
email => { regex => qr/^[-\+\.#\$=\w]+\@$re_domain$/, maxlength => 254 }, |
50
|
|
|
|
|
|
|
weburl => { regex => qr/^https?:\/\/$re_domain(?::[1-9][0-9]{0,5})?\/[^\s<>"]*$/, maxlength => 65536 }, # the maxlength is a bit arbitrary, but better than unlimited |
51
|
|
|
|
|
|
|
); |
52
|
|
|
|
|
|
|
|
53
|
|
|
|
|
|
|
|
54
|
|
|
|
|
|
|
sub kv_validate { |
55
|
92
|
|
|
92
|
1
|
54336
|
my($sources, $templates, $params) = @_; |
56
|
92
|
|
|
|
|
540
|
$templates = { %default_templates, %$templates }; |
57
|
|
|
|
|
|
|
|
58
|
92
|
|
|
|
|
168
|
my @err; |
59
|
|
|
|
|
|
|
my %ret; |
60
|
|
|
|
|
|
|
|
61
|
92
|
|
|
|
|
154
|
for my $f (@$params) { |
62
|
|
|
|
|
|
|
# Inherit some options from templates. |
63
|
|
|
|
|
|
|
!exists($f->{$_}) && _val_from_tpl($f, $_, $templates, $f) |
64
|
92
|
|
100
|
|
|
278
|
for(qw|required default rmwhitespace multi mincount maxcount|); |
65
|
|
|
|
|
|
|
|
66
|
92
|
|
|
|
|
283
|
my $src = (grep $f->{$_}, keys %$sources)[0]; |
67
|
92
|
|
|
|
|
190
|
my @values = $sources->{$src}->($f->{$src}); |
68
|
92
|
100
|
|
|
|
869
|
@values = ($values[0]) if !$f->{multi}; |
69
|
|
|
|
|
|
|
|
70
|
|
|
|
|
|
|
# check each value and add it to %ret |
71
|
92
|
|
|
|
|
127
|
for (@values) { |
72
|
97
|
|
100
|
|
|
128
|
my $errfield = _validate_early($_, $f) || _validate($_, $templates, $f); |
73
|
97
|
100
|
100
|
|
|
278
|
next if !$errfield || $errfield eq 'default'; |
74
|
45
|
|
|
|
|
102
|
push @err, [ $f->{$src}, $errfield, $f->{$errfield} ]; |
75
|
45
|
|
|
|
|
63
|
last; |
76
|
|
|
|
|
|
|
} |
77
|
92
|
100
|
|
|
|
200
|
$ret{$f->{$src}} = $f->{multi} ? \@values : $values[0]; |
78
|
|
|
|
|
|
|
|
79
|
|
|
|
|
|
|
# check mincount/maxcount |
80
|
92
|
100
|
100
|
|
|
148
|
push @err, [ $f->{$src}, 'mincount', $f->{mincount} ] if $f->{mincount} && @values < $f->{mincount}; |
81
|
92
|
100
|
100
|
|
|
198
|
push @err, [ $f->{$src}, 'maxcount', $f->{maxcount} ] if $f->{maxcount} && @values > $f->{maxcount}; |
82
|
|
|
|
|
|
|
} |
83
|
|
|
|
|
|
|
|
84
|
92
|
100
|
|
|
|
148
|
$ret{_err} = \@err if @err; |
85
|
92
|
|
|
|
|
329
|
return \%ret; |
86
|
|
|
|
|
|
|
} |
87
|
|
|
|
|
|
|
|
88
|
|
|
|
|
|
|
|
89
|
|
|
|
|
|
|
sub _val_from_tpl { |
90
|
896
|
|
|
896
|
|
1076
|
my($top_rules, $field, $tpls, $rules) = @_; |
91
|
896
|
100
|
|
|
|
1575
|
return if !$rules->{template}; |
92
|
379
|
|
|
|
|
435
|
my $tpl = $tpls->{$rules->{template}}; |
93
|
379
|
100
|
|
|
|
472
|
if(exists $tpl->{$field}) { |
94
|
17
|
|
|
|
|
36
|
$top_rules->{$field} = $tpl->{$field}; |
95
|
|
|
|
|
|
|
} else { |
96
|
362
|
|
|
|
|
414
|
_val_from_tpl($top_rules, $field, $tpls, $tpl); |
97
|
|
|
|
|
|
|
} |
98
|
|
|
|
|
|
|
} |
99
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
101
|
|
|
|
|
|
|
# Initial validation of a value. Same as _validate() below, but this one |
102
|
|
|
|
|
|
|
# validates options that need to be checked only once. (The checks in |
103
|
|
|
|
|
|
|
# _validate() may run several times when templates are used). |
104
|
|
|
|
|
|
|
sub _validate_early { # value, \%rules |
105
|
97
|
|
|
97
|
|
134
|
my($v, $r) = @_; |
106
|
|
|
|
|
|
|
|
107
|
97
|
100
|
|
|
|
181
|
$r->{required}++ if not exists $r->{required}; |
108
|
97
|
100
|
|
|
|
163
|
$r->{rmwhitespace}++ if not exists $r->{rmwhitespace}; |
109
|
|
|
|
|
|
|
|
110
|
|
|
|
|
|
|
# remove whitespace |
111
|
97
|
100
|
100
|
|
|
261
|
if($v && $r->{rmwhitespace}) { |
112
|
75
|
|
|
|
|
137
|
$_[0] =~ s/\r//g; |
113
|
75
|
|
|
|
|
182
|
$_[0] =~ s/^[\s\n]+//; |
114
|
75
|
|
|
|
|
124
|
$_[0] =~ s/[\s\n]+$//; |
115
|
75
|
|
|
|
|
95
|
$v = $_[0] |
116
|
|
|
|
|
|
|
} |
117
|
|
|
|
|
|
|
|
118
|
|
|
|
|
|
|
# empty |
119
|
97
|
100
|
100
|
|
|
278
|
if(!defined($v) || length($v) < 1) { |
120
|
9
|
100
|
|
|
|
26
|
return 'required' if $r->{required}; |
121
|
3
|
100
|
|
|
|
8
|
$_[0] = $r->{default} if exists $r->{default}; |
122
|
3
|
|
|
|
|
7
|
return 'default'; |
123
|
|
|
|
|
|
|
} |
124
|
88
|
|
|
|
|
200
|
return undef; |
125
|
|
|
|
|
|
|
} |
126
|
|
|
|
|
|
|
|
127
|
|
|
|
|
|
|
|
128
|
|
|
|
|
|
|
# Internal function used by kv_validate, checks one value on the validation |
129
|
|
|
|
|
|
|
# rules, the name of the failed rule on error, undef otherwise |
130
|
|
|
|
|
|
|
sub _validate { # value, \%templates, \%rules |
131
|
155
|
|
|
155
|
|
195
|
my($v, $t, $r) = @_; |
132
|
|
|
|
|
|
|
|
133
|
155
|
50
|
66
|
|
|
332
|
croak "Template $r->{template} not defined." if $r->{template} && !$t->{$r->{template}}; |
134
|
|
|
|
|
|
|
|
135
|
|
|
|
|
|
|
# length |
136
|
155
|
100
|
100
|
|
|
235
|
return 'minlength' if $r->{minlength} && length $v < $r->{minlength}; |
137
|
153
|
100
|
100
|
|
|
282
|
return 'maxlength' if $r->{maxlength} && length $v > $r->{maxlength}; |
138
|
|
|
|
|
|
|
# enum |
139
|
150
|
100
|
100
|
|
|
207
|
return 'enum' if $r->{enum} && !grep $_ eq $v, @{$r->{enum}}; |
|
3
|
|
|
|
|
23
|
|
140
|
|
|
|
|
|
|
# regex |
141
|
149
|
100
|
100
|
|
|
611
|
return 'regex' if $r->{regex} && (ref($r->{regex}) eq 'ARRAY' ? ($v !~ m/$r->{regex}[0]/) : ($v !~ m/$r->{regex}/)); |
|
|
100
|
|
|
|
|
|
142
|
|
|
|
|
|
|
# template |
143
|
126
|
100
|
|
|
|
177
|
if($r->{template}) { |
144
|
67
|
|
|
|
|
94
|
my $in = $t->{$r->{template}}{inherit}; |
145
|
67
|
100
|
|
|
|
124
|
my %r = (($in ? (map exists($r->{$_}) ? ($_,$r->{$_}) : (), @$in) : ()), %{$t->{$r->{template}}}); |
|
67
|
100
|
|
|
|
170
|
|
146
|
67
|
100
|
|
|
|
136
|
return 'template' if _validate($_[0], $t, \%r); |
147
|
|
|
|
|
|
|
} |
148
|
|
|
|
|
|
|
# function |
149
|
96
|
100
|
100
|
|
|
205
|
return 'func' if $r->{func} && (ref($r->{func}) eq 'ARRAY' ? !$r->{func}[0]->($_[0], $r) : !$r->{func}->($_[0], $r)); |
|
|
100
|
|
|
|
|
|
150
|
|
|
|
|
|
|
# passed validation |
151
|
86
|
|
|
|
|
211
|
return undef; |
152
|
|
|
|
|
|
|
} |
153
|
|
|
|
|
|
|
|
154
|
|
|
|
|
|
|
|
155
|
|
|
|
|
|
|
|
156
|
|
|
|
|
|
|
|
157
|
|
|
|
|
|
|
sub formValidate { |
158
|
0
|
|
|
0
|
1
|
|
my($self, @fields) = @_; |
159
|
|
|
|
|
|
|
return kv_validate( |
160
|
0
|
|
|
0
|
|
|
{ post => sub { $self->reqPosts(shift) }, |
161
|
0
|
|
|
0
|
|
|
get => sub { $self->reqGets(shift) }, |
162
|
0
|
|
|
0
|
|
|
param => sub { $self->reqParams(shift) }, |
163
|
0
|
|
|
0
|
|
|
cookie => sub { $self->reqCookie(shift) }, |
164
|
|
|
|
|
|
|
}, $self->{_TUWF}{validate_templates} || {}, |
165
|
|
|
|
|
|
|
\@fields |
166
|
0
|
|
0
|
|
|
|
); |
167
|
|
|
|
|
|
|
} |
168
|
|
|
|
|
|
|
|
169
|
|
|
|
|
|
|
|
170
|
|
|
|
|
|
|
|
171
|
|
|
|
|
|
|
# A simple mail function, body and headers as arguments. Usage: |
172
|
|
|
|
|
|
|
# $self->mail('body', header1 => 'value of header 1', ..); |
173
|
|
|
|
|
|
|
sub mail { |
174
|
0
|
|
|
0
|
1
|
|
my $self = shift; |
175
|
0
|
|
|
|
|
|
my $body = shift; |
176
|
0
|
|
|
|
|
|
my %hs = @_; |
177
|
|
|
|
|
|
|
|
178
|
0
|
0
|
|
|
|
|
croak "No To: specified!\n" if !$hs{To}; |
179
|
0
|
0
|
|
|
|
|
croak "No Subject: specified!\n" if !$hs{Subject}; |
180
|
0
|
|
0
|
|
|
|
$hs{'Content-Type'} ||= 'text/plain; charset=\'UTF-8\''; |
181
|
0
|
|
0
|
|
|
|
$hs{From} ||= $self->{_TUWF}{mail_from}; |
182
|
0
|
|
|
|
|
|
$body =~ s/\r?\n/\n/g; |
183
|
|
|
|
|
|
|
|
184
|
0
|
|
|
|
|
|
my $mail = ''; |
185
|
0
|
|
|
|
|
|
foreach (keys %hs) { |
186
|
0
|
|
|
|
|
|
$hs{$_} =~ s/[\r\n]//g; |
187
|
0
|
|
|
|
|
|
$mail .= sprintf "%s: %s\n", $_, $hs{$_}; |
188
|
|
|
|
|
|
|
} |
189
|
0
|
|
|
|
|
|
$mail .= sprintf "\n%s", $body; |
190
|
|
|
|
|
|
|
|
191
|
0
|
0
|
|
|
|
|
if(open(my $mailer, '|-:utf8', "$self->{_TUWF}{mail_sendmail} -t -f '$hs{From}'")) { |
192
|
0
|
|
|
|
|
|
print $mailer $mail; |
193
|
0
|
0
|
|
|
|
|
croak "Error running sendmail ($!)" |
194
|
|
|
|
|
|
|
if !close($mailer); |
195
|
|
|
|
|
|
|
} else { |
196
|
0
|
|
|
|
|
|
croak "Error opening sendail ($!)"; |
197
|
|
|
|
|
|
|
} |
198
|
|
|
|
|
|
|
} |
199
|
|
|
|
|
|
|
|
200
|
|
|
|
|
|
|
|
201
|
|
|
|
|
|
|
1; |