| Line 61... |
Line 61... |
| 61 |
# the default values for everything. These are overridden
|
61 |
# the default values for everything. These are overridden
|
| 62 |
# by default values in the conf file (.yaml) or by individual
|
62 |
# by default values in the conf file (.yaml) or by individual
|
| 63 |
# accounts entries.
|
63 |
# accounts entries.
|
| 64 |
my $config = {
|
64 |
my $config = {
|
| 65 |
'default' => {
|
65 |
'default' => {
|
| - |
|
66 |
# if set, most log stuff is sent to this file
|
| - |
|
67 |
'logfile' => '',
|
| - |
|
68 |
# defines the amount of crap which ends up in the logfile
|
| - |
|
69 |
'loglevel' => 4,
|
| 66 |
# where the mail is going to
|
70 |
# where the mail is going to
|
| 67 |
'target' => {
|
71 |
'target' => {
|
| 68 |
# these have no defaults. They should be in the configuration file
|
72 |
# these have no defaults. They should be in the configuration file
|
| 69 |
# 'password' => 'password',
|
73 |
# 'password' => 'password',
|
| 70 |
# 'username' => 'username',
|
74 |
# 'username' => 'username',
|
| Line 111... |
Line 115... |
| 111 |
# if 0, will not be processed
|
115 |
# if 0, will not be processed
|
| 112 |
'enabled' => 1,
|
116 |
'enabled' => 1,
|
| 113 |
}
|
117 |
}
|
| 114 |
};
|
118 |
};
|
| 115 |
|
119 |
|
| - |
|
120 |
# prints to a log file, if defined. Otherwise, prints to STDOUT
|
| - |
|
121 |
sub logit {
|
| - |
|
122 |
my $priority = shift;
|
| - |
|
123 |
return unless $priority <= $config->{'default'}->{'loglevel'};
|
| - |
|
124 |
if ( $config->{'default'}->{'logfile'} ) {
|
| - |
|
125 |
open( my $logfile, '>>', $config->{'default'}->{'logfile'} ) || die "Could not write to $config->{default}->{logfile}: $!\n";
|
| - |
|
126 |
while ( my $message = shift ) {
|
| - |
|
127 |
print $logfile "$message\n";
|
| - |
|
128 |
}
|
| - |
|
129 |
close( $logfile );
|
| - |
|
130 |
} else {
|
| - |
|
131 |
print STDOUT join( "\n", @_ ) . "\n";
|
| - |
|
132 |
}
|
| - |
|
133 |
return;
|
| - |
|
134 |
} # logit
|
| - |
|
135 |
|
| 116 |
|
136 |
|
| 117 |
#
|
137 |
#
|
| 118 |
# find where the script is actually located as cfg should be there
|
138 |
# find where the script is actually located as cfg should be there
|
| 119 |
#
|
139 |
#
|
| 120 |
sub getScriptLocation {
|
140 |
sub getScriptLocation {
|
| Line 227... |
Line 247... |
| 227 |
# If folder has messages that match the criteria, move them to target
|
247 |
# If folder has messages that match the criteria, move them to target
|
| 228 |
# creates the target folder if necessary
|
248 |
# creates the target folder if necessary
|
| 229 |
#
|
249 |
#
|
| 230 |
sub processFolder {
|
250 |
sub processFolder {
|
| 231 |
my ( $source, $target, $folder, $TESTING ) = @_;
|
251 |
my ( $source, $target, $folder, $TESTING ) = @_;
|
| 232 |
#$dateBefore, $sourceDelimiter, $targetDelimiter, $deleteOnSuccess
|
- |
|
| 233 |
print "\n=== Processing $folder\n";
|
252 |
&logit( 1, '', "=== Processing $folder" );
|
| 234 |
my $sourceAccount = $source->{'connection'};
|
253 |
my $sourceAccount = $source->{'connection'};
|
| 235 |
my $targetAccount = $target->{'connection'};
|
254 |
my $targetAccount = $target->{'connection'};
|
| 236 |
my $numMessages = 0;
|
255 |
my $numMessages = 0;
|
| 237 |
$sourceAccount->expunge_mailbox( $folder ); # clean it up so we don't copy deleted messages
|
256 |
$sourceAccount->expunge_mailbox( $folder ); # clean it up so we don't copy deleted messages
|
| 238 |
$sourceAccount->select( $folder ) or die "Could not connect to folder $folder\n"; # move into the correct folder for the source
|
257 |
$sourceAccount->select( $folder ) or die "Could not connect to folder $folder\n"; # move into the correct folder for the source
|
| 239 |
my @ids = $sourceAccount->search_sent_before( $source->{'before date'} ); # use sent_before to get the sent date from message
|
258 |
my @ids = $sourceAccount->search_sent_before( $source->{'before date'} ); # use sent_before to get the sent date from message
|
| 240 |
print "Found " . scalar( @ids ) . " messages to process\n";
|
- |
|
| 241 |
return 0 unless @ids; # we have nothing to copy, so exit
|
259 |
return 0 unless @ids; # we have nothing to copy, so exit
|
| - |
|
260 |
&logit( 3, "Found " . scalar( @ids ) . " messages to process" );
|
| 242 |
# make life easier by precalculating some paths as array pointers
|
261 |
# make life easier by precalculating some paths as array pointers
|
| 243 |
my @sourceFolders = split( '\\' . $source->{'separator'}, $folder );
|
262 |
my @sourceFolders = split( '\\' . $source->{'separator'}, $folder );
|
| 244 |
my @pattern = split '\\^', $target->{'hierarchy'};
|
263 |
my @pattern = split '\\^', $target->{'hierarchy'};
|
| 245 |
$source->{'source folder list'} = \@sourceFolders;
|
264 |
$source->{'source folder list'} = \@sourceFolders;
|
| 246 |
$target->{'hierachy pattern'} = \@pattern;
|
265 |
$target->{'hierachy pattern'} = \@pattern;
|
| Line 248... |
Line 267... |
| 248 |
# process each message to be done
|
267 |
# process each message to be done
|
| 249 |
while ( my $id = shift ( @ids ) ) {
|
268 |
while ( my $id = shift ( @ids ) ) {
|
| 250 |
# get the flags
|
269 |
# get the flags
|
| 251 |
my @flags = $sourceAccount->msg_flags( $id );
|
270 |
my @flags = $sourceAccount->msg_flags( $id );
|
| 252 |
# get the message
|
271 |
# get the message
|
| 253 |
my $message = $sourceAccount->get( $id ) or die $sourceAccount->errstr;
|
272 |
my $message = $sourceAccount->get( $id ) or die "Error getting message ID $id: $sourceAccount->errstr\n";
|
| 254 |
# calculate where we are going to move this to
|
273 |
# calculate where we are going to move this to
|
| 255 |
my $targetFolder = &calculateTargetFolder( $message, $source, $target );
|
274 |
my $targetFolder = &calculateTargetFolder( $message, $source, $target );
|
| 256 |
if ( $TESTING ) {
|
275 |
if ( $TESTING ) {
|
| 257 |
print "Would have " . ( $source->{'deleteOnSuccess'} ? 'moved' : 'copied' ) . " message to $targetFolder\n";
|
276 |
&logit( 0, "Would have " . ( $source->{'deleteOnSuccess'} ? 'moved' : 'copied' ) . " message to $targetFolder" );
|
| 258 |
next;
|
277 |
next;
|
| 259 |
}
|
278 |
}
|
| 260 |
if ( $target->{'connection'}->select( $targetFolder ) || &makeFolder( $target->{'connection'}, $targetFolder, $target->{'separator'} ) ) {
|
279 |
if ( $target->{'connection'}->select( $targetFolder ) || &makeFolder( $target->{'connection'}, $targetFolder, $target->{'separator'} ) ) {
|
| 261 |
if ( $target->{'connection'}->put( $targetFolder, $message, @flags ) ) {
|
280 |
if ( $target->{'connection'}->put( $targetFolder, $message, @flags ) ) {
|
| 262 |
$source->{'connection'}->delete( $id ) if ( $source->{'deleteOnSuccess'} ) ;
|
281 |
$source->{'connection'}->delete( $id ) if ( $source->{'deleteOnSuccess'} ) ;
|
| Line 299... |
Line 318... |
| 299 |
# fully qualified path with the correct delimiters
|
318 |
# fully qualified path with the correct delimiters
|
| 300 |
#
|
319 |
#
|
| 301 |
sub makeFolder {
|
320 |
sub makeFolder {
|
| 302 |
my ($imap, $folder, $delimiter) = @_;
|
321 |
my ($imap, $folder, $delimiter) = @_;
|
| 303 |
|
322 |
|
| 304 |
#print "\n\t\tCreating folder $folder";
|
323 |
&logit( 3, "\n\t\tCreating folder $folder" );
|
| 305 |
# you must create the parent folder before creating the children
|
324 |
# you must create the parent folder before creating the children
|
| 306 |
my $escapedDelimiter = '\\' . $delimiter;
|
325 |
my $escapedDelimiter = '\\' . $delimiter;
|
| 307 |
my @folders = split( $escapedDelimiter, $folder );
|
326 |
my @folders = split( $escapedDelimiter, $folder );
|
| 308 |
$folder = '';
|
327 |
$folder = '';
|
| 309 |
# take them from the left and, if they don't exist, create it
|
328 |
# take them from the left and, if they don't exist, create it
|
| 310 |
while ( my $subdir = shift @folders ) {
|
329 |
while ( my $subdir = shift @folders ) {
|
| 311 |
$folder .= $delimiter if $folder;
|
330 |
$folder .= $delimiter if $folder;
|
| 312 |
$folder .= $subdir;
|
331 |
$folder .= $subdir;
|
| 313 |
next if $imap->select( $folder ); # already created, so look deeper in hierachy
|
332 |
next if $imap->select( $folder ); # already created, so look deeper in hierachy
|
| 314 |
print "\n\t\t\tCreating subfolder $folder";
|
333 |
&logit( 1, "\n\t\t\tCreating subfolder $folder" );
|
| 315 |
$imap->create_mailbox( $folder ) || warn $imap->errstr();
|
334 |
$imap->create_mailbox( $folder ) || warn $imap->errstr();
|
| 316 |
$imap->folder_subscribe( $folder ) || die $imap->errstr();
|
335 |
$imap->folder_subscribe( $folder ) || die $imap->errstr();
|
| 317 |
unless ( $imap->select( $folder ) ) { # verify it was created
|
336 |
unless ( $imap->select( $folder ) ) { # verify it was created
|
| 318 |
warn "Unable to create $folder on target account\n";
|
337 |
warn "Unable to create $folder on target account\n";
|
| 319 |
return 0;
|
338 |
return 0;
|
| Line 331... |
Line 350... |
| 331 |
my $separator = $source->{'separator'};
|
350 |
my $separator = $source->{'separator'};
|
| 332 |
return 1 if $folder eq 'INBOX'; # do NOT mess with INBOX
|
351 |
return 1 if $folder eq 'INBOX'; # do NOT mess with INBOX
|
| 333 |
return 2 if $sourceAccount->select($folder) > 0; # do not mess with it if it still has messages in it
|
352 |
return 2 if $sourceAccount->select($folder) > 0; # do not mess with it if it still has messages in it
|
| 334 |
return 3 if $sourceAccount->mailboxes( $folder . $separator . '*' ); # do not mess with it if it has subfolders
|
353 |
return 3 if $sourceAccount->mailboxes( $folder . $separator . '*' ); # do not mess with it if it has subfolders
|
| 335 |
return 4 if $source->{'system folders'}->{lc $folder}; # do not mess with system folders
|
354 |
return 4 if $source->{'system folders'}->{lc $folder}; # do not mess with system folders
|
| 336 |
print "\n\t\tDeleting empty folder $folder" . ( $TESTING ? ' Dry Run' : '' );
|
355 |
&logit( 1, "\n\t\tDeleting empty folder $folder" . ( $TESTING ? ' Dry Run' : '' ) );
|
| 337 |
return if $TESTING;
|
356 |
return 0 if $TESTING;
|
| - |
|
357 |
# select something other than the folder to be deleted
|
| - |
|
358 |
$sourceAccount->select( 'INBOX' );
|
| 338 |
$sourceAccount->folder_unsubscribe($folder);
|
359 |
if ( $sourceAccount->folder_unsubscribe($folder) ) {
|
| 339 |
$sourceAccount->delete_mailbox( $folder );
|
360 |
if ( $sourceAccount->delete_mailbox( $folder ) ) {
|
| - |
|
361 |
return 0;
|
| - |
|
362 |
} else {
|
| - |
|
363 |
warn "Error trying to delete mailbox $folder: " . $sourceAccount->errstr . "\n";
|
| - |
|
364 |
}
|
| - |
|
365 |
} else {
|
| - |
|
366 |
warn "Error trying to unsubscribe from $folder: " . $sourceAccount->errstr . "\n";
|
| - |
|
367 |
}
|
| - |
|
368 |
return 0;
|
| 340 |
}
|
369 |
}
|
| 341 |
|
370 |
|
| 342 |
|
371 |
|
| 343 |
# main process loop to handle one account
|
372 |
# main process loop to handle one account
|
| 344 |
#
|
373 |
#
|
| Line 346... |
Line 375... |
| 346 |
my $account = shift;
|
375 |
my $account = shift;
|
| 347 |
|
376 |
|
| 348 |
return 0 unless $account->{'enabled'}; # blow it off if it is not enabled
|
377 |
return 0 unless $account->{'enabled'}; # blow it off if it is not enabled
|
| 349 |
my $TESTING = $account->{'testing'}; # create mini global if we should test this account
|
378 |
my $TESTING = $account->{'testing'}; # create mini global if we should test this account
|
| 350 |
|
379 |
|
| 351 |
print "========= Test Mode ========\n" if $TESTING;
|
380 |
&logit( 0, "========= Test Mode ========\n" ) if $TESTING;
|
| 352 |
|
381 |
|
| 353 |
# open and log into both source and target, and get the separator used
|
382 |
# open and log into both source and target, and get the separator used
|
| 354 |
foreach my $acct ( 'target','source' ) {
|
383 |
foreach my $acct ( 'target','source' ) {
|
| 355 |
$account->{$acct}->{'connection'} = &openIMAPConnection( $account->{$acct}->{'server'}, $account->{$acct}->{'username'}, $account->{$acct}->{'password'} );
|
384 |
$account->{$acct}->{'connection'} = &openIMAPConnection( $account->{$acct}->{'server'}, $account->{$acct}->{'username'}, $account->{$acct}->{'password'} );
|
| 356 |
unless ( $account->{$acct}->{'connection'} ) {
|
385 |
unless ( $account->{$acct}->{'connection'} ) {
|
| Line 366... |
Line 395... |
| 366 |
|
395 |
|
| 367 |
my %temp = map{ lc($_) => 1 } @{$source->{'system'}};
|
396 |
my %temp = map{ lc($_) => 1 } @{$source->{'system'}};
|
| 368 |
$source->{'system folders'} = \%temp;
|
397 |
$source->{'system folders'} = \%temp;
|
| 369 |
|
398 |
|
| 370 |
$source->{'before date'} = &getDate( $source->{'age'} );
|
399 |
$source->{'before date'} = &getDate( $source->{'age'} );
|
| 371 |
print "\t" . ( $source->{'deleteOnSuccess'} ? 'Moving' : 'Copying' ) . " all messages before $source->{'before date'}\n";
|
400 |
&logit( 1, "\t" . ( $source->{'deleteOnSuccess'} ? 'Moving' : 'Copying' ) . " all messages before $source->{'before date'}" );
|
| 372 |
|
401 |
|
| 373 |
# get a list of all folders to be processed on the source
|
402 |
# get a list of all folders to be processed on the source
|
| 374 |
$source->{'folders'} = &getFolders( $source->{'connection'}, $source->{'ignore'}, $source->{'separator'} );
|
403 |
$source->{'folders'} = &getFolders( $source->{'connection'}, $source->{'ignore'}, $source->{'separator'} );
|
| 375 |
|
404 |
|
| 376 |
if ( $TESTING ) {
|
405 |
if ( $TESTING ) {
|
| Line 387... |
Line 416... |
| 387 |
my $messages;
|
416 |
my $messages;
|
| 388 |
$messages = &processFolder( $source, $target, $folder, $TESTING ); #, $date, $$source{'separator'}, $$target{'separator'}, $deleteOnSuccess );
|
417 |
$messages = &processFolder( $source, $target, $folder, $TESTING ); #, $date, $$source{'separator'}, $$target{'separator'}, $deleteOnSuccess );
|
| 389 |
|
418 |
|
| 390 |
$TESTING ? print "Would expunge $folder\n" : $source->{'connection'}->expunge_mailbox( $folder );
|
419 |
$TESTING ? print "Would expunge $folder\n" : $source->{'connection'}->expunge_mailbox( $folder );
|
| 391 |
# delete folder if empty and client has requested it.
|
420 |
# delete folder if empty and client has requested it.
|
| 392 |
&deleteAFolder( $source, $folder, $TESTING ) if $account->{'source'}->{'deleteEmptyFolders'};
|
421 |
if ( $account->{'source'}->{'deleteEmptyFolders'} ) {
|
| - |
|
422 |
my $returnCode = &deleteAFolder( $source, $folder, $TESTING );
|
| - |
|
423 |
&logit( 3, "Return code from deleteAFolder is $returnCode" );
|
| - |
|
424 |
}
|
| 393 |
print "\n\t\t$messages processed\n";
|
425 |
&logit( 1, "$messages processed" );
|
| 394 |
$count += $messages;
|
426 |
$count += $messages;
|
| 395 |
$processedCount++;
|
427 |
$processedCount++;
|
| 396 |
# next line used only for testing. Dies after 5 folders on first account
|
428 |
# next line used only for testing. Dies after 5 folders on first account
|
| 397 |
last if $processedCount > 5 and $TESTING;
|
429 |
last if $processedCount > 5 and $TESTING;
|
| 398 |
}
|
430 |
}
|
| Line 408... |
Line 440... |
| 408 |
# Main #
|
440 |
# Main #
|
| 409 |
#######################################################################
|
441 |
#######################################################################
|
| 410 |
|
442 |
|
| 411 |
# read and evaluate configuration file
|
443 |
# read and evaluate configuration file
|
| 412 |
&readConfig() || die "could not load config file\n";
|
444 |
&readConfig() || die "could not load config file\n";
|
| - |
|
445 |
|
| 413 |
#print Dumper( $config ); die;
|
446 |
#print Dumper( $config ); die;
|
| 414 |
foreach my $account ( keys %{$config->{'accounts'}} ) {
|
447 |
foreach my $account ( keys %{$config->{'accounts'}} ) {
|
| 415 |
$config->{'accounts'}->{$account} = &fixupAccount( $config->{'default'}, $config->{'accounts'}->{$account} );
|
448 |
$config->{'accounts'}->{$account} = &fixupAccount( $config->{'default'}, $config->{'accounts'}->{$account} );
|
| 416 |
}
|
449 |
}
|
| 417 |
|
450 |
|
| Line 428... |
Line 461... |
| 428 |
#die Dumper( $accounts );
|
461 |
#die Dumper( $accounts );
|
| 429 |
|
462 |
|
| 430 |
# now, process each in turn
|
463 |
# now, process each in turn
|
| 431 |
foreach my $account ( keys %$accounts ) {
|
464 |
foreach my $account ( keys %$accounts ) {
|
| 432 |
# talk to user
|
465 |
# talk to user
|
| 433 |
print "Processing account $account\n";
|
466 |
&logit( 1, "Processing account $account" );
|
| 434 |
# do the account. This is the main worker bee
|
467 |
# do the account. This is the main worker bee
|
| 435 |
$accounts->{$account}->{'processed'} = &processAccount( $accounts->{$account} );
|
468 |
$accounts->{$account}->{'processed'} = &processAccount( $accounts->{$account} );
|
| 436 |
print "Done, $accounts->{$account}->{'processed'} messages copied\n";
|
469 |
&logit( 1, "Done, $accounts->{$account}->{'processed'} messages copied" );
|
| 437 |
$processed{'Accounts'}++;
|
470 |
$processed{'Accounts'}++;
|
| 438 |
$processed{'Messages'} += $accounts->{$account}->{'processed'};
|
471 |
$processed{'Messages'} += $accounts->{$account}->{'processed'};
|
| 439 |
# free up space we allocated since we stored a bunch of stuff in there, and we don't need it anymore
|
472 |
# free up space we allocated since we stored a bunch of stuff in there, and we don't need it anymore
|
| 440 |
$accounts->{$account} = undef;
|
473 |
$accounts->{$account} = undef;
|
| 441 |
} # foreach loop
|
474 |
} # foreach loop
|
| 442 |
|
475 |
|
| 443 |
print "$processed{'Accounts'} accounts processed, $processed{'Messages'} messages\n";
|
476 |
&logit( 1, "$processed{Accounts} accounts processed, $processed{Messages} messages" );
|
| 444 |
|
477 |
|
| 445 |
|
478 |
|
| 446 |
1;
|
479 |
1;
|
| 447 |
|
480 |
|