my $p= MIME::Parser->new; $p->output_to_core(1); $p->parse($msg_or_fh); #### # Find every MIME part which is not a container for other parts sub _leaf_parts { my @parts= $_->parts; @parts? ( map { _leaf_parts() } @parts ) : ( $_ ) } my @leaf_parts= map { _leaf_parts() } $email; #### # Open a handle to each part which is an attachment my @attachments= map +{ name => $_->head->recommended_filename, content_type => _decoded_mime_header($_->head, 'Content-Type'), handle => $_->bodyhandle->open('r'), mimepart => $_, email => $email }, grep length($_->head->recommended_filename//''), @leaf_parts; # Convert zipfile attachments to the list of files within @attachments= map { $_->{name} =~ /\.zip$/? _extract_zipfile($_) : ($_) } @attachments; #### # Takes one file info, and returns a list of file infos for each file within the zip file. # Since these are not directly MIME parts, they are simply: # { # name => $original_filename, # handle => $io_handle # } sub _extract_zipfile { my ($file)= @_; my @files; my $zipfile= IO::Uncompress::Unzip->new($file->{handle}) or die "Can't open zip file: $UnzipError"; my $status; for ($status= 1; $status > 0; $status= $zipfile->nextStream()) { my $name= $zipfile->getHeaderInfo->{Name}; $log->info("Extracting $name from zip file"); my $tmp= File::Temp->new(TEMPLATE => 'email-zip-content-XXXXXXX'); my $buf; while (($status= $zipfile->read($buf)) > 0) { $tmp->print($buf) or die; } last if $status < 0; push @files, { name => $name, handle => $tmp }; } die "Error processing zip file" if $status < 0; return @files; }