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 |
|