#!/usr/bin/perl # Written by Manuel Arriaga. You may do whatever you wish with this code. # Required modules: use Mail::POP3Client; use Mail::Sender; use Mail::MboxParser::Mail; use Archive::Zip qw( :ERROR_CODES :CONSTANTS); ################## # CONFIGURATION: # ################## # Account to which the zipped archive of all attachments will be sent (note: this CAN be the same account from which attachments are retrieved):: $user_email = 'joe@example.net'; # POP3 account from which we will retrieve the files: $username = 'joe'; $password = 'secret'; $server = 'pop.example.net'; # SMTP server to use when mailing the zip archive to $user_email: $smtp_host = 'smtp.example.net'; # Set the following variables to non-empty strings if you wish to send a short acknowledgement email to all those who sent you an attachment which was successfully downloaded: $ack_from = 'Joe User '; # what goes in the 'From: ' field of the acknowledgements $ack_to = 'Students'; # String which will replace the list of all ack. email recipients $ack_msg = 'I received your paper!'; # acknowledgement message $ack_subject = 'Confirming reception of paper'; # subject of confirmation email(s) ######################## # END OF CONFIGURATION # ######################## ########### # SCRIPT: # ########### ####################### # ====> SUB-ROUTINES: # ####################### sub zip() { my $zip_file = $attachments_dir . '.zip'; $zip = Archive::Zip->new(); $zip->addTree($attachments_dir); if ($zip->writeToFileNamed($zip_file) != AZ_OK) {return undef;} else {return $zip_file;} } sub email_zip() { my $sender = new Mail::Sender{smtp => $smtp_host, from => 'getattached.perl\@$smtp_host'}; $sender->MailFile({to => $user_email, subject => 'Attachments retrieved by getattached.perl', msg => "Attached to this email you will find a zip archive containing the $number_attachments files you were sent in emails with \"$identifier\" in the subject line.", file => $zip_file}); if ($sender->Error) {print "\n\nCouldn't send email: $Mail::Sender::Error. The messages have been removed from the server; all the attachments can be found in $zip_file.\n\n";} else {print "\n\nZipped archive $zip_file successfully sent!!\n\n";} return 1; } sub email_ack() { my $sender = new Mail::Sender{smtp => $smtp_host, from => $ack_from, to => \@sender_emails, fake_to => $ack_to, subject => $ack_subject}; $sender->MailMsg({msg => $ack_msg}); if ($sender->Error) {print "Couldn't send acknowledgement emails: $Mail::Sender::Error.\n\n";} else {print "Acknowledgement emails successfully sent!\n\n";} return 1; } sub move_files_to_attach_dir() { opendir TMPDIR, $tmp_dir or return undef; @orig_files = readdir TMPDIR; shift @orig_files; shift @orig_files; # remove '.' and '..' foreach $file (@orig_files) { $orig_file = $tmp_dir . '/' . $file; $dest_file = $attachments_dir . '/' . $file; while (-e $dest_file) { if (($base, $version, $ext) = ($dest_file =~ /(.+)\[(\d+)\]\.(.+)/)) { $dest_file = $base . '[' . ++$version . ']' . '.' . $ext; } else { $dest_file =~ s/(.+)\.(.+)/$1\[2\]\.$2/; } } rename $orig_file, $dest_file or return undef; } return 1; } ################### # ====> "MAIN()": # ################### # Read the identifier from the command line: die "Usage: getattached.perl identifier\n\nwhere identifier is the string to be found in email subject lines.\n\n" unless $identifier = $ARGV[0]; # Prepare the dir which will hold the attachments and after which the zip file will be named: ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time); $year += 1900; $attachments_dir = $identifier . "_$year-$mon-$mday\_$hour:$min:$sec"; mkdir $attachments_dir or die "\nCouldn't create directory $attachments_dir.\n\n"; $tmp_dir = '.getattached-temp'; # Connect to POP server: $pop = new Mail::POP3Client ( USER => $username, PASSWORD => $password, HOST => $server, PORT => 110, ); $number_emails = $pop->Count(); print "\nThe account contains $number_emails messages.\n"; # Iterate along the mail box looking for messages matching the identifier and containing attachments: $number_attachments = 0; $error = "false"; @sender_emails; for( $i = 1; $i <= $number_emails; $i++ ) { print "\nLooking at message $i: "; @header = $pop->Head($i); @body = $pop->Body($i); $mail = new Mail::MboxParser::Mail(\@header, \@body); $subject = $mail->header->{subject}; unless (lc($subject) eq lc($identifier)) { print "skipping because the subject line ($subject) doesn't match the identifier.\n"; next; } print "matches the identifier "; @attachs = $mail->store_all_attachments(path => $tmp_dir); if (! move_files_to_attach_dir()) { $error = "true"; last; } if (@attachs) { print "and has attachment(s): @attachs.\n"; $number_attachments += @attachs; push @sender_emails, $mail->from()->{email}; $pop->Delete($i); } else { print "but it doesn't have attachments.\n";} } # Check the error flag and the number of retrieved attachments: if ($error eq "true" || # An error occurred in the message loop above ($number_attachments > 0 && ! ($zip_file = zip()))) # No error happened above but attachments exist and zipping them failed { $pop->Reset(); print "\n\nAn error occurred; all messages were left on the server.\n\n"; } elsif ($number_attachments > 0) # No errors occurred, attachments exist and we now have a zip file called $zip_file: { print "\n\n$number_attachments attachments have been zipped, emailing the archive $zip_file to $user_email...\n"; email_zip(); # Even if email_zip() failed, we *remove* the messages from the server and acknowledge receipt because a handy zip file is already available to the user email_ack() if ($ack_msg && $ack_subject); } else # No attachments were downloaded { print "\n\nNo attachments were downloaded, leaving...\n\n"; } system "rm", "-rf", "$attachments_dir"; # try to clean up before leaving (we keep the zip file around in case the email gets 'lost') $pop->Close(); exit;