#### sub getforminput { my %opt = @_; # OPTIONS: # multiples - What to do if there are multiple inputs with the # same name. By default, you get an arrayref, but # if you set this to 'first' or 'last', you'll get # the first or last value, respectively. 'join' # will "firstval,secondval,thirdval,...,lastval". # filename - If true, and if the browser supplies a filename for # a file upload, send it as 'filename'. (Multiples # are not supported by this, so you can't also have # a form element named 'filename'.) Default is # to ignore any user-supplied filename(s), which # is generally recommended for security anyway. # content_type - If true, and the browser supplies a Content-type # with a file upload, send it as 'content_type', # with similar caveats as for filename. use Taint; die "Cannot both fold and reject multiples.\n" if $opt{fold_multiples} and $opt{reject_multiples}; my ($formdata, %input); { my $num_bytes=$ENV{CONTENT_LENGTH}; if ($num_bytes > 0) { $num_bytes == read (STDIN, $formdata, $num_bytes) or warn "CONTENT_LENGTH is full of lies!"; } else { $formdata=$ENV{QUERY_STRING}; } } loginput($formdata) if $formdata; if ($ENV{CONTENT_TYPE}=~/multipart\/form-data.*boundary=(.+?)$/) { my $boundary=$1; Taint::taint($boundary); foreach my $part (split /--$boundary/, $formdata) { my $partname=""; my ($headers, $value, @moreval) = $part =~ /^(.*?)\r?\n\s?\r?\n(.*?)(?:\r?\n)?$/s; Taint::taint($headers, $value); $value=join("\n\n", ($value, @moreval)) if @moreval; foreach my $h (split (/\r?\n/, $headers)) { if ($h =~ /Content-Disposition: ([^;]+); (.*?)$/) { my ($content_disposition, $t)=($1,$2); Taint::taint($t); foreach (split /; /, $t) { if (/^name=(.*?)$/) { ($partname) = $1 =~ /\"?([^"]*)/; Taint::taint($partname); } elsif (/^filename=(.*?)$/ and $opt{filename}) { my ($filename) = $1 =~ /\"?([^"]*)/; Taint::taint($filename); $input{filename} = $filename; # Note that multiples aren't supported for this. } } } else { if ($h =~ /Content-Type:\s+(.*)/ and $opt{content_type}) { ($input{content_type}) = $1 =~ m!([\w]+/[\w]+)!; } } } if ($partname) { if ($opt{multiples} eq 'first') { $input{$partname} = $value unless exists $input{$partname}; # Take first value only; reject subsequent ones. } elsif ($opt{multiples} eq 'last') { $input{$partname} = $value; # Take the latest value every time. } elsif ($opt{multiples} eq 'join') { $input{$partname} = (join ",", $input{$partname} , $value); # Join with commas. } else { # # Default: construct an arrayref if necessary. if (exists $input{$partname} and ref $input{$partname}) { push @{$input{$partname}}, $value; } elsif (exists $input{$partname}) { $input{$partname} = [$input{$partname}, $value]; } else { $input{$partname} = $value; }} }} } else { foreach (split /&/, $formdata) { s/\+/ /g; # That's how CGI encodes spaces. my ($name, $value) = split(/=/, $_); $name =~ s/%(..)/pack("c",hex($1))/ge; # These lines reverse the %nn encodings $value =~ s/%(..)/pack("c",hex($1))/ge; # CGI does for punctuation marks and such. Taint::taint($name, $value); $input{$name}=$value; }} if ($formdata) { return \%input; } else { return undef; } }