line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package Test::CGI::Multipart; |
2
|
|
|
|
|
|
|
|
3
|
11
|
|
|
11
|
|
423444
|
use warnings; |
|
11
|
|
|
|
|
29
|
|
|
11
|
|
|
|
|
388
|
|
4
|
11
|
|
|
11
|
|
60
|
use strict; |
|
11
|
|
|
|
|
24
|
|
|
11
|
|
|
|
|
391
|
|
5
|
11
|
|
|
11
|
|
56
|
use Carp; |
|
11
|
|
|
|
|
23
|
|
|
11
|
|
|
|
|
1127
|
|
6
|
11
|
|
|
11
|
|
10403
|
use UNIVERSAL::require; |
|
11
|
|
|
|
|
19679
|
|
|
11
|
|
|
|
|
114
|
|
7
|
11
|
|
|
11
|
|
11949
|
use Params::Validate qw(:all); |
|
11
|
|
|
|
|
127349
|
|
|
11
|
|
|
|
|
2748
|
|
8
|
11
|
|
|
11
|
|
13716
|
use MIME::Entity; |
|
11
|
|
|
|
|
1659530
|
|
|
11
|
|
|
|
|
200
|
|
9
|
11
|
|
|
11
|
|
11806
|
use Readonly; |
|
11
|
|
|
|
|
37135
|
|
|
11
|
|
|
|
|
751
|
|
10
|
|
|
|
|
|
|
require 5.006_001; # we use 3-arg open in places |
11
|
|
|
|
|
|
|
|
12
|
11
|
|
|
11
|
|
10336
|
use version; our $VERSION = qv('0.0.3'); |
|
11
|
|
|
|
|
25626
|
|
|
11
|
|
|
|
|
92
|
|
13
|
|
|
|
|
|
|
|
14
|
|
|
|
|
|
|
# Module implementation here |
15
|
|
|
|
|
|
|
|
16
|
|
|
|
|
|
|
# Make callbacks a package variable as then loading callbacks |
17
|
|
|
|
|
|
|
# will be prettier. |
18
|
|
|
|
|
|
|
my @callbacks; |
19
|
|
|
|
|
|
|
|
20
|
|
|
|
|
|
|
# Parameter specs |
21
|
|
|
|
|
|
|
# Note the purpose of these spcs is to protect our data structures. |
22
|
|
|
|
|
|
|
# It should not protect the code that will be tested |
23
|
|
|
|
|
|
|
# as that must look after itself. |
24
|
|
|
|
|
|
|
Readonly my $NAME_SPEC => {type=>SCALAR}; |
25
|
|
|
|
|
|
|
Readonly my $VALUE_SPEC => {type=>SCALAR|ARRAYREF}; |
26
|
|
|
|
|
|
|
Readonly my $UA_SPEC => {type=>SCALAR, default=> 'Test::CGI::Multipart'}; |
27
|
|
|
|
|
|
|
Readonly my $CGI_SPEC => { |
28
|
|
|
|
|
|
|
type=>SCALAR, |
29
|
|
|
|
|
|
|
default=>'CGI', |
30
|
|
|
|
|
|
|
regex=> qr{ |
31
|
|
|
|
|
|
|
\A # start of string |
32
|
|
|
|
|
|
|
(?: |
33
|
|
|
|
|
|
|
\w |
34
|
|
|
|
|
|
|
|(?:\:\:) # Module name separator |
35
|
|
|
|
|
|
|
)+ |
36
|
|
|
|
|
|
|
\z # end of string |
37
|
|
|
|
|
|
|
}xms |
38
|
|
|
|
|
|
|
}; |
39
|
|
|
|
|
|
|
Readonly my $TYPE_SPEC => { |
40
|
|
|
|
|
|
|
type=>SCALAR, |
41
|
|
|
|
|
|
|
optional=>1, |
42
|
|
|
|
|
|
|
regex=> qr{ |
43
|
|
|
|
|
|
|
\A # start of string |
44
|
|
|
|
|
|
|
[\w\-]+ # major type |
45
|
|
|
|
|
|
|
\/ # MIME type separator |
46
|
|
|
|
|
|
|
[\w\-]+ # sub-type |
47
|
|
|
|
|
|
|
\z # end of string |
48
|
|
|
|
|
|
|
}xms |
49
|
|
|
|
|
|
|
}; |
50
|
|
|
|
|
|
|
Readonly my $FILE_SPEC => { |
51
|
|
|
|
|
|
|
type=>SCALAR, |
52
|
|
|
|
|
|
|
}; |
53
|
|
|
|
|
|
|
Readonly my $MIME_SPEC => { |
54
|
|
|
|
|
|
|
type=>OBJECT, |
55
|
|
|
|
|
|
|
isa=>'MIME::Entity', |
56
|
|
|
|
|
|
|
}; |
57
|
|
|
|
|
|
|
Readonly my $CODE_SPEC => { |
58
|
|
|
|
|
|
|
type=>CODEREF, |
59
|
|
|
|
|
|
|
}; |
60
|
|
|
|
|
|
|
|
61
|
|
|
|
|
|
|
# MIME parsing states |
62
|
|
|
|
|
|
|
Readonly my $TYPE_STATE => 0; |
63
|
|
|
|
|
|
|
Readonly my $HEADER_STATE => 1; |
64
|
|
|
|
|
|
|
Readonly my $DATA_STATE=> 2; |
65
|
|
|
|
|
|
|
Readonly my $EOL => "\015\012"; |
66
|
|
|
|
|
|
|
|
67
|
|
|
|
|
|
|
sub new { |
68
|
10
|
|
|
10
|
1
|
15006
|
my $class = shift; |
69
|
10
|
|
|
|
|
59
|
my $self = { |
70
|
|
|
|
|
|
|
file_index=>0, |
71
|
|
|
|
|
|
|
params=>{}, |
72
|
|
|
|
|
|
|
}; |
73
|
10
|
|
|
|
|
32
|
bless $self, $class; |
74
|
10
|
|
|
|
|
32
|
return $self; |
75
|
|
|
|
|
|
|
} |
76
|
|
|
|
|
|
|
|
77
|
|
|
|
|
|
|
sub set_param { |
78
|
14
|
|
|
14
|
1
|
9317
|
my $self = shift; |
79
|
14
|
|
|
|
|
96
|
my %params = validate(@_, {name=>$NAME_SPEC, value=>$VALUE_SPEC}); |
80
|
7
|
|
|
|
|
35
|
my @values = ref $params{value} eq 'ARRAY' |
81
|
14
|
100
|
|
|
|
5745
|
? @{$params{value}} |
82
|
|
|
|
|
|
|
: $params{value} |
83
|
|
|
|
|
|
|
; |
84
|
14
|
|
|
|
|
103
|
$self->{params}->{$params{name}} = \@values; |
85
|
14
|
|
|
|
|
133
|
return; |
86
|
|
|
|
|
|
|
} |
87
|
|
|
|
|
|
|
|
88
|
|
|
|
|
|
|
sub upload_file { |
89
|
17
|
|
|
17
|
1
|
23895
|
my $self = shift; |
90
|
17
|
|
|
|
|
94
|
my %params = @_; |
91
|
17
|
|
|
|
|
31
|
my $params = \%params; |
92
|
|
|
|
|
|
|
|
93
|
17
|
|
|
|
|
58
|
foreach my $code (@callbacks) { |
94
|
17
|
|
|
|
|
164
|
$params = &$code($params); |
95
|
|
|
|
|
|
|
} |
96
|
|
|
|
|
|
|
|
97
|
15
|
|
|
|
|
112
|
$self->_upload_file(%$params); |
98
|
|
|
|
|
|
|
|
99
|
12
|
|
|
|
|
3445
|
return; |
100
|
|
|
|
|
|
|
} |
101
|
|
|
|
|
|
|
|
102
|
|
|
|
|
|
|
|
103
|
|
|
|
|
|
|
sub _upload_file { |
104
|
15
|
|
|
15
|
|
24
|
my $self = shift; |
105
|
15
|
|
|
|
|
133
|
my %params = validate(@_, { |
106
|
|
|
|
|
|
|
name=>$NAME_SPEC, |
107
|
|
|
|
|
|
|
value=>$VALUE_SPEC, |
108
|
|
|
|
|
|
|
file=>$FILE_SPEC, |
109
|
|
|
|
|
|
|
type=>$TYPE_SPEC |
110
|
|
|
|
|
|
|
}); |
111
|
13
|
|
|
|
|
758
|
my $name = $params{name}; |
112
|
|
|
|
|
|
|
|
113
|
13
|
100
|
|
|
|
67
|
if (!exists $self->{params}->{$name}) { |
114
|
10
|
|
|
|
|
44
|
$self->{params}->{$name} = {}; |
115
|
|
|
|
|
|
|
} |
116
|
13
|
100
|
|
|
|
65
|
if (ref $self->{params}->{$name} ne 'HASH') { |
117
|
1
|
|
|
|
|
32
|
croak "mismatch: is $name a file upload or not"; |
118
|
|
|
|
|
|
|
} |
119
|
|
|
|
|
|
|
|
120
|
12
|
|
|
|
|
26
|
my $file_index = $self->{file_index}; |
121
|
|
|
|
|
|
|
|
122
|
12
|
|
|
|
|
48
|
$self->{params}->{$name}->{$file_index} = \%params; |
123
|
|
|
|
|
|
|
|
124
|
12
|
|
|
|
|
34
|
$self->{file_index}++; |
125
|
|
|
|
|
|
|
|
126
|
12
|
|
|
|
|
36
|
return; |
127
|
|
|
|
|
|
|
} |
128
|
|
|
|
|
|
|
|
129
|
|
|
|
|
|
|
sub get_param { |
130
|
65
|
|
|
65
|
1
|
92131
|
my $self = shift; |
131
|
65
|
|
|
|
|
458
|
my %params = validate(@_, {name=>$NAME_SPEC}); |
132
|
65
|
|
|
|
|
2051
|
my $name = $params{name}; |
133
|
65
|
100
|
|
|
|
406
|
if (ref $self->{params}->{$name} eq 'HASH') { |
134
|
23
|
|
|
|
|
46
|
return values %{$self->{params}->{$name}}; |
|
23
|
|
|
|
|
158
|
|
135
|
|
|
|
|
|
|
} |
136
|
42
|
|
|
|
|
65
|
return @{$self->{params}->{$name}}; |
|
42
|
|
|
|
|
282
|
|
137
|
|
|
|
|
|
|
} |
138
|
|
|
|
|
|
|
|
139
|
|
|
|
|
|
|
sub get_names { |
140
|
46
|
|
|
46
|
1
|
9983
|
my $self = shift; |
141
|
46
|
|
|
|
|
81
|
return keys %{$self->{params}}; |
|
46
|
|
|
|
|
323
|
|
142
|
|
|
|
|
|
|
} |
143
|
|
|
|
|
|
|
|
144
|
|
|
|
|
|
|
sub create_cgi { |
145
|
11
|
|
|
11
|
|
32951
|
use autodie qw(open); |
|
11
|
|
|
|
|
238896
|
|
|
11
|
|
|
|
|
77
|
|
146
|
20
|
|
|
20
|
1
|
21805
|
my $self = shift; |
147
|
20
|
|
|
|
|
152
|
my %params = validate(@_, {cgi=>$CGI_SPEC, ua=>$UA_SPEC}); |
148
|
|
|
|
|
|
|
|
149
|
20
|
|
|
|
|
1293
|
my $mime = $self->_mime_data; |
150
|
18
|
|
|
|
|
135
|
my $mime_str = $mime->stringify; |
151
|
18
|
|
|
|
|
127498
|
my $mime_string = $self->_normalize1($mime_str); |
152
|
18
|
|
|
|
|
81
|
my $boundary = $mime->head->multipart_boundary; |
153
|
|
|
|
|
|
|
|
154
|
18
|
|
|
|
|
2416
|
$ENV{REQUEST_METHOD}='POST'; |
155
|
18
|
|
|
|
|
104
|
$ENV{CONTENT_TYPE}="multipart/form-data; boundary=$boundary"; |
156
|
18
|
|
|
|
|
97
|
$ENV{CONTENT_LENGTH}=length($mime_string); |
157
|
18
|
|
|
|
|
164
|
$ENV{HTTP_USER_AGENT}=$params{ua}; |
158
|
|
|
|
|
|
|
|
159
|
|
|
|
|
|
|
# Would like to localize these but this causes problems with CGI::Simple. |
160
|
18
|
|
|
|
|
85
|
local *STDIN; |
161
|
18
|
|
|
|
|
125
|
open(STDIN, '<', \$mime_string); |
162
|
18
|
|
|
|
|
25945
|
binmode STDIN; |
163
|
|
|
|
|
|
|
|
164
|
18
|
|
|
|
|
223
|
$params{cgi}->require; |
165
|
|
|
|
|
|
|
|
166
|
18
|
50
|
|
|
|
132817
|
if ($params{cgi} eq 'CGI::Simple') { |
167
|
0
|
|
|
|
|
0
|
$CGI::Simple::DISABLE_UPLOADS = 0; |
168
|
|
|
|
|
|
|
} |
169
|
18
|
50
|
|
|
|
105
|
if ($params{cgi} eq 'CGI') { |
170
|
18
|
|
|
|
|
81
|
CGI::initialize_globals(); |
171
|
|
|
|
|
|
|
} |
172
|
18
|
50
|
|
|
|
1735
|
if ($params{cgi} eq 'CGI::Minimal') { |
173
|
0
|
|
|
|
|
0
|
CGI::Minimal::reset_globals(); |
174
|
|
|
|
|
|
|
} |
175
|
|
|
|
|
|
|
|
176
|
18
|
|
|
|
|
113
|
my $cgi = $params{cgi}->new; |
177
|
18
|
|
|
|
|
263997
|
return $cgi; |
178
|
|
|
|
|
|
|
} |
179
|
|
|
|
|
|
|
|
180
|
|
|
|
|
|
|
sub _normalize1 { |
181
|
18
|
|
|
18
|
|
45
|
my $self = shift; |
182
|
18
|
|
|
|
|
48
|
my $mime_string = shift; |
183
|
18
|
|
|
|
|
462
|
$mime_string =~ s{([\w-]+:\s+[^\n]+)\n\n}{$1$EOL$EOL}xmsg; |
184
|
18
|
|
|
|
|
2000
|
$mime_string =~ s{\n([\w-]+:\s+)}{$EOL$1}xmsg; |
185
|
18
|
|
|
|
|
2265
|
$mime_string =~ s{\n(-------)}{$EOL$1}xmsg; |
186
|
18
|
|
|
|
|
708
|
return $mime_string; |
187
|
|
|
|
|
|
|
} |
188
|
|
|
|
|
|
|
|
189
|
|
|
|
|
|
|
sub _mime_data { |
190
|
20
|
|
|
20
|
|
49
|
my $self = shift; |
191
|
|
|
|
|
|
|
|
192
|
20
|
|
|
|
|
96
|
my $mime = $self->_create_multipart; |
193
|
20
|
|
|
|
|
32371
|
foreach my $name ($self->get_names) { |
194
|
50
|
|
|
|
|
174
|
my $value = $self->{params}->{$name}; |
195
|
50
|
100
|
|
|
|
205
|
if (ref($value) eq "ARRAY") { |
|
|
100
|
|
|
|
|
|
196
|
28
|
|
|
|
|
66
|
foreach my $v (@$value) { |
197
|
70
|
|
|
|
|
299
|
$self->_attach_field( |
198
|
|
|
|
|
|
|
mime=>$mime, |
199
|
|
|
|
|
|
|
name=>$name, |
200
|
|
|
|
|
|
|
value=>$v, |
201
|
|
|
|
|
|
|
); |
202
|
|
|
|
|
|
|
} |
203
|
|
|
|
|
|
|
} |
204
|
|
|
|
|
|
|
elsif(ref($value) eq "HASH") { |
205
|
20
|
|
|
|
|
85
|
$self->_encode_upload(mime=>$mime,values=>$value); |
206
|
|
|
|
|
|
|
} |
207
|
|
|
|
|
|
|
else { |
208
|
2
|
|
|
|
|
51
|
croak "unexpected data structure"; |
209
|
|
|
|
|
|
|
} |
210
|
|
|
|
|
|
|
} |
211
|
|
|
|
|
|
|
|
212
|
|
|
|
|
|
|
# Required so at least we don't have an empty MIME structure. |
213
|
|
|
|
|
|
|
# And lynx at least does send it. |
214
|
|
|
|
|
|
|
# CGI.pm seems to strip it out where as the others seem to pass it on. |
215
|
|
|
|
|
|
|
$self->_attach_field( |
216
|
18
|
|
|
|
|
126
|
mime=>$mime, |
217
|
|
|
|
|
|
|
name=>'.submit', |
218
|
|
|
|
|
|
|
value=>'Submit', |
219
|
|
|
|
|
|
|
); |
220
|
|
|
|
|
|
|
|
221
|
18
|
|
|
|
|
58
|
return $mime; |
222
|
|
|
|
|
|
|
} |
223
|
|
|
|
|
|
|
|
224
|
|
|
|
|
|
|
sub _attach_field { |
225
|
88
|
|
|
88
|
|
136
|
my $self = shift; |
226
|
88
|
|
|
|
|
461
|
my %params = validate(@_, { |
227
|
|
|
|
|
|
|
mime => $MIME_SPEC, |
228
|
|
|
|
|
|
|
name=>$NAME_SPEC, |
229
|
|
|
|
|
|
|
value=>$VALUE_SPEC, |
230
|
|
|
|
|
|
|
} |
231
|
|
|
|
|
|
|
); |
232
|
88
|
|
|
|
|
5138
|
$params{mime}->attach( |
233
|
|
|
|
|
|
|
'Content-Disposition'=>"form-data; name=\"$params{name}\"", |
234
|
|
|
|
|
|
|
Data=>$params{value}, |
235
|
|
|
|
|
|
|
); |
236
|
88
|
|
|
|
|
125528
|
return; |
237
|
|
|
|
|
|
|
} |
238
|
|
|
|
|
|
|
|
239
|
|
|
|
|
|
|
sub _create_multipart { |
240
|
20
|
|
|
20
|
|
45
|
my $self = shift; |
241
|
20
|
|
|
|
|
187
|
my %params = validate(@_, {}); |
242
|
20
|
|
|
|
|
274
|
return MIME::Entity->build( |
243
|
|
|
|
|
|
|
'Type'=>"multipart/form-data", |
244
|
|
|
|
|
|
|
); |
245
|
|
|
|
|
|
|
} |
246
|
|
|
|
|
|
|
|
247
|
|
|
|
|
|
|
sub _encode_upload { |
248
|
20
|
|
|
20
|
|
33
|
my $self = shift; |
249
|
20
|
|
|
|
|
356
|
my %params = validate(@_, { |
250
|
|
|
|
|
|
|
mime => $MIME_SPEC, |
251
|
|
|
|
|
|
|
values => {type=>HASHREF} |
252
|
|
|
|
|
|
|
}); |
253
|
20
|
|
|
|
|
902
|
my %values = %{$params{values}}; |
|
20
|
|
|
|
|
103
|
|
254
|
20
|
|
|
|
|
59
|
foreach my $k (keys %values) { |
255
|
24
|
|
|
|
|
183
|
$self->_attach_file( |
256
|
|
|
|
|
|
|
mime=>$params{mime}, |
257
|
24
|
|
|
|
|
55
|
%{$values{$k}} |
258
|
|
|
|
|
|
|
); |
259
|
|
|
|
|
|
|
} |
260
|
20
|
|
|
|
|
95
|
return; |
261
|
|
|
|
|
|
|
} |
262
|
|
|
|
|
|
|
|
263
|
|
|
|
|
|
|
sub _attach_file { |
264
|
24
|
|
|
24
|
|
41
|
my $self = shift; |
265
|
24
|
|
|
|
|
110
|
my %params = validate(@_, { |
266
|
|
|
|
|
|
|
mime => $MIME_SPEC, |
267
|
|
|
|
|
|
|
file=>$FILE_SPEC, |
268
|
|
|
|
|
|
|
type=>$TYPE_SPEC, |
269
|
|
|
|
|
|
|
name=>$NAME_SPEC, |
270
|
|
|
|
|
|
|
value=>$VALUE_SPEC, |
271
|
|
|
|
|
|
|
} |
272
|
|
|
|
|
|
|
); |
273
|
24
|
|
|
|
|
1879
|
my %attach = ( |
274
|
|
|
|
|
|
|
'Content-Disposition'=> |
275
|
|
|
|
|
|
|
"form-data; name=\"$params{name}\"; filename=\"$params{file}\"", |
276
|
|
|
|
|
|
|
Data=>$params{value}, |
277
|
|
|
|
|
|
|
Encoding=>'binary', |
278
|
|
|
|
|
|
|
); |
279
|
24
|
100
|
|
|
|
99
|
if ($params{type}) { |
280
|
15
|
|
|
|
|
38
|
$attach{Type} = $params{type}; |
281
|
|
|
|
|
|
|
} |
282
|
|
|
|
|
|
|
$params{mime}->attach( |
283
|
24
|
|
|
|
|
119
|
%attach |
284
|
|
|
|
|
|
|
); |
285
|
24
|
|
|
|
|
27916
|
return; |
286
|
|
|
|
|
|
|
} |
287
|
|
|
|
|
|
|
|
288
|
|
|
|
|
|
|
sub register_callback { |
289
|
4
|
|
|
4
|
1
|
10069
|
my $self = shift; |
290
|
4
|
|
|
|
|
31
|
my %params = validate(@_, { |
291
|
|
|
|
|
|
|
callback => $CODE_SPEC, |
292
|
|
|
|
|
|
|
} |
293
|
|
|
|
|
|
|
); |
294
|
4
|
|
|
|
|
190
|
push @callbacks, $params{callback}; |
295
|
4
|
|
|
|
|
16
|
return; |
296
|
|
|
|
|
|
|
} |
297
|
|
|
|
|
|
|
|
298
|
|
|
|
|
|
|
|
299
|
|
|
|
|
|
|
1; # Magic true value required at end of module |
300
|
|
|
|
|
|
|
__END__ |