I have struggled with this and searched all over the net without a solution, but at the end I found a number of scripts and combined to give me a fairly bulletproof perl script that will export each email as a text file, with its attachments into its own folder with a cleaned up subject field as the foldername and text file name.
A tree structure looks then as such:
Default Directory\[View Name In Lotus Notes]\[Cleaned Subject Field]\Cleaned Subject.txt
with also all attachments as originally named within the Lotus mail.
This will handle multiple attachments per email and I have extracted a 5GB mailbox with this containing 16000 mails without a problem. This does not work on selected mails, and only export wholesale from the main mailbox. This is set to save it in the d:\archives directory (which must not exist before you start) and you will have to modify it to suit your purposes. The core of the script comes from
so please visit them if you wish to get the original script. I cannot remember where some of the other pieces come from unfortunately.
# This program extracts email messages from a
# Lotus Notes account.
#
# Email messages will be saved in directories
# named for the mail folder they're stored in.
# All of these directories will be stored
# under a new top-level directory named
# "C:\temp\mail" by default, but this can be
# overridden with the -d flag.
#
# For each email message, a subdirectory will
# be created, containing the text and attachments
# for that message. These subdirectories are
# currently named by sequential numbers instead of
# by their subject titles. That's on my TODO list
# to fix; it shouldn't be too hard.
#
# Some folders in the Notes mail database are not
# really mail folders, so I try to skip them.
# Currently I skip all folders whose names are in
# parentheses (except for Inbox), and the folders
# in the array @badlist. You can customize @badlist
# as necessary.
#
# By default, Lotus Notes will open the email
# database for the PC's default user. To access
# the email for a different user: open Notes
# and switch to another userid first; then run
# this program while Notes is still open.
#
# Original by Kyle Krom (buckaduck)
#
# Tweaked by Doug Marsh (NateTut)
#
# Added Default Directory of "My Documents\Archived-Notes2Files"
# Added 'All by Purge Date', 'Stationery' to the @badlist
# Added an Exception for (Sent)
# Remove extraneous LFs from message.txt
# Message Directories are now named for $Subject
# Multiple Messages with the same or Related Subjects (i.e. FW & RE) are
# now stored to the same folder.
# Tweaked some prints
#
use strict;
use English;
use warnings;
use vars qw($opt_d $opt_v);
use charnames ':full';
use Getopt::Std;
use Win32::OLE;
use Win32;
# Command-line options:
# -d dirname Save everything under the directory "dirname"
# -v Verbose reporting of progress
getopt("d");
# Define a directory to store the results
my $dir = 'D:\Archives';
# Define a list of "Normal" folders to skip
my @badlist = ('_Archiving', 'Archiving\\Age of Documents',
'Discussion Threads', 'Events', 'Mail By Sender', 'All by Purge Date', 'Unread\\unread', 'Stationery');
# Auto-print carriage returns
$OUTPUT_RECORD_SEPARATOR = "\n";
# Open the email database in Lotus Notes
my $notes = Win32::OLE->new('Notes.NotesSession')
or die "Can't open Lotus Notes";
my $database = $notes->GetDatabase("","");
$database->OpenMail;
# Verify the server connection
print "Connected to ", $database->{Title},
" on ", $database->{Server};
# Loop over all of the folders
foreach my $viewname (GetViews($database)) {
# Get the object for this View
print "Saving Messages in folder $viewname...";
my $view = $database->GetView($viewname);
my $path = 'D:\Archives';
chdir ($path);
# Get the first document in the folder
my $num = 1;
my $doc = $view->GetFirstDocument;
next unless $doc;
GetInfo($num, $path, $doc);
# Get the remaining documents in the folder
while ($doc = $view->GetNextDocument($doc)) {
$num++;
GetInfo($num, $path, $doc);
}
}
sub GetInfo {
my ($num, $path, $doc) = @_;
print "Processing message $num";
if ( $doc->HasEmbedded ) {
# Create a new subdirectory based on the message subject
my $Cleaned_Subject = $doc->{Subject}->[0];
$Cleaned_Subject =~s/Re\://gi;
$Cleaned_Subject =~s/Tr\://gi;
while($Cleaned_Subject =~ s/[^A-z0-9\$%'`\-\@{}~!#()&_^ ]/\-/g)
{
;
}
$Cleaned_Subject =~ s/^\s*//;
$Cleaned_Subject =~ s/\s*$//;
$Cleaned_Subject =~ s/\\//;
$Cleaned_Subject =~ s/\s+/\-/g;
my $subdir = $Cleaned_Subject;
$subdir =~ s/^RE\-//i;
$subdir =~ s/^FW\-//i;
$subdir =~ s/^Fw\-//i;
$subdir =~ s/^Tr\-//i;
$subdir =~ s/^Re\-//i;
$subdir =~ s/^\-//i;
$subdir =~ s/\-\-/\-/g;
$subdir =~ s/\-\-/\-/g;
$subdir =~ s/\-\-/\-/g;
$subdir =~ s/^\-//;
$subdir = substr($subdir, 0, 50);
$Cleaned_Subject = $subdir;
if($subdir eq '')
{
$subdir = 'No_Subject';
}
if(! -d $subdir)
{
print "Making subdirectory $subdir";
mkdir ($subdir, 0755) or warn "Can't make $path\$subdir directory: $!";
}
my $DocumentBody = $doc->GetFirstItem('Body');
foreach my $embeddedDoc ( $DocumentBody->{EmbeddedObjects} ) {
foreach my $arryele ( @{$embeddedDoc } ) {
my @array = $arryele->{Source};
ExtractAttachment($doc, "$path/$subdir", @array);
}
}
}
}
sub ExtractAttachment {
my ($doc, $path, $filename) = @_;
#Get a Windows-friendly pathname for the file
$path = "$path/$filename";
$path =~ tr/\//\\/;
#Save the attachment to a file
my $attachment = $doc->GetAttachment($filename);
if (-e "$path/$attachment") {
print "File exists!";
}
else {
# print "File does not exist.";
print "Extracting attachment $path";
eval { $attachment->ExtractFile($path) };
if($?)
{
print("Error Saving Attachment:$filename:$?:$!:$@:$^E\n");
}
}
}
sub GetViews {
my ($database) = @_;
my @views = ();
# Loop through all of the views in this database
my $array_ref = $database->{Views};
foreach my $view (@$array_ref) {
my $name = $view->{Name};
# We only want folders if it's the Inbox or Sent or a normal folder name with no parentheses
if ($name eq '($All)') # or ($name eq '($Sent)') or ($name !~ /\(.+\)/))
{
push(@views, $name); # unless (grep { $name eq $_ } @badlist);
}
}
return @views;
}
# END OF SCRIPT
#############################################################################
I have also modified the code slightly to allow me to export all attachments to a single directory by adding a sequential counter to the filename, therefore preventing overwriting of files with the same name.
It would probably be better to add the counter as a suffix rather than a prefix as it will allow for better sorting and filtering of similarly named files, but that would require parsing and concatenating of the filename before saving, and I am still struggling to understand regular expressions.
Somebody else can perhaps help out with that?
#############################################################################
# START OF FLAT EXPORT SCRIPT
use strict;
use English;
use warnings;
use vars qw($opt_d $opt_v);
use charnames ':full';
use Getopt::Std;
use Win32::OLE;
use Win32;
# Command-line options:
# -d dirname Save everything under the directory "dirname"
# -v Verbose reporting of progress
getopt("d");
# Define a directory to store the results
my $dir = 'D:\Archives';
my $Counter = 1;
# Define a list of "Normal" folders to skip
my @badlist = ('_Archiving', 'Archiving\\Age of Documents',
'Discussion Threads', 'Events', 'Mail By Sender', 'All by Purge Date', 'Unread\\unread', 'Stationery');
# Auto-print carriage returns
$OUTPUT_RECORD_SEPARATOR = "\n";
# Open the email database in Lotus Notes
my $notes = Win32::OLE->new('Notes.NotesSession')
or die "Can't open Lotus Notes";
my $database = $notes->GetDatabase("","");
$database->OpenMail;
# Verify the server connection
print "Connected to ", $database->{Title},
" on ", $database->{Server};
# Loop over all of the folders
foreach my $viewname (GetViews($database)) {
# Get the object for this View
print "Saving Messages in folder $viewname...";
my $view = $database->GetView($viewname);
my $path = 'D:\Archives';
chdir ($path);
# Get the first document in the folder
my $num = 1;
my $doc = $view->GetFirstDocument;
next unless $doc;
GetInfo($num, $path, $doc);
# Get the remaining documents in the folder
while ($doc = $view->GetNextDocument($doc)) {
$num++;
GetInfo($num, $path, $doc);
}
}
sub GetInfo {
my ($num, $path, $doc) = @_;
print "Processing message $num";
if ( $doc->HasEmbedded ) {
my $DocumentBody = $doc->GetFirstItem('Body');
foreach my $embeddedDoc ( $DocumentBody->{EmbeddedObjects} ) {
foreach my $arryele ( @{$embeddedDoc } ) {
my @array = $arryele->{Source};
ExtractAttachment($doc, "$path", @array);
}
}
}
}
sub ExtractAttachment {
my ($doc, $path, $filename) = @_;
$Counter = $Counter + 1;
#Get a Windows-friendly pathname for the file
$path = "$path/$Counter-$filename";
$path =~ tr/\//\\/;
print "Extracting attachment $path";
#Save the attachment to a file
my $attachment = $doc->GetAttachment($filename);
eval { $attachment->ExtractFile($path) };
if($?)
{
print("Error Saving Attachment:$filename:$?:$!:$@:$^E\n");
}
}
sub GetViews {
my ($database) = @_;
my @views = ();
# Loop through all of the views in this database
my $array_ref = $database->{Views};
foreach my $view (@$array_ref) {
my $name = $view->{Name};
# We only want folders if it's the Inbox or Sent or a normal folder name with no parentheses
if ($name eq '($All)') # or ($name eq '($Sent)') or ($name !~ /\(.+\)/))
{
push(@views, $name); # unless (grep { $name eq $_ } @badlist);
}
}
return @views;
}