package Email::MIME; use base 'Email::Simple'; use Email::MIME::ContentType; use Email::MIME::Encodings; require 5.006; use strict; use Carp; use warnings; our $VERSION = '1.7'; sub new { my $self = shift->SUPER::new(@_); $self->{ct} = parse_content_type($self->content_type); $self->parts; return $self; } sub as_string { my $self = shift; return $self->_headers_as_string.($self->{mycrlf} || "\n" # sop. ).$self->body_raw; } sub parts { my $self = shift; if (!$self->{parts}) { $self->fill_parts } return @{$self->{parts}} } sub fill_parts { my $self = shift; if ($self->{ct}{discrete} eq "multipart" or $self->{ct}{discrete} eq "message") { $self->parts_multipart; } else { $self->parts_single_part; } return $self; } sub body { my $self = shift; my $body = $self->{body}; my $cte = $self->header("Content-Transfer-Encoding"); return $body unless $cte; if (!$self->force_decode_hook and $cte =~ /^7bit|8bit|binary/i) { return $body; } $body = $self->decode_hook($body) if $self->can("decode_hook"); # For S/MIME, etc. $body = Email::MIME::Encodings::decode( $cte, $body ); return $body; } sub parts_single_part { my $self = shift; $self->{parts} = [ $self ]; return $self; } sub body_raw { return $_[0]->{body_raw} || $_[0]->SUPER::body; } sub parts_multipart { my $self = shift; my $boundary = $self->{ct}->{attributes}->{boundary} || ""; $self->{body_raw} = $self->SUPER::body; # rfc1521 7.2.1 my ($body, $epilogue) = split /^--\Q$boundary\E--\s*$/sm, $self->body_raw, 2; my @bits = split /^--\Q$boundary\E\s*$/sm, $body; delete $self->{body}; # This might be a hack $self->{body} = shift @bits if $bits[0] !~ /.*:.*/; $self->{parts} = [ map { (ref $self)->new($_) } @bits ]; return @{$self->{parts}}; } sub force_decode_hook { 0 } sub decode_hook { return $_[1] } sub content_type { shift->header("Content-type"); } sub header { my $header = shift->SUPER::header(@_); if ($header !~ /=\?/) { return $header } if (eval { require Encode }) { Encode::decode("MIME-Header", $header); } else { require MIME::Words; MIME::Words::decode_mimewords($header); } } sub debug_structure { my ($self, $level) = @_; $level ||= 0; my $rv = " "x(5*$level); $rv .= "+ ".$self->content_type."\n"; my @parts = $self->parts; if (@parts > 1) { $rv .= $_->debug_structure($level +1) for @parts; } return $rv; } my $gname = 0; my %gcache; sub filename { my ($self, $force) = @_; return $gcache{$self} if exists $gcache{$self}; my $dis = $self->header("Content-Disposition"); my $attrs = $dis =~ s/^.*?;// ? Email::MIME::ContentType::_parse_attributes($dis) : {}; my $name = $attrs->{filename} || $self->{ct}{attributes}{name}; return $name if $name or !$force; # Make shit up! require MIME::Types; my $type = MIME::Types->new->type($self->{ct}->{discrete} ."/". $self->{ct}->{composite}); my $ext = $type && (($type->extensions)[0]); $ext ||= "dat"; return $gcache{$self} = "attachment-$$-".$gname++.".$ext"; } 1; __END__ # Below is stub documentation for your module. You better edit it! =head1 NAME Email::MIME - Easy MIME message parsing. =head1 SYNOPSIS use Email::MIME; my $parsed = Email::MIME->new($message); my Email::MIME @parts = $parsed->parts; my $decoded = $parsed->body; my $non_decoded = $parsed->body_raw; my $content_type = $parsed->content_type; =head1 DESCRIPTION This is an extension of the L module, to handle MIME encoded messages. It takes a message as a string, splits it up into its constituent parts, and allows you access to various parts of the message. Headers are decoded from MIME encoding. =head1 NOTE This is an alpha release, designed to stimulate discussion on the API, which may change in future releases. Please send me comments about any features you think C should have. Note that I expect most things to be driven by subclassing and mix-ins. =head1 METHODS Please see L for the base set of methods. It won't take very long. Added to that, you have: =head2 parts This returns a set of C objects reflecting the parts of the message. If it's a single-part message, you get the original object back. =head2 body This decodes and returns the body of the object. For top-level objects in multi-part messages, this is highly likely to be something like "This is a multi-part message in MIME format." =head2 body_raw This returns the body of the object, but doesn't decode the transfer encoding. =head2 content_type This is a shortcut for access to the content type header. =head2 filename This provides the suggested filename for the attachment part. Normally it will return the filename from the headers, but if C is passed a true parameter, it will generate an appropriate "stable" filename if one is not found in the MIME headers. =head1 AUTHOR Simon Cozens, C You may distribute this module under the terms of the Artistic or GPL licenses. =head1 THANKS This module was generously sponsored by Best Practical (http://www.bestpractical.com/) and Pete Sergeant. =head1 SEE ALSO L. =cut