Email-Outlook-Message
view release on metacpan or search on metacpan
lib/Email/Outlook/Message.pm view on Meta::CPAN
$VERSION = "0.921";
use Email::Simple;
use Email::MIME::Creator;
use Email::Outlook::Message::AddressInfo;
use Email::Outlook::Message::Attachment;
use Carp;
use base 'Email::Outlook::Message::Base';
our $skipheaders = {
map { uc($_) => 1 }
"MIME-Version",
"Content-Type",
"Content-Transfer-Encoding",
"X-Mailer",
"X-Msgconvert",
"X-MS-Tnef-Correlator",
"X-MS-Has-Attach"
};
our $MAP_SUBITEM_FILE = {
'1000' => "BODY_PLAIN", # Body
'1009' => "BODY_RTF", # Compressed-RTF version of body
'1013' => "BODY_HTML", # HTML Version of body
'0037' => "SUBJECT", # Subject
'0047' => "SUBMISSION_ID", # Seems to contain the date
'007D' => "HEAD", # Full headers
'0C1A' => "FROM", # From: Name
'0C1E' => "FROM_ADDR_TYPE", # From: Address type
'0C1F' => "FROM_ADDR", # From: Address
'0E04' => "TO", # To: Names
'0E03' => "CC", # Cc: Names
'1035' => "MESSAGEID", # Message-Id
'1039' => "REFERENCES", # References: Header
'1042' => "INREPLYTO", # In reply to Message-Id
'3007' => 'DATE2ND', # Creation Time
'0039' => 'DATE1ST', # Outlook sent date
'3FDE' => 'CODEPAGE', # Code page for text or html body
};
# Map codepage numbers to charset names. Codepages not listed here just get
# 'CP' prepended, so 1252 -> 'CP1252'.
our $MAP_CODEPAGE = {
20127 => 'US-ASCII',
20866 => 'KOI8-R',
28591 => 'ISO-8859-1',
65001 => 'UTF-8',
};
#
# Main body of module
#
sub new {
my $class = shift;
my $file = shift or croak "File name is required parameter";
my $verbose = shift;
my $self = $class->_empty_new;
$self->{EMBEDDED} = 0;
my $msg = OLE::Storage_Lite->new($file);
my $pps = $msg->getPpsTree(1);
$pps or croak "Parsing $file as OLE file failed";
$self->_set_verbosity($verbose);
# TODO: Use separate object as parser?
$self->_process_pps($pps);
return $self;
}
sub _empty_new {
my $class = shift;
return bless {
ADDRESSES => [], ATTACHMENTS => [], FROM_ADDR_TYPE => "",
VERBOSE => 0, EMBEDDED => 1
}, $class;
}
sub to_email_mime {
my $self = shift;
my $bodymime;
my $mime;
my @parts;
if ($self->{BODY_PLAIN}) { push(@parts, $self->_create_mime_plain_body()); }
if ($self->{BODY_HTML}) { push(@parts, $self->_create_mime_html_body()); }
if ($self->{BODY_RTF}) { push(@parts, $self->_create_mime_rtf_body()); }
if ((scalar @parts) > 1) {
for (@parts) { $self->_clean_part_header($_) };
$bodymime = Email::MIME->create(
attributes => {
content_type => "multipart/alternative",
encoding => "8bit",
},
parts => \@parts
);
} elsif ((@parts) == 1) {
$bodymime = $parts[0];
} else {
$bodymime = $self->_create_mime_plain_body();
}
if (@{$self->{ATTACHMENTS}}>0) {
$self->_clean_part_header($bodymime);
my $mult = Email::MIME->create(
attributes => {
content_type => "multipart/mixed",
encoding => "8bit",
},
parts => [$bodymime],
);
foreach my $att (@{$self->{ATTACHMENTS}}) {
$self->_SaveAttachment($mult, $att);
}
$mime = $mult;
} else {
$mime = $bodymime;
}
#$mime->header_set('Date', undef);
$self->_SetHeaderFields($mime);
$self->_copy_header_data($mime);
return $mime;
}
#
# Below are functions that walk the PPS tree. This is simply a tree walk.
# It's not really recursive (except when an attachment contains a .msg
# file), since the tree is shallow (max. 1 subdirectory deep).
#
lib/Email/Outlook/Message.pm view on Meta::CPAN
# Dirs containing Attachments
# Items with properties of the attachment (including its data)
# Dir that is itself a .msg file (if the attachment is an email).
#
sub _property_map {
return $MAP_SUBITEM_FILE;
}
#
# Process a subdirectory. This is either an address or an attachment.
#
sub _process_subdirectory {
my ($self, $pps) = @_;
$self->_extract_ole_date($pps);
my $name = $self->_get_pps_name($pps);
if ($name =~ '__recip_version1 0_ ') { # Address of one recipient
$self->_process_address($pps);
} elsif ($name =~ '__attach_version1 0_ ') { # Attachment
$self->_process_attachment($pps);
} else {
$self->_warn_about_unknown_directory($pps);
}
return;
}
#
# Process a subdirectory that contains an email address.
#
sub _process_address {
my ($self, $pps) = @_;
my $addr_info = Email::Outlook::Message::AddressInfo->new($pps,
$self->{VERBOSE});
push @{$self->{ADDRESSES}}, $addr_info;
return;
}
#
# Process a subdirectory that contains an attachment.
#
sub _process_attachment {
my ($self, $pps) = @_;
my $attachment = Email::Outlook::Message::Attachment->new($pps,
$self->{VERBOSE});
push @{$self->{ATTACHMENTS}}, $attachment;
return;
}
#
# Header length of the property stream depends on whether the Message
# object is embedded or not.
#
sub _property_stream_header_length {
my $self = shift;
return ($self->{EMBEDDED} ? 24 : 32)
}
#
# Helper functions
#
#
# Extract time stamp of this OLE item (this is in GMT)
#
sub _extract_ole_date {
my ($self, $pps) = @_;
unless (defined ($self->{OLEDATE})) {
# Make Date
my $datearr;
$datearr = $pps->{Time2nd};
$datearr = $pps->{Time1st} unless $datearr and $datearr->[0];
$self->{OLEDATE} = $self->_format_date($datearr) if $datearr and $datearr->[0];
}
return;
}
# If we didn't get the date from the original header data, we may be able
# to get it from the SUBMISSION_ID:
# It seems to have the format of a semicolon-separated list of key=value
# pairs. The key l has a value with the format:
# <SERVER>-<DATETIME>Z-<NUMBER>, where DATETIME is the date and time (gmt)
# in the format YYMMDDHHMMSS.
sub _submission_id_date {
my $self = shift;
my $submission_id = $self->{SUBMISSION_ID} or return;
$submission_id =~ m/ l=.*- (\d\d) (\d\d) (\d\d) (\d\d) (\d\d) (\d\d) Z-.* /x
or return;
my $year = $1;
$year += 100 if $year < 20;
return $self->_format_date([$6,$5,$4,$3,$2-1,$year]);
}
sub _SaveAttachment {
my ($self, $mime, $att) = @_;
my $m = $att->to_email_mime;
$self->_clean_part_header($m);
$mime->parts_add([$m]);
return;
}
# Set header fields
sub _AddHeaderField {
my ($self, $mime, $fieldname, $value) = @_;
#my $oldvalue = $mime->header($fieldname);
#return if $oldvalue;
$mime->header_set($fieldname, $value) if $value;
return;
}
sub _Address {
my ($self, $tag) = @_;
( run in 0.882 second using v1.01-cache-2.11-cpan-71847e10f99 )