####
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;
}
}