File Coverage

blib/lib/Apache/ASP/Request.pm
Criterion Covered Total %
statement 53 161 32.9
branch 12 84 14.2
condition 2 20 10.0
subroutine 9 16 56.2
pod 0 10 0.0
total 76 291 26.1


line stmt bran cond sub pod time code
1              
2             package Apache::ASP::Request;
3              
4 46     46   296 use Apache::ASP::Collection;
  46         91  
  46         1706  
5 46     46   275 use strict;
  46         102  
  46         156962  
6              
7             sub new {
8 69     69 0 426 my $asp = shift;
9 69         209 my $r = $asp->{r};
10              
11 69   50     4279 my $self = bless
12             {
13             asp => $asp,
14             # content => undef,
15             # Cookies => undef,
16             # FileUpload => undef,
17             # Form => undef,
18             # QueryString => undef,
19             # ServerVariables => undef,
20             Method => $r->method || 'GET',
21             TotalBytes => 0,
22             };
23              
24             # calculate whether to read POST data here
25 69         1316 my $request_binary_read = &config($asp, 'RequestBinaryRead', undef, 1);
26 69         345 $asp->{request_binary_read} = $request_binary_read;
27              
28             # set up the environment, including authentication info
29 69         725 my $env = { %{$r->subprocess_env}, %ENV };
  69         1891  
30 69 50       9630 if(&config($asp, 'AuthServerVariables')) {
31 0 0       0 if(defined $r->get_basic_auth_pw) {
32 0         0 my $c = $r->connection;
33             #X: this needs to be extended to support Digest authentication
34 0         0 $env->{AUTH_TYPE} = $c->auth_type;
35 0         0 $env->{AUTH_USER} = $c->user;
36 0         0 $env->{AUTH_NAME} = $r->auth_name;
37 0         0 $env->{REMOTE_USER} = $c->user;
38 0         0 $env->{AUTH_PASSWD} = $r->get_basic_auth_pw;
39             }
40             }
41 69         750 $self->{'ServerVariables'} = bless $env, 'Apache::ASP::Collection';
42              
43             # assign no matter what so Form is always defined
44 69         177 my $form = {};
45 69         158 my %upload;
46 69         498 my $headers_in = $self->{asp}{headers_in};
47 69 50 33     359 if($self->{Method} eq 'POST' and $request_binary_read) {
48 0 0       0 $self->{TotalBytes} = defined($ENV{CONTENT_LENGTH}) ? $ENV{CONTENT_LENGTH} : $headers_in->get('Content-Length');
49 0 0       0 if($headers_in->get('Content-Type') =~ m|^multipart/form-data|) {
50             # do the logic here so that the normal form POST processing will not
51             # occur either
52 0         0 $asp->{file_upload_process} = &config($asp, 'FileUploadProcess', undef, 1);
53 0 0       0 if($asp->{file_upload_process}) {
54 0 0       0 if($asp->{file_upload_temp} = &config($asp, 'FileUploadTemp')) {
55 0         0 eval "use CGI;";
56             } else {
57             # default leaves no temp files for prying eyes
58 0         0 eval "use CGI qw(-private_tempfiles);";
59             }
60 0 0       0 if($@) {
61 0         0 $self->{asp}->Error("can't use file upload without CGI.pm: $@");
62 0         0 goto ASP_REQUEST_POST_READ_DONE;
63             }
64              
65             # new behavior for file uploads when FileUploadMax is exceeded,
66             # before it used to error abruptly, now it will simply skip the file
67             # upload data
68 0         0 local $CGI::DISABLE_UPLOADS = $CGI::DISABLE_UPLOADS;
69 0 0       0 if($asp->{file_upload_max} = &config($asp, 'FileUploadMax')) {
70 0 0       0 if($self->{TotalBytes} > $asp->{file_upload_max} ) {
71 0         0 $CGI::DISABLE_UPLOADS = 1;
72             }
73             }
74            
75             $asp->{dbg} && $asp->Debug("using CGI.pm version ".
76 0 0 0     0 (eval { CGI->VERSION } || $CGI::VERSION).
77             " for file upload support"
78             );
79              
80 0         0 my %form;
81 0         0 my $q = $self->{cgi} = new CGI;
82 0         0 $asp->Debug($q->param);
83 0         0 for(my @names = $q->param) {
84 0         0 my @params = $q->param($_);
85 0 0       0 $form{$_} = @params > 1 ? [ @params ] : $params[0];
86 0 0       0 if(ref($form{$_}) eq 'Fh') {
87 0         0 my $fh = $form{$_};
88 0 0       0 binmode $fh if $asp->{win32};
89 0         0 $upload{$_} = $q->uploadInfo($fh);
90 0 0       0 if($asp->{file_upload_temp}) {
91 0         0 $upload{$_}{TempFile} = $q->tmpFileName($fh);
92 0         0 $upload{$_}{TempFile} =~ s|^/+|/|;
93             }
94 0         0 $upload{$_}{BrowserFile} = "$fh";
95 0         0 $upload{$_}{FileHandle} = $fh;
96 0         0 $upload{$_}{ContentType} = $upload{$_}{'Content-Type'};
97             # tie the file upload reference to a collection... %upload
98             # may be many file uploads note.
99 0         0 $upload{$_} = bless $upload{$_}, 'Apache::ASP::Collection';
100 0 0       0 $asp->{dbg} && $asp->Debug("file upload field processed for \$Request->{FileUpload}{$_}", $upload{$_});
101             }
102             }
103 0         0 $form = \%form;
104             } else {
105 0         0 $self->{asp}->Debug("FileUploadProcess is disabled, file upload data in \$Request->BinaryRead");
106             }
107              
108             } else {
109             # Only tie to STDIN if we have cached contents
110             # don't untie *STDIN until DESTROY, so filtered handlers
111             # have an opportunity to use any cached contents that may exist
112 0 0       0 if(my $len = $self->{TotalBytes}) {
113 0   0     0 $self->{content} = $self->BinaryRead($len) || '';
114 0         0 tie(*STDIN, 'Apache::ASP::Request', $self);
115             #AJAX POSTs are ``application/x-www-form-urlencoded; charset=UTF-8'' in Firefox3+
116             #by Richard Walsh Nov 25, 2008 (found in nabble)
117 0 0       0 if($headers_in->get('Content-Type') =~ m|^application/x-www-form-urlencoded|) {
118 0         0 $form = &ParseParams($self, \$self->{content});
119             } else {
120 0         0 $form = {};
121             }
122             }
123             }
124             }
125              
126             ASP_REQUEST_POST_READ_DONE:
127              
128 69         310 $self->{'Form'} = bless $form, 'Apache::ASP::Collection';
129 69         425 $self->{'FileUpload'} = bless \%upload, 'Apache::ASP::Collection';
130 69         496 my $query = $r->args();
131 69 100       322 my $parsed_query = $query ? &ParseParams($self, \$query) : {};
132 69         358 $self->{'QueryString'} = bless $parsed_query, 'Apache::ASP::Collection';
133              
134 69 50       270 if(&config($asp, 'RequestParams')) {
135 0         0 $self->{'Params'} = bless { %$parsed_query, %$form }, 'Apache::ASP::Collection';
136             }
137              
138             # do cookies now
139 69         193 my %cookies;
140 69 50       579 if(my $cookie = $headers_in->get('Cookie')) {
141 0   0     0 my @parts = split(/;\s*/, ($cookie || ''));
142 0         0 for(@parts) {
143 0         0 my($name, $value) = split(/\=/, $_, 2);
144 0         0 $name = &Unescape($self, $name);
145            
146 0 0       0 next if ($name eq $Apache::ASP::SessionCookieName);
147 0 0       0 next if $cookies{$name}; # skip dup's
148            
149 0 0       0 $cookies{$name} = ($value =~ /\=/) ?
150             &ParseParams($self, $value) : &Unescape($self, $value);
151             }
152             }
153 69         686 $self->{Cookies} = bless \%cookies, 'Apache::ASP::Collection';
154              
155 69         469 $self;
156             }
157              
158             sub DESTROY {
159 91     91   256 my $self = shift;
160              
161 91 50       515 if($self->{cgi}) {
162             # make sure CGI file handles are freed
163 0         0 $self->{cgi}->DESTROY();
164 0         0 $self->{cgi} = undef;
165             }
166              
167 91         165 for(keys %{$self->{FileUpload}}) {
  91         509  
168 0         0 my $upload = $self->{FileUpload}{$_};
169 0         0 $self->{Form}{$_} = undef;
170 0 0       0 if($upload->{FileHandle}) {
171 0         0 close $upload->{FileHandle};
172             # $self->{asp}->Debug("closing fh $upload->{FileHandle}");
173             }
174 0         0 $self->{FileUpload}{$_} = undef;
175             }
176              
177 91         2537 %$self = ();
178             }
179              
180             # just returns itself
181 0     0   0 sub TIEHANDLE { $_[1] };
182              
183             # just spill the cache into the scalar, so multiple reads are
184             # fine... whoever is reading from the cached contents must
185             # be reading the whole thing just once for this to work,
186             # which is fine for CGI.pm
187             sub READ {
188 0     0   0 my $self = $_[0];
189 0   0     0 $_[1] ||= '';
190 0         0 $_[1] .= $self->{content};
191 0         0 $self->{ServerVariables}{CONTENT_LENGTH};
192             }
193              
194 0     0   0 sub BINMODE { };
195              
196             # COLLECTIONS, normal, Cookies are special, with the dictionary lookup
197             # directly aliased as this should be faster than autoloading
198 24     24 0 249 sub Form { shift->{Form}->Item(@_) }
199 0     0 0 0 sub FileUpload { shift->{FileUpload}->Item(@_) }
200 24     24 0 239 sub QueryString { shift->{QueryString}->Item(@_) }
201 2     2 0 15 sub ServerVariables { shift->{ServerVariables}->Item(@_) }
202              
203             sub Params {
204 1     1 0 6 my $self = shift;
205 1 50       12 $self->{Params}
206             || die("\$Request->Params object does not exist, enable with 'PerlSetVar RequestParams 1'");
207 0         0 $self->{Params}->Item(@_);
208             }
209              
210             sub BinaryRead {
211 0     0 0 0 my($self, $length) = @_;
212 0         0 my $data;
213 0 0       0 return undef unless $self->{TotalBytes};
214              
215 0 0 0     0 if(ref(tied(*STDIN)) && tied(*STDIN)->isa('Apache::ASP::Request')) {
216 0 0       0 if($self->{TotalBytes}) {
217 0 0       0 if(defined $length) {
218 0         0 return substr($self->{content}, 0, $length);
219             } else {
220 0         0 return $self->{content}
221             }
222             } else {
223 0         0 return undef;
224             }
225             } else {
226 0 0       0 defined($length) || ( $length = $self->{TotalBytes} );
227 0         0 my $asp = $self->{asp};
228 0         0 my $r = $asp->{r};
229 0 0       0 if(! $ENV{MOD_PERL}) {
230 0         0 my $rv = sysread(*STDIN, $data, $length, 0);
231 0 0       0 $asp->{dbg} && $asp->Debug("read $rv bytes from STDIN for CGI mode, tried $length bytes");
232             } else {
233 0         0 $r->read($data, $length);
234 0 0       0 $asp->{dbg} && $asp->Debug("read ".length($data)." bytes, tried $length bytes");
235             }
236 0         0 return $data;
237             }
238             }
239              
240             sub Cookies {
241 0     0 0 0 my($self, $name, $key) = @_;
242              
243 0 0       0 if(! $name) {
    0          
244 0         0 $self->{Cookies};
245             } elsif($key) {
246 0         0 $self->{Cookies}{$name}{$key};
247             } else {
248             # when we just have the name, are we expecting a dictionary or not
249 0         0 my $cookie = $self->{Cookies}{$name};
250 0 0 0     0 if(ref $cookie && wantarray) {
251 0         0 return %$cookie;
252             } else {
253             # CollectionItem support here one day, to not return
254             # an undef object, CollectionItem needs tied hash support
255 0         0 return $cookie;
256             }
257             }
258             }
259              
260             sub ParseParams {
261 3     3 0 7 my($self, $string) = @_;
262 3 100       14 ($string = $$string) if ref($string); ## faster if we pass a ref for a big string
263              
264 3         7 my %params;
265 3 50       10 defined($string) || return(\%params);
266 3         16 my @params = split /[\&\;]/, $string, -1;
267              
268             # we have to iterate through the params here to collect multiple values for
269             # the same param, say from a multiple select statement
270 3         8 for my $pair (@params) {
271 6         10 my($key, $value) = map {
272             # inline for greater efficiency
273             # &Unescape($self, $_)
274 3         15 my $todecode = $_;
275 6         88 $todecode =~ tr/+/ /; # pluses become spaces
276 6         11 $todecode =~ s/%([0-9a-fA-F]{2})/chr(hex($1))/ge;
  0         0  
277 6         18 $todecode;
278             } split (/\=/, $pair, 2);
279 3 50       13 if(defined $params{$key}) {
280 0         0 my $collect = $params{$key};
281              
282 0 0       0 if(ref $collect) {
283             # we have already collected more than one param for that key
284 0         0 push(@{$collect}, $value);
  0         0  
285             } else {
286             # this is the second value for a key we've seen, start array
287 0         0 $params{$key} = [$collect, $value];
288             }
289             } else {
290             # normal use, one to one key value pairs, just set
291 3         14 $params{$key} = $value;
292             }
293             }
294              
295 3         12 \%params;
296             }
297              
298             # unescape URL-encoded data
299             sub Unescape {
300 0     0 0   my $todecode = $_[1];
301 0           $todecode =~ tr/+/ /; # pluses become spaces
302 0           $todecode =~ s/%([0-9a-fA-F]{2})/chr(hex($1))/ge;
  0            
303 0           $todecode;
304             }
305              
306             *config = *Apache::ASP::config;
307              
308             1;