| 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__ |