home *** CD-ROM | disk | FTP | other *** search
-
- package Apache::ASP::Request;
-
- use Apache::ASP::Collection;
- use strict;
-
- sub new {
- my $asp = shift;
- my $r = $asp->{r};
-
- my $self = bless
- {
- asp => $asp,
- # content => undef,
- # Cookies => undef,
- # FileUpload => undef,
- # Form => undef,
- # QueryString => undef,
- # ServerVariables => undef,
- Method => $r->method || 'GET',
- TotalBytes => 0,
- };
-
- # calculate whether to read POST data here
- my $request_binary_read = &config($asp, 'RequestBinaryRead', undef, 1);
- $asp->{request_binary_read} = $request_binary_read;
-
- # set up the environment, including authentication info
- my $env = { %{$r->subprocess_env}, %ENV };
- if(&config($asp, 'AuthServerVariables')) {
- if(defined $r->get_basic_auth_pw) {
- my $c = $r->connection;
- #X: this needs to be extended to support Digest authentication
- $env->{AUTH_TYPE} = $c->auth_type;
- $env->{AUTH_USER} = $c->user;
- $env->{AUTH_NAME} = $r->auth_name;
- $env->{REMOTE_USER} = $c->user;
- $env->{AUTH_PASSWD} = $r->get_basic_auth_pw;
- }
- }
- $self->{'ServerVariables'} = bless $env, 'Apache::ASP::Collection';
-
- # assign no matter what so Form is always defined
- my $form = {};
- my %upload;
- my $headers_in = $self->{asp}{headers_in};
- if($self->{Method} eq 'POST' and $request_binary_read) {
- $self->{TotalBytes} = defined($ENV{CONTENT_LENGTH}) ? $ENV{CONTENT_LENGTH} : $headers_in->get('Content-Length');
- if($headers_in->get('Content-Type') =~ m|^multipart/form-data|) {
- # do the logic here so that the normal form POST processing will not
- # occur either
- $asp->{file_upload_process} = &config($asp, 'FileUploadProcess', undef, 1);
- if($asp->{file_upload_process}) {
- if($asp->{file_upload_temp} = &config($asp, 'FileUploadTemp')) {
- eval "use CGI;";
- } else {
- # default leaves no temp files for prying eyes
- eval "use CGI qw(-private_tempfiles);";
- }
- if($@) {
- $self->{asp}->Error("can't use file upload without CGI.pm: $@");
- goto ASP_REQUEST_POST_READ_DONE;
- }
-
- # new behavior for file uploads when FileUploadMax is exceeded,
- # before it used to error abruptly, now it will simply skip the file
- # upload data
- local $CGI::DISABLE_UPLOADS = $CGI::DISABLE_UPLOADS;
- if($asp->{file_upload_max} = &config($asp, 'FileUploadMax')) {
- if($self->{TotalBytes} > $asp->{file_upload_max} ) {
- $CGI::DISABLE_UPLOADS = 1;
- }
- }
-
- $asp->{dbg} && $asp->Debug("using CGI.pm version ".
- (eval { CGI->VERSION } || $CGI::VERSION).
- " for file upload support"
- );
-
- my %form;
- my $q = $self->{cgi} = new CGI;
- $asp->Debug($q->param);
- for(my @names = $q->param) {
- my @params = $q->param($_);
- $form{$_} = @params > 1 ? [ @params ] : $params[0];
- if(ref($form{$_}) eq 'Fh') {
- my $fh = $form{$_};
- binmode $fh if $asp->{win32};
- $upload{$_} = $q->uploadInfo($fh);
- if($asp->{file_upload_temp}) {
- $upload{$_}{TempFile} = $q->tmpFileName($fh);
- $upload{$_}{TempFile} =~ s|^/+|/|;
- }
- $upload{$_}{BrowserFile} = "$fh";
- $upload{$_}{FileHandle} = $fh;
- $upload{$_}{ContentType} = $upload{$_}{'Content-Type'};
- # tie the file upload reference to a collection... %upload
- # may be many file uploads note.
- $upload{$_} = bless $upload{$_}, 'Apache::ASP::Collection';
- $asp->{dbg} && $asp->Debug("file upload field processed for \$Request->{FileUpload}{$_}", $upload{$_});
- }
- }
- $form = \%form;
- } else {
- $self->{asp}->Debug("FileUploadProcess is disabled, file upload data in \$Request->BinaryRead");
- }
-
- } else {
- # Only tie to STDIN if we have cached contents
- # don't untie *STDIN until DESTROY, so filtered handlers
- # have an opportunity to use any cached contents that may exist
- if(my $len = $self->{TotalBytes}) {
- $self->{content} = $self->BinaryRead($len) || '';
- tie(*STDIN, 'Apache::ASP::Request', $self);
- if($headers_in->get('Content-Type') eq 'application/x-www-form-urlencoded') {
- $form = &ParseParams($self, \$self->{content});
- } else {
- $form = {};
- }
- }
- }
- }
-
- ASP_REQUEST_POST_READ_DONE:
-
- $self->{'Form'} = bless $form, 'Apache::ASP::Collection';
- $self->{'FileUpload'} = bless \%upload, 'Apache::ASP::Collection';
- my $query = $r->args();
- my $parsed_query = $query ? &ParseParams($self, \$query) : {};
- $self->{'QueryString'} = bless $parsed_query, 'Apache::ASP::Collection';
-
- if(&config($asp, 'RequestParams')) {
- $self->{'Params'} = bless { %$parsed_query, %$form }, 'Apache::ASP::Collection';
- }
-
- # do cookies now
- my %cookies;
- if(my $cookie = $headers_in->get('Cookie')) {
- my @parts = split(/;\s*/, ($cookie || ''));
- for(@parts) {
- my($name, $value) = split(/\=/, $_, 2);
- $name = &Unescape($self, $name);
-
- next if ($name eq $Apache::ASP::SessionCookieName);
- next if $cookies{$name}; # skip dup's
-
- $cookies{$name} = ($value =~ /\=/) ?
- &ParseParams($self, $value) : &Unescape($self, $value);
- }
- }
- $self->{Cookies} = bless \%cookies, 'Apache::ASP::Collection';
-
- $self;
- }
-
- sub DESTROY {
- my $self = shift;
-
- if($self->{cgi}) {
- # make sure CGI file handles are freed
- $self->{cgi}->DESTROY();
- $self->{cgi} = undef;
- }
-
- for(keys %{$self->{FileUpload}}) {
- my $upload = $self->{FileUpload}{$_};
- $self->{Form}{$_} = undef;
- if($upload->{FileHandle}) {
- close $upload->{FileHandle};
- # $self->{asp}->Debug("closing fh $upload->{FileHandle}");
- }
- $self->{FileUpload}{$_} = undef;
- }
-
- %$self = ();
- }
-
- # just returns itself
- sub TIEHANDLE { $_[1] };
-
- # just spill the cache into the scalar, so multiple reads are
- # fine... whoever is reading from the cached contents must
- # be reading the whole thing just once for this to work,
- # which is fine for CGI.pm
- sub READ {
- my $self = $_[0];
- $_[1] ||= '';
- $_[1] .= $self->{content};
- $self->{ServerVariables}{CONTENT_LENGTH};
- }
-
- sub BINMODE { };
-
- # COLLECTIONS, normal, Cookies are special, with the dictionary lookup
- # directly aliased as this should be faster than autoloading
- sub Form { shift->{Form}->Item(@_) }
- sub FileUpload { shift->{FileUpload}->Item(@_) }
- sub QueryString { shift->{QueryString}->Item(@_) }
- sub ServerVariables { shift->{ServerVariables}->Item(@_) }
-
- sub Params {
- my $self = shift;
- $self->{Params}
- || die("\$Request->Params object does not exist, enable with 'PerlSetVar RequestParams 1'");
- $self->{Params}->Item(@_);
- }
-
- sub BinaryRead {
- my($self, $length) = @_;
- my $data;
- return undef unless $self->{TotalBytes};
-
- if(ref(tied(*STDIN)) && tied(*STDIN)->isa('Apache::ASP::Request')) {
- if($self->{TotalBytes}) {
- if(defined $length) {
- return substr($self->{content}, 0, $length);
- } else {
- return $self->{content}
- }
- } else {
- return undef;
- }
- } else {
- defined($length) || ( $length = $self->{TotalBytes} );
- my $asp = $self->{asp};
- my $r = $asp->{r};
- if(! $ENV{MOD_PERL}) {
- my $rv = sysread(*STDIN, $data, $length, 0);
- $asp->{dbg} && $asp->Debug("read $rv bytes from STDIN for CGI mode, tried $length bytes");
- } else {
- $r->read($data, $length);
- $asp->{dbg} && $asp->Debug("read ".length($data)." bytes, tried $length bytes");
- }
- return $data;
- }
- }
-
- sub Cookies {
- my($self, $name, $key) = @_;
-
- if(! $name) {
- $self->{Cookies};
- } elsif($key) {
- $self->{Cookies}{$name}{$key};
- } else {
- # when we just have the name, are we expecting a dictionary or not
- my $cookie = $self->{Cookies}{$name};
- if(ref $cookie && wantarray) {
- return %$cookie;
- } else {
- # CollectionItem support here one day, to not return
- # an undef object, CollectionItem needs tied hash support
- return $cookie;
- }
- }
- }
-
- sub ParseParams {
- my($self, $string) = @_;
- ($string = $$string) if ref($string); ## faster if we pass a ref for a big string
-
- my %params;
- defined($string) || return(\%params);
- my @params = split /[\&\;]/, $string, -1;
-
- # we have to iterate through the params here to collect multiple values for
- # the same param, say from a multiple select statement
- for my $pair (@params) {
- my($key, $value) = map {
- # inline for greater efficiency
- # &Unescape($self, $_)
- my $todecode = $_;
- $todecode =~ tr/+/ /; # pluses become spaces
- $todecode =~ s/%([0-9a-fA-F]{2})/chr(hex($1))/ge;
- $todecode;
- } split (/\=/, $pair, 2);
- if(defined $params{$key}) {
- my $collect = $params{$key};
-
- if(ref $collect) {
- # we have already collected more than one param for that key
- push(@{$collect}, $value);
- } else {
- # this is the second value for a key we've seen, start array
- $params{$key} = [$collect, $value];
- }
- } else {
- # normal use, one to one key value pairs, just set
- $params{$key} = $value;
- }
- }
-
- \%params;
- }
-
- # unescape URL-encoded data
- sub Unescape {
- my $todecode = $_[1];
- $todecode =~ tr/+/ /; # pluses become spaces
- $todecode =~ s/%([0-9a-fA-F]{2})/chr(hex($1))/ge;
- $todecode;
- }
-
- *config = *Apache::ASP::config;
-
- 1;
-