/[pkg-rrfw]/libapache2-parseformdata-perl/ParseFormData.pm
ViewVC logotype

Contents of /libapache2-parseformdata-perl/ParseFormData.pm

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1.1.1.1 - (show annotations) (download) (vendor branch)
Sun Aug 8 03:03:30 2004 UTC (8 years, 9 months ago) by jurij-guest
Branch: APACHE_PARSEFORMDATA_0_09_VENDOR, MAIN
CVS Tags: APACHE_PARSEFORMDATA_0_09_RELEASE, HEAD
Changes since 1.1: +0 -0 lines
* Initial population.
1 #############################################################################
2 #
3 # Apache::ParseFormData
4 # Last Modification: Thu Oct 23 11:44:58 WEST 2003
5 #
6 # Copyright (c) 2003 Henrique Dias <hdias@aesbuc.pt>. All rights reserved.
7 # This module is free software; you can redistribute it and/or modify
8 # it under the same terms as Perl itself.
9 #
10 ##############################################################################
11 package Apache::ParseFormData;
12
13 use strict;
14 use Apache::Log;
15 use Apache::Const -compile => qw(OK M_POST M_GET FORBIDDEN HTTP_REQUEST_ENTITY_TOO_LARGE);
16 use Apache::RequestIO ();
17 use APR::Table;
18 use IO::File;
19 use POSIX qw(tmpnam);
20 require Exporter;
21 our @ISA = qw(Exporter Apache::RequestRec);
22 our %EXPORT_TAGS = ( 'all' => [ qw() ] );
23 our @EXPORT = qw();
24 our $VERSION = '0.09';
25 require 5;
26
27 use constant NELTS => 10;
28 use constant BUFFLENGTH => 1024;
29
30 sub new {
31 my $proto = shift;
32 my $class = ref($proto) || $proto;
33 my $self = shift;
34 my %args = (
35 temp_dir => "/tmp",
36 disable_uploads => 0,
37 post_max => 0,
38 @_,
39 );
40 my $table = APR::Table::make($self->pool, NELTS);
41 $self->pnotes('apr_req' => $table);
42 bless ($self, $class);
43
44 if(my $data = $self->headers_in->get('cookie')) {
45 &_parse_query($self, $data, " *; *");
46 }
47 if($self->method_number == Apache::M_POST) {
48 $self->pnotes('apr_req_result' => &parse_content($self, \%args));
49 } elsif($self->method_number == Apache::M_GET) {
50 my $data = $self->args();
51 &_parse_query($self, $data) if($data);
52 $self->pnotes('apr_req_result' => Apache::OK);
53 }
54 return($self);
55 }
56
57 sub DESTROY {
58 my $self = shift;
59 for my $v (values(%{$self->pnotes('upload')})) {
60 my $path = $v->[1];
61 unlink($path) if(-e $path);
62 }
63 }
64
65 sub parse_result { $_[0]->pnotes('apr_req_result') }
66
67 sub parms { $_[0]->pnotes('apr_req') }
68
69 sub _parse_query {
70 my $r = shift;
71 my $query_string = shift;
72 my $re = shift || "&";
73
74 my %hash = ();
75 for(split(/$re/, $query_string)) {
76 my ($n, $v) = split(/=/);
77 defined($v) or $v = "";
78 &decode_chars($n);
79 &decode_chars($v);
80 push(@{$hash{$n}}, $v);
81 }
82 $r->param(%hash);
83 return();
84 }
85
86 sub decode_chars {
87 $_[0] =~ tr/+/ /;
88 $_[0] =~ s/%([\dA-Fa-f][\dA-Fa-f])/pack("C", hex($1))/egi;
89 }
90
91 sub set_cookie {
92 my $self = shift;
93 my $args = {
94 name => "",
95 value => "",
96 path => "/",
97 expires => "",
98 secure => 0,
99 domain => "",
100 @_,
101 };
102 $args->{'name'} or return();
103 my @a = (
104 join("=", $args->{'name'}, $args->{'value'}),
105 join("=", "path", $args->{'path'}),
106 );
107 push(@a, join("=", "expires", &cookie_expire($args->{'expires'}))) if($args->{'expires'});
108 push(@a, join("=", "secure", $args->{'secure'})) if($args->{'secure'});
109 push(@a, join("=", "domain", $args->{'domain'})) if($args->{'domain'});
110 $self->headers_out->{'Set-Cookie'} = join(";", @a);
111 $self->param($args->{'name'} => $args->{'value'});
112 return();
113 }
114
115 sub cookie_expire {
116 my $time = shift;
117 my ($sec,$min,$hour,$mday,$mon,$year,$wday) = localtime($time);
118 my @months = qw(Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec);
119 my @weekday = qw(Sun Mon Tue Wed Thu Fri Sat);
120 return sprintf("%3s, %02d-%3s-%04d %02d:%02d:%02d GMT", $weekday[$wday], $mday, $months[$mon], $year+1900, $hour, $min, $sec);
121 }
122
123 sub upload {
124 my $self = shift;
125 my $name = shift || "";
126 return($name ? @{$self->pnotes('upload')->{$name}} : keys(%{$self->pnotes('upload')}));
127 }
128
129 sub parse_content {
130 my $r = shift;
131 my $args = shift;
132
133 my $buf = "";
134 $r->setup_client_block;
135 $r->should_client_block or return '';
136 my $ct = $r->headers_in->get('content-type');
137
138 if($args->{'disable_uploads'} && index($ct, "multipart/form-data") > -1) {
139 my $error_str = "[Apache::ParseFormData] file upload forbidden";
140 $r->notes->set("error-notes" => $error_str);
141 $r->log_error($error_str);
142 return(Apache::FORBIDDEN);
143 }
144 my $rm = $r->remaining;
145 if($args->{'post_max'} && ($rm > $args->{'post_max'})) {
146 my $pm = $args->{'post_max'};
147 my $error_str = "[Apache::ParseFormData] entity too large ($rm, max=$pm)";
148 $r->notes->set("error-notes" => $error_str);
149 $r->log_error($error_str);
150 return(Apache::HTTP_REQUEST_ENTITY_TOO_LARGE);
151 }
152 if($ct =~ /^multipart\/form-data; boundary=(.+)$/) {
153 my $boundary = $1;
154 my $lenbdr = length("--$boundary");
155 $r->get_client_block($buf, $lenbdr+2);
156 $buf = substr($buf, $lenbdr);
157 $buf =~ s/[\n\r]+//;
158 my $iter = -1;
159 my @data = ();
160 &multipart_data($r, $args, \@data, $boundary, BUFFLENGTH, 1, $buf, $iter);
161 my %uploads = ();
162 for(@data) {
163 if(exists($_->{'headers'}->{'content-disposition'})) {
164 my @a = split(/ *; */, $_->{'headers'}->{'content-disposition'});
165 if(shift(@a) eq "form-data") {
166 if(scalar(@a) == 1) {
167 my ($key) = ($a[0] =~ /name=\"([^\"]+)\"/);
168 $r->param($key => $_->{'values'} || "");
169 } else {
170 (ref($_->{'values'}) eq "ARRAY") or next;
171 my ($fh, $path) = @{$_->{'values'}};
172 seek($fh, 0, 0);
173 my %hash = (
174 filename => "",
175 type => exists($_->{'headers'}->{'content-type'}) ? $_->{'headers'}->{'content-type'} : "",
176 size => ($fh->stat())[7],
177 );
178 my $param = "";
179 for(@a) {
180 my ($name, $value) = (/([^=]+)=\"([^\"]+)\"/);
181 if($name eq "name") {
182 $uploads{$value} = [$fh, $path];
183 $param = $value;
184 } else {
185 $hash{$name} = $value;
186 }
187 }
188 $r->param($param => \%hash);
189 }
190 }
191 }
192 }
193 $r->pnotes('upload' => \%uploads);
194 } else {
195 my $len = $r->headers_in->get('content-length');
196 $r->get_client_block($buf, $len);
197 &_parse_query($r, $buf) if($buf);
198 }
199 return(Apache::OK);
200 }
201
202 sub extract_headers {
203 my $raw = shift;
204 my %hash = ();
205 for(split(/\r?\n/, $raw)) {
206 s/[\r\n]+$//;
207 $_ or next;
208 my ($h, $v) = split(/ *: */, $_, 2);
209 $hash{lc($h)} = $v;
210 }
211 $_[0] = \%hash;
212 return(exists($hash{'content-type'}));
213 }
214
215 sub output_data {
216 my $dest = shift;
217 my $data = shift;
218
219 if(ref($dest->{values}) eq "ARRAY") {
220 my $fh = $dest->{values}->[0];
221 print $fh $data;
222 } else { $dest->{values} .= $data; }
223 }
224
225 sub new_tmp_file {
226 my $temp_dir = shift;
227 my $data = shift;
228
229 my $path = "";
230 my $fh;
231 my $i = 0;
232 do {
233 $i < 3 or last;
234 my $name = tmpnam();
235 $name = (split("/", $name))[-1];
236 $path = join("/", $temp_dir, $name);
237 $i++;
238 } until($fh = IO::File->new($path, O_RDWR|O_CREAT|O_EXCL));
239 defined($fh) or return("Couldn't create temporary file: $path");
240 binmode($fh);
241 $fh->autoflush(1);
242 $data->{values} = [$fh, $path];
243 return();
244 }
245
246 sub multipart_data {
247 my $r = shift;
248 my $args = shift;
249 my $data = shift;
250 my $boundary = shift;
251 my $len = shift;
252 my $h = shift;
253 my $buff = shift;
254
255 my ($part, $content) = ($buff, "");
256 while($r->get_client_block($buff, $len)) {
257 $part .= $buff;
258 if($h) {
259 if($part =~ /\r?\n\r?\n/) {
260 my ($left, $right) = ($`, $');
261 $left =~ s/[\r\n]+$//;
262 $_[0]++;
263 push(@{$data}, {values => "", headers => {}});
264 if(&extract_headers($left, $data->[$_[0]]->{'headers'})) {
265 if(my $error = &new_tmp_file($args->{'temp_dir'}, $data->[$_[0]])) { $r->log->warn($error), next; }
266 }
267 $part = $content = $right;
268 $h = 0;
269 } else { next; }
270 }
271 if($part =~ /\r?\n--$boundary\r?\n/) {
272 my ($left, $right) = ($`, $');
273 &output_data($data->[$_[0]], $left) if($left);
274 &multipart_data($r, $args, $data, $boundary, $len, 1, $right, $_[0]);
275 $part = "";
276 }
277 if($part) {
278 $content = substr($part, 0, int($len/2));
279 &output_data($data->[$_[0]], $content) if($content);
280 $part = substr($part, int($len/2));
281 }
282 }
283 if($h && $part =~ /\r?\n\r?\n/) {
284 my ($left, $right) = ($`, $');
285 $left =~ s/[\r\n]+$//;
286 $_[0]++;
287 push(@{$data}, {values => "", headers => {}});
288 if(&extract_headers($left, $data->[$_[0]]->{'headers'})) {
289 if(my $error = &new_tmp_file($args->{'temp_dir'}, $data->[$_[0]])) { $r->log->warn($error), next; }
290 }
291 $part = $right;
292 $h = 0;
293 }
294 if($part =~ /\r?\n--$boundary\r?\n/) {
295 my ($left, $right) = ($`, $');
296 &output_data($data->[$_[0]], $left) if($left);
297 &multipart_data($r, $args, $data, $boundary, $len, 1, $right, $_[0]);
298 $part = "";
299 }
300 if($part =~ /\r?\n--$boundary--[\r\n]*/) {
301 my $left = $`;
302 &output_data($data->[$_[0]], $left) if($left);
303 }
304 return();
305 }
306
307 sub delete {
308 my $self = shift;
309 map { $self->parms->unset($_); } @_;
310 return();
311 }
312
313 sub delete_all {
314 my $self = shift;
315 $self->parms->clear();
316 return();
317 }
318
319 sub param {
320 my $self = shift;
321
322 if(scalar(@_) > 1) {
323 my %hash = @_;
324 while(my ($k, $v) = each(%hash)) {
325 my @transfer = (ref($v) eq "HASH") ? %{$v} : (ref($v) eq "ARRAY") ? @{$v} : ($v);
326 my $first = shift(@transfer) || "";
327 $self->parms->set($k => $first);
328 map { $self->parms->add($k, $_); } @transfer;
329 }
330 return();
331 }
332 if(scalar(@_) == 1) {
333 my $k = shift;
334 return($self->parms->get($k));
335 }
336 return(keys(%{$self->parms}));
337 }
338
339 1;
340 __END__
341
342 =head1 NAME
343
344 Apache::ParseFormData - Perl extension for dealing with client request data
345
346 =head1 SYNOPSIS
347
348 use Apache::RequestRec ();
349 use Apache::RequestUtil ();
350 use Apache::Const -compile => qw(DECLINED OK);
351 use Apache::ParseFormData;
352
353 sub handler {
354 my $r = shift;
355 my $apr = Apache::ParseFormData->new($r);
356
357 my $scalar = 'abc';
358 $apr->param('scalar_test' => $scalar);
359 my $s_test = $apr->param('scalar_test');
360 print $s_test;
361
362 my @array = ('a', 'b', 'c');
363 $apr->param('array_test' => \@array);
364 my @a_test = $apr->param('array_test');
365 print $a_test[0];
366
367 my %hash = (
368 a => 1,
369 b => 2,
370 c => 3,
371 );
372 $apr->param('hash_test' => \%hash);
373 my %h_test = $apr->param('hash_test');
374 print $h_test{'a'};
375
376 $apr->notes->clear();
377
378 return Apache::OK;
379 }
380
381 =head1 ABSTRACT
382
383 The Apache::ParseFormData module allows you to easily decode and parse
384 form and query data, even multipart forms generated by "file upload".
385 This module only work with mod_perl 2.
386
387 =head1 DESCRIPTION
388
389 C<Apache::ParseFormData> extension parses a GET and POST requests, with
390 multipart form data input stream, and saves any files/parameters
391 encountered for subsequent use.
392
393 =head1 Apache::ParseFormData METHODS
394
395
396 =head2 new
397
398 Create a new I<Apache::ParseFormData> object. The methods from I<Apache>
399 class are inherited. The optional arguments which can be passed to the
400 method are the following:
401
402 =over 3
403
404 =item temp_dir
405
406 Directory where the upload files are stored.
407
408 =item disable_uploads
409
410 Disable file uploads.
411
412 my $apr = Apache::ParseFormData->new($r, disable_uploads => 1);
413
414 my $status = $apr->parse_result;
415 unless($status == Apache::OK) {
416 my $error = $apr->notes->get("error-notes");
417 ...
418 return $status;
419 }
420
421 =item post_max
422
423 Limit the size of POST data.
424
425 my $apr = Apache::ParseFormData->new($r, post_max => 1024);
426
427 my $status = $apr->parse_result;
428 unless($status == Apache::OK) {
429 my $error = $apr->notes->get("error-notes");
430 ...
431 return $status;
432 }
433
434 =back
435
436 =head2 parse_result
437
438 return the status code after the request is parsed.
439
440 =head2 param
441
442 Like I<CGI.pm> you can add or modify the value of parameters within your
443 script.
444
445 my $scalar = 'abc';
446 $apr->param('scalar_test' => $scalar);
447 my $s_test = $apr->param('scalar_test');
448 print $s_test;
449
450 my @array = ('a', 'b', 'c');
451 $apr->param('array_test' => \@array);
452 my @a_test = $apr->param('array_test');
453 print $a_test[0];
454
455 my %hash = (
456 a => 1,
457 b => 2,
458 c => 3,
459 );
460 $apr->param('hash_test' => \%hash);
461 my %h_test = $apr->param('hash_test');
462 print $h_test{'a'};
463
464 You can create a parameter with multiple values by passing additional
465 arguments:
466
467 $apr->param(
468 'color' => "red",
469 'numbers' => [0,1,2,3,4,5,6,7,8,9],
470 'language' => "perl",
471 );
472
473 Fetching the names of all the parameters passed to your script:
474
475 foreach my $name (@names) {
476 my $value = $apr->param($name);
477 print "$name => $value\n";
478 }
479
480 =head2 delete
481
482 To delete a parameter provide the name of the parameter:
483
484 $apr->delete("color");
485
486 You can delete multiple values:
487
488 $apr->delete("color", "nembers");
489
490 =head2 delete_all
491
492 This method clear all of the parameters
493
494 =head2 upload
495
496 You can access the name of an uploaded file with the param method, just
497 like the value of any other form element.
498
499 my %file_hash = $apr->param('file');
500 my $filename = $file_hash{'filename'};
501 my $content_type = $file_hash{'type'};
502 my $size = $file_hash{'size'};
503
504 my ($fh, $path) = $apr->upload('file_0');
505
506 for my $form_name ($apr->upload()) {
507 my ($fh, $path) = $apr->upload($form_name);
508
509 while(<$fh>) {
510 print $_;
511 }
512
513 my %file_hash = $apr->param($form_name);
514 my $filename = $file_hash{'filename'};
515 my $content_type = $file_hash{'type'};
516 my $size = $file_hash{'size'};
517 unlink($path);
518 }
519
520 =head2 set_cookie
521
522 Set the cookies before send any printable data to client.
523
524 my $apr = Apache::ParseFormData->new($r);
525
526 $apr->set_cookie(
527 name => "foo",
528 value => "bar",
529 path => "/cgi-bin/database",
530 expires => time + 3600,
531 secure => 1,
532 domain => ".capricorn.com",
533 );
534
535 Get the value of foo:
536
537 $apr->param('foo');
538
539 Clean cookie:
540
541 $apr->set_cookie(
542 name => "foo",
543 value => "",
544 expires => time - 3600,
545 );
546
547 =head1 SEE ALSO
548
549 libapreq, Apache::Request
550
551 =head1 CREDITS
552
553 This interface is based on the libapreq by Doug MacEachern.
554
555 =head1 AUTHOR
556
557 Henrique Dias, E<lt>hdias@aesbuc.ptE<gt>
558
559 =head1 COPYRIGHT AND LICENSE
560
561 Copyright 2003 by Henrique Dias
562
563 This library is free software; you can redistribute it and/or modify
564 it under the same terms as Perl itself.
565
566 =cut

  ViewVC Help
Powered by ViewVC 1.1.5