| Apache-ParseFormData documentation | Contained in the Apache-ParseFormData distribution. |
Apache::ParseFormData - Perl extension for dealing with client request data
use Apache::RequestRec ();
use Apache::RequestUtil ();
use Apache::Const -compile => qw(DECLINED OK);
use Apache::ParseFormData;
sub handler {
my $r = shift;
my $apr = Apache::ParseFormData->new($r);
my $scalar = 'abc';
$apr->param('scalar_test' => $scalar);
my $s_test = $apr->param('scalar_test');
print $s_test;
my @array = ('a', 'b', 'c');
$apr->param('array_test' => \@array);
my @a_test = $apr->param('array_test');
print $a_test[0];
my %hash = (
a => 1,
b => 2,
c => 3,
);
$apr->param('hash_test' => \%hash);
my %h_test = $apr->param('hash_test');
print $h_test{'a'};
$apr->notes->clear();
return Apache::OK;
}
The Apache::ParseFormData module allows you to easily decode and parse form and query data, even multipart forms generated by "file upload". This module only work with mod_perl 2.
Apache::ParseFormData extension parses a GET and POST requests, with
multipart form data input stream, and saves any files/parameters
encountered for subsequent use.
Create a new Apache::ParseFormData object. The methods from Apache class are inherited. The optional arguments which can be passed to the method are the following:
Directory where the upload files are stored.
Disable file uploads.
my $apr = Apache::ParseFormData->new($r, disable_uploads => 1);
my $status = $apr->parse_result;
unless($status == Apache::OK) {
my $error = $apr->notes->get("error-notes");
...
return $status;
}
Limit the size of POST data.
my $apr = Apache::ParseFormData->new($r, post_max => 1024);
my $status = $apr->parse_result;
unless($status == Apache::OK) {
my $error = $apr->notes->get("error-notes");
...
return $status;
}
return the status code after the request is parsed.
Like CGI.pm you can add or modify the value of parameters within your script.
my $scalar = 'abc';
$apr->param('scalar_test' => $scalar);
my $s_test = $apr->param('scalar_test');
print $s_test;
my @array = ('a', 'b', 'c');
$apr->param('array_test' => \@array);
my @a_test = $apr->param('array_test');
print $a_test[0];
my %hash = (
a => 1,
b => 2,
c => 3,
);
$apr->param('hash_test' => \%hash);
my %h_test = $apr->param('hash_test');
print $h_test{'a'};
You can create a parameter with multiple values by passing additional arguments:
$apr->param(
'color' => "red",
'numbers' => [0,1,2,3,4,5,6,7,8,9],
'language' => "perl",
);
Fetching the names of all the parameters passed to your script:
foreach my $name (@names) {
my $value = $apr->param($name);
print "$name => $value\n";
}
To delete a parameter provide the name of the parameter:
$apr->delete("color");
You can delete multiple values:
$apr->delete("color", "nembers");
This method clear all of the parameters
You can access the name of an uploaded file with the param method, just like the value of any other form element.
my %file_hash = $apr->param('file');
my $filename = $file_hash{'filename'};
my $content_type = $file_hash{'type'};
my $size = $file_hash{'size'};
my ($fh, $path) = $apr->upload('file_0');
for my $form_name ($apr->upload()) {
my ($fh, $path) = $apr->upload($form_name);
while(<$fh>) {
print $_;
}
my %file_hash = $apr->param($form_name);
my $filename = $file_hash{'filename'};
my $content_type = $file_hash{'type'};
my $size = $file_hash{'size'};
unlink($path);
}
libapreq, Apache::Request
This interface is based on the libapreq by Doug MacEachern.
Henrique Dias, <hdias@aesbuc.pt>
Copyright 2003 by Henrique Dias
This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself.
| Apache-ParseFormData documentation | Contained in the Apache-ParseFormData distribution. |
############################################################################# # # Apache::ParseFormData # Last Modification: Thu Oct 23 11:44:58 WEST 2003 # # Copyright (c) 2003 Henrique Dias <hdias@aesbuc.pt>. All rights reserved. # This module is free software; you can redistribute it and/or modify # it under the same terms as Perl itself. # ############################################################################## package Apache::ParseFormData; use strict; use Apache::Log; use Apache::Const -compile => qw(OK M_POST M_GET FORBIDDEN HTTP_REQUEST_ENTITY_TOO_LARGE); use Apache::RequestIO (); use APR::Table; use IO::File; use POSIX qw(tmpnam); require Exporter; our @ISA = qw(Exporter Apache::RequestRec); our %EXPORT_TAGS = ( 'all' => [ qw() ] ); our @EXPORT = qw(); our $VERSION = '0.09'; require 5; use constant NELTS => 10; use constant BUFFLENGTH => 1024; sub new { my $proto = shift; my $class = ref($proto) || $proto; my $self = shift; my %args = ( temp_dir => "/tmp", disable_uploads => 0, post_max => 0, @_, ); my $table = APR::Table::make($self->pool, NELTS); $self->pnotes('apr_req' => $table); bless ($self, $class); if(my $data = $self->headers_in->get('cookie')) { &_parse_query($self, $data, " *; *"); } if($self->method_number == Apache::M_POST) { $self->pnotes('apr_req_result' => &parse_content($self, \%args)); } elsif($self->method_number == Apache::M_GET) { my $data = $self->args(); &_parse_query($self, $data) if($data); $self->pnotes('apr_req_result' => Apache::OK); } return($self); } sub DESTROY { my $self = shift; for my $v (values(%{$self->pnotes('upload')})) { my $path = $v->[1]; unlink($path) if(-e $path); } } sub parse_result { $_[0]->pnotes('apr_req_result') } sub parms { $_[0]->pnotes('apr_req') } sub _parse_query { my $r = shift; my $query_string = shift; my $re = shift || "&"; my %hash = (); for(split(/$re/, $query_string)) { my ($n, $v) = split(/=/); defined($v) or $v = ""; &decode_chars($n); &decode_chars($v); push(@{$hash{$n}}, $v); } $r->param(%hash); return(); } sub decode_chars { $_[0] =~ tr/+/ /; $_[0] =~ s/%([\dA-Fa-f][\dA-Fa-f])/pack("C", hex($1))/egi; } sub set_cookie { my $self = shift; my $args = { name => "", value => "", path => "/", expires => "", secure => 0, domain => "", @_, }; $args->{'name'} or return(); my @a = ( join("=", $args->{'name'}, $args->{'value'}), join("=", "path", $args->{'path'}), ); push(@a, join("=", "expires", &cookie_expire($args->{'expires'}))) if($args->{'expires'}); push(@a, join("=", "secure", $args->{'secure'})) if($args->{'secure'}); push(@a, join("=", "domain", $args->{'domain'})) if($args->{'domain'}); $self->headers_out->{'Set-Cookie'} = join(";", @a); $self->param($args->{'name'} => $args->{'value'}); return(); } sub cookie_expire { my $time = shift; my ($sec,$min,$hour,$mday,$mon,$year,$wday) = localtime($time); my @months = qw(Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec); my @weekday = qw(Sun Mon Tue Wed Thu Fri Sat); return sprintf("%3s, %02d-%3s-%04d %02d:%02d:%02d GMT", $weekday[$wday], $mday, $months[$mon], $year+1900, $hour, $min, $sec); } sub upload { my $self = shift; my $name = shift || ""; return($name ? @{$self->pnotes('upload')->{$name}} : keys(%{$self->pnotes('upload')})); } sub parse_content { my $r = shift; my $args = shift; my $buf = ""; $r->setup_client_block; $r->should_client_block or return ''; my $ct = $r->headers_in->get('content-type'); if($args->{'disable_uploads'} && index($ct, "multipart/form-data") > -1) { my $error_str = "[Apache::ParseFormData] file upload forbidden"; $r->notes->set("error-notes" => $error_str); $r->log_error($error_str); return(Apache::FORBIDDEN); } my $rm = $r->remaining; if($args->{'post_max'} && ($rm > $args->{'post_max'})) { my $pm = $args->{'post_max'}; my $error_str = "[Apache::ParseFormData] entity too large ($rm, max=$pm)"; $r->notes->set("error-notes" => $error_str); $r->log_error($error_str); return(Apache::HTTP_REQUEST_ENTITY_TOO_LARGE); } if($ct =~ /^multipart\/form-data; boundary=(.+)$/) { my $boundary = $1; my $lenbdr = length("--$boundary"); $r->get_client_block($buf, $lenbdr+2); $buf = substr($buf, $lenbdr); $buf =~ s/[\n\r]+//; my $iter = -1; my @data = (); &multipart_data($r, $args, \@data, $boundary, BUFFLENGTH, 1, $buf, $iter); my %uploads = (); for(@data) { if(exists($_->{'headers'}->{'content-disposition'})) { my @a = split(/ *; */, $_->{'headers'}->{'content-disposition'}); if(shift(@a) eq "form-data") { if(scalar(@a) == 1) { my ($key) = ($a[0] =~ /name=\"([^\"]+)\"/); $r->param($key => $_->{'values'} || ""); } else { (ref($_->{'values'}) eq "ARRAY") or next; my ($fh, $path) = @{$_->{'values'}}; seek($fh, 0, 0); my %hash = ( filename => "", type => exists($_->{'headers'}->{'content-type'}) ? $_->{'headers'}->{'content-type'} : "", size => ($fh->stat())[7], ); my $param = ""; for(@a) { my ($name, $value) = (/([^=]+)=\"([^\"]+)\"/); if($name eq "name") { $uploads{$value} = [$fh, $path]; $param = $value; } else { $hash{$name} = $value; } } $r->param($param => \%hash); } } } } $r->pnotes('upload' => \%uploads); } else { my $len = $r->headers_in->get('content-length'); $r->get_client_block($buf, $len); &_parse_query($r, $buf) if($buf); } return(Apache::OK); } sub extract_headers { my $raw = shift; my %hash = (); for(split(/\r?\n/, $raw)) { s/[\r\n]+$//; $_ or next; my ($h, $v) = split(/ *: */, $_, 2); $hash{lc($h)} = $v; } $_[0] = \%hash; return(exists($hash{'content-type'})); } sub output_data { my $dest = shift; my $data = shift; if(ref($dest->{values}) eq "ARRAY") { my $fh = $dest->{values}->[0]; print $fh $data; } else { $dest->{values} .= $data; } } sub new_tmp_file { my $temp_dir = shift; my $data = shift; my $path = ""; my $fh; my $i = 0; do { $i < 3 or last; my $name = tmpnam(); $name = (split("/", $name))[-1]; $path = join("/", $temp_dir, $name); $i++; } until($fh = IO::File->new($path, O_RDWR|O_CREAT|O_EXCL)); defined($fh) or return("Couldn't create temporary file: $path"); binmode($fh); $fh->autoflush(1); $data->{values} = [$fh, $path]; return(); } sub multipart_data { my $r = shift; my $args = shift; my $data = shift; my $boundary = shift; my $len = shift; my $h = shift; my $buff = shift; my ($part, $content) = ($buff, ""); while($r->get_client_block($buff, $len)) { $part .= $buff; if($h) { if($part =~ /\r?\n\r?\n/) { my ($left, $right) = ($`, $'); $left =~ s/[\r\n]+$//; $_[0]++; push(@{$data}, {values => "", headers => {}}); if(&extract_headers($left, $data->[$_[0]]->{'headers'})) { if(my $error = &new_tmp_file($args->{'temp_dir'}, $data->[$_[0]])) { $r->log->warn($error), next; } } $part = $content = $right; $h = 0; } else { next; } } if($part =~ /\r?\n--$boundary\r?\n/) { my ($left, $right) = ($`, $'); &output_data($data->[$_[0]], $left) if($left); &multipart_data($r, $args, $data, $boundary, $len, 1, $right, $_[0]); $part = ""; } if($part) { $content = substr($part, 0, int($len/2)); &output_data($data->[$_[0]], $content) if($content); $part = substr($part, int($len/2)); } } if($h && $part =~ /\r?\n\r?\n/) { my ($left, $right) = ($`, $'); $left =~ s/[\r\n]+$//; $_[0]++; push(@{$data}, {values => "", headers => {}}); if(&extract_headers($left, $data->[$_[0]]->{'headers'})) { if(my $error = &new_tmp_file($args->{'temp_dir'}, $data->[$_[0]])) { $r->log->warn($error), next; } } $part = $right; $h = 0; } if($part =~ /\r?\n--$boundary\r?\n/) { my ($left, $right) = ($`, $'); &output_data($data->[$_[0]], $left) if($left); &multipart_data($r, $args, $data, $boundary, $len, 1, $right, $_[0]); $part = ""; } if($part =~ /\r?\n--$boundary--[\r\n]*/) { my $left = $`; &output_data($data->[$_[0]], $left) if($left); } return(); } sub delete { my $self = shift; map { $self->parms->unset($_); } @_; return(); } sub delete_all { my $self = shift; $self->parms->clear(); return(); } sub param { my $self = shift; if(scalar(@_) > 1) { my %hash = @_; while(my ($k, $v) = each(%hash)) { my @transfer = (ref($v) eq "HASH") ? %{$v} : (ref($v) eq "ARRAY") ? @{$v} : ($v); my $first = shift(@transfer) || ""; $self->parms->set($k => $first); map { $self->parms->add($k, $_); } @transfer; } return(); } if(scalar(@_) == 1) { my $k = shift; return($self->parms->get($k)); } return(keys(%{$self->parms})); } 1; __END__