root/trunk/lib/Mungo/MultipartFormData.pm

Revision 11, 4.1 kB (checked in by jesus, 7 years ago)

reset the file to the beginning when we are finished building it, closes #4

Line 
1 package Mungo::MultipartFormData;
2
3 # Copyright (c) 2007 OmniTI Computer Consulting, Inc. All rights reserved.
4 # For information on licensing see:
5 #   https://labs.omniti.com/zetaback/trunk/LICENSE
6
7 use strict;
8 use IO::Scalar;
9 use Mungo;
10 use Mungo::Request;
11
12 sub new {
13   my $class = shift;
14   my $self = bless {}, $class;
15   $self->load(@_);
16
17   # Now merge up the parts into the hash itself, so it looks "normal"
18   foreach my $part (@{$self->{parts}}) {
19     if($part->{name}) {
20       if($part->{filename}) {
21         $self->{$part->{name}} = $part;
22         # Make this payload into an IO::Scalar
23         if($part->{payload}) {
24           $part->{handle} = IO::Scalar->new(\$part->{payload});
25           delete $part->{payload};
26         }
27         $part->{handle}->seek(0,0);
28       }
29       else {
30         $self->{$part->{name}} = $part->{payload};
31       }
32     }
33     delete $part->{maxmem};
34     delete $part->{name};
35   }
36   delete $self->{parts};
37   $self;
38 }
39
40 sub load {
41   my ($self, $r, $cl, $b) = @_;
42   my $BLOCK = $r->dir_config('PostBlockSize') || $Mungo::DEFAULT_POST_BLOCK;
43   my $MAXSIZE = $r->dir_config('PostMaxSize') || $Mungo::DEFAULT_POST_MAX_SIZE;
44   my $MAXPART = $r->dir_config('PostMaxPart') || $Mungo::DEFAULT_POST_MAX_PART;
45   my $MAXMEM = $r->dir_config('PostMaxInMemory') ||
46                  $Mungo::DEFAULT_POST_MAX_IN_MEMORY;
47
48   # I expect to see the boundary as the first thing.. so $BLOCK has to be
49   # at least the length of boundary + CR LF
50   $BLOCK = length($b) + 2 unless($BLOCK > length($b) + 2);
51
52   my $bytes_read = 0;
53   my $part = '';
54   my $buffer = "\r\n";
55   my $new_buffer = '';
56   my $current_part;
57   while($bytes_read < $cl) {
58     my $to_read = ($BLOCK < $cl - $bytes_read) ? $BLOCK : ($cl - $bytes_read);
59     $r->read($new_buffer, $to_read);
60     $buffer .= $new_buffer;
61     my $pos;
62     while(($pos = index($buffer, "\r\n--$b\r\n")) >= 0) {
63       if($current_part) {
64         $current_part->append(substr($buffer, 0, $pos));
65       }
66       $current_part = Mungo::MultipartFormData::Part->new($MAXMEM);
67       push @{$self->{parts}}, $current_part;
68       substr($buffer, 0, $pos + length($b) + 6) = '';
69     }
70     if(!$current_part) {
71       $current_part = Mungo::MultipartFormData::Part->new($MAXMEM);
72       push @{$self->{parts}}, $current_part;
73     }
74     if(($pos = index($buffer, "\r\n--$b--")) >= 0) {
75       $current_part->append(substr($buffer, 0, $pos));
76       $buffer = '';
77     }
78     elsif(length($buffer) > length($b) + 6) {
79       # This is to make sure we leave enough to index() in the next pass
80       $current_part->append(substr($buffer, 0,
81                                    length($buffer) - length($b) - 6));
82       substr($buffer, 0, length($buffer) - length($b) - 6) = '';
83     }
84     $bytes_read += length($new_buffer);
85   }
86 }
87
88 package Mungo::MultipartFormData::Part;
89
90 use strict;
91 use File::Temp qw/:POSIX/;
92
93 sub new {
94   my $class = shift;
95   my $maxmem = shift;
96   return bless { payload => '', maxmem => $maxmem }, $class;
97 }
98
99 sub extract_headers {
100   my $self = shift;
101   # We already extracted out headers
102   return if($self->{name});
103   my $pos = index($self->{payload}, "\r\n\r\n");
104   my @headers = split(/\r\n/, substr($self->{payload}, 0, $pos));
105   # Consume it
106   substr($self->{payload}, 0, $pos + 4) = '';
107   $self->{size} = length($self->{payload});
108   foreach my $header (@headers) {
109     my ($k, $v) = split(/:\s+/, $header, 2);
110     $self->{lc $k} = $v;
111     if(lc $k eq 'content-disposition') {
112       if($v =~ /^form-data;/) {
113         $self->{name} = $1 if($v =~ / name="([^;]*)"/);
114         $self->{filename} = $1 if($v =~ / filename="([^;]*)"/);
115       }
116     }
117   }
118 }
119 sub append {
120   my $self = shift;
121   my $buffer = shift;
122   $self->{size} += length($buffer);
123   if(exists($self->{handle})) {
124     $self->{handle}->print($buffer);
125   }
126   else {
127     $self->{payload} .= $buffer;
128     $self->extract_headers();
129     if(length($self->{payload}) > $self->{maxmem}) {
130       my($fh, $file) = tmpnam();
131       if(!$fh) {
132         print STDERR "Could not create tmpfile (for POST storage)\n";
133         return undef;
134       }
135       unlink($file);
136       $self->{handle} = $fh;
137       $self->{handle}->print($self->{payload});
138       delete $self->{payload};
139     }
140   }
141 }
142
143 1;
Note: See TracBrowser for help on using the browser.