1 package HTTP::Body::Multipart;
12 unless ( $self->content_type =~ /boundary=\"?([^\";,]+)\"?/ ) {
13 my $content_type = $self->content_type;
14 Carp::croak("Invalid boudrary in content_type: '$content_type'");
17 $self->{boundary} = $1;
18 $self->{state} = 'preamble';
19 $self->{length} = $self->content_length - $self->content_length * 2;
25 my ( $self, $buffer ) = @_;
27 unless ( defined $buffer ) {
31 $self->{buffer} .= $buffer;
32 $self->{length} += length($buffer);
36 if ( $self->{state} eq 'done' ) {
40 elsif ( $self->{state} =~ /^(preamble|boundary|header|body)$/ ) {
41 my $method = "parse_$1";
42 return $self->{length} unless $self->$method;
46 Carp::croak('Unknown state');
53 $self->{boundary} = shift if @_;
54 return $self->{boundary};
58 return "--" . shift->boundary;
62 return shift->boundary_begin . "--";
71 return $self->crlf . $self->boundary_begin;
76 return $self->crlf . $self->boundary_end;
82 my $index = index( $self->{buffer}, $self->boundary_begin );
84 unless ( $index >= 0 ) {
88 # replace preamble with CRLF so we can match dash-boundary as delimiter
89 substr( $self->{buffer}, 0, $index, $self->crlf );
91 $self->{state} = 'boundary';
99 if ( index( $self->{buffer}, $self->delimiter_begin . $self->crlf ) == 0 ) {
101 substr( $self->{buffer}, 0, length( $self->delimiter_begin ) + 2, '' );
102 $self->{current} = {};
103 $self->{state} = 'header';
108 if ( index( $self->{buffer}, $self->delimiter_end . $self->crlf ) == 0 ) {
109 $self->{current} = {};
110 $self->{state} = 'done';
120 my $crlf = $self->crlf;
121 my $index = index( $self->{buffer}, $crlf . $crlf );
123 unless ( $index >= 0 ) {
127 my $header = substr( $self->{buffer}, 0, $index );
129 substr( $self->{buffer}, 0, $index + 4, '' );
132 for ( split /$crlf/, $header ) {
141 my $token = qr/[^][\x00-\x1f\x7f()<>@,;:\\"\/?={} \t]+/;
143 for my $header (@headers) {
145 $header =~ s/^($token):[\t ]*//;
147 ( my $field = $1 ) =~ s/\b(\w)/uc($1)/eg;
149 if ( exists $self->{current}->{headers}->{$field} ) {
150 for ( $self->{current}->{headers}->{$field} ) {
151 $_ = [$_] unless ref($_) eq "ARRAY";
152 push( @$_, $header );
156 $self->{current}->{headers}->{$field} = $header;
160 $self->{state} = 'body';
168 my $index = index( $self->{buffer}, $self->delimiter_begin );
172 # make sure we have enough buffer to detect end delimiter
173 my $length = length( $self->{buffer} ) - ( length( $self->delimiter_end ) + 2 );
175 unless ( $length > 0 ) {
179 $self->{current}->{data} .= substr( $self->{buffer}, 0, $length, '' );
180 $self->{current}->{size} += $length;
181 $self->{current}->{done} = 0;
183 $self->handler( $self->{current} );
188 $self->{current}->{data} .= substr( $self->{buffer}, 0, $index, '' );
189 $self->{current}->{size} += $index;
190 $self->{current}->{done} = 1;
192 $self->handler( $self->{current} );
194 $self->{state} = 'boundary';
200 my ( $self, $part ) = @_;
202 if ( $part->{done} && $part->{size} == 0 ) {
206 unless ( $self->{seen}->{"$part"}++ ) {
208 my $disposition = $part->{headers}->{'Content-Disposition'};
209 my ($name) = $disposition =~ / name="?([^\";]+)"?"/;
210 my ($filename) = $disposition =~ / filename="?([^\"]+)"?/;
212 $part->{name} = $name;
213 $part->{filename} = $filename;
217 my $fh = File::Temp->new( UNLINK => 0 );
220 $part->{tempname} = $fh->filename;
224 if ( $part->{filename} ) {
225 $part->{fh}->write( delete $part->{data} );
228 if ( $part->{done} ) {
230 if ( $part->{filename} ) {
232 my $fh = delete $part->{fh};
235 $self->upload( $part->{name}, $part );
239 $self->param( $part->{name}, $part->{data} );