Perl read a file and send e-mail - perl

I have a log file of below format
D12345 joe#gmail.com
C67890 mary#gmail.com
B45678 don#gmail.com
A12309 joe#gmail.com
F45670 mary#gmail.com
F45470 joe#gmail.com
Currently I'm able to send e-mail to everyone with below e-mail body content
your Product id: $product_id
($product_id is D12345, A12309, F45470 , C67890, F45670)
Problem is 'N' e-mails are sent to an user if their e-mail id is present N times in the log file.
Example :
2 e-mails are sent to mary#gmail.com and 3 e-mails are sent to joe#gmail.com
I want to send only one e-mail to the users but having all their product ids, if single e-mail id occurs multiple times in the output file, like
your Product ids: C67890, F45670 to mary#gmail.com
and
your Product ids: D12345, A12309, F45470 to joe#gmail
My current code snippet
open my $fh, '<', "output.txt" or die "could not open file: $!";
while (my $line = <$fh>) {
my ($product_id, $to_email) = $line =~ /(\w\d+)\t(\S+)/;
my $MailFrom = 'myemail#domain.com';
my $subject = "PRODUCT ID DETAILS";
my $message = "your Product ids: $product_id";
%mail = (
To => $to_email,
From => $MailFrom,
Subject => $subject,
Message => $message
);
$mail{Smtp} = 'mail.myorg.com';
sendmail(%mail)or die $Mail::Sendmail::error;
}
close $fh;
Suggestions are really appreciated. Thanks in advance.

You must aggregate the information from the log file into a hash before sending the mails
Here's some example code which dumps the %mail hash instead of calling sendmail so that you can see the results
use strict;
use warnings 'all';
use Data::Dump;
my %users;
{
open my $fh, '<', "output.txt" or die "could not open file: $!";
while ( <$fh> ) {
my ($prod, $user) = split;
push #{ $users{$user} }, $prod;
}
}
# Convert arrays of product codes into comma-separated strings
#
$_ = join ', ', #$_ for values %users;
while ( my ($user, $prods) = each %users ) {
my %mail = (
To => $user,
From => 'myemail#domain.com',
Subject => 'PRODUCT ID DETAILS',
Message => "Your product IDs: $prods",
Smtp => 'mail.myorg.com',
);
dd \%mail
#sendmail(%mail) or die $Mail::Sendmail::error;
}
output
{
From => "myemail\#domain.com",
Message => "Your product IDs: D12345, A12309, F45470",
Smtp => "mail.myorg.com",
Subject => "PRODUCT ID DETAILS",
To => "joe\#gmail.com",
}
{
From => "myemail\#domain.com",
Message => "Your product IDs: B45678",
Smtp => "mail.myorg.com",
Subject => "PRODUCT ID DETAILS",
To => "don\#gmail.com",
}
{
From => "myemail\#domain.com",
Message => "Your product IDs: C67890, F45670",
Smtp => "mail.myorg.com",
Subject => "PRODUCT ID DETAILS",
To => "mary\#gmail.com",
}

This is not a perl question, just how to do basic coding.
I would preprocess the data to fit your needs i.e. create a hash of arrays using the email id as the key and push the product into the array.
You will end up with something like:
{
joe#gmail.com: ['D12345', 'A12309', 'F45470'],
mary#gmail.com: ['C67890', 'F45670'],
don#gmail.com: ['B45678']
}
Once you've created that data structure form your log file it should be simple enough the iterate over each key and create an email with the values.

Related

How to send pdf from URL using perl and sendmail [Mail::Sendmail]

I have a legacy app that needs to change. it is written in perl and uses send::mail to send mails to users. Previously we sent links in the email message body but now they want pdf attachments instead. The PDF's are generated on another server using php.
the workflow would be
create the email body
get the pdf from another server via a URL
add the pdf as an attachment to the email
send it.
I think I can use
use LWP::Simple;
unless (defined ($content = get $URL)) {
die "could not get $URL\n";
}
to get the contents of the URL but I can't figure out how to use that var as an attachment in sendmail. current sendmail code is:
my %maildata = (To => $to,
From => 'OurSite - Billing <billing#ourSite.com>',
Organization => 'OurSite, LLC http://www.OurSite.com/',
Bcc => 'sent-billing#ourSite.com',
Subject => $subject{$message} || 'ourSite invoice',
Message => $body
);
print STDERR "notify1 now calling sendmail\n";
sendmail(%maildata) || print STDERR $Mail::Sendmail::error;
The other issue I have is I don't know how to find out if the version of sendmail I have (old freebsd system) is even capable of sending attachments ?
ok thanks for the posters who gave me some direction / the will to give it a go.
In the end I built the mime body doing the following
use LWP::Simple;
use MIME::Base64;
unless (defined ($content = get $URL)) {
die "could not get $URL\n";
}
my $pdfencoded = encode_base64($content);
my %maildata = (To => $to,
From => 'OurSite - Billing <billing#ourSite.com>',
Organization => 'OurSite, LLC http://www.OurSite.com/',
Bcc => 'sent-billing#ourSite.com',
Subject => $subject{$message} || 'ourSite invoice',
);
my $boundary = "====" . time() . "====";
$maildata{'content-type'} = "multipart/mixed; boundary=\"$boundary\"";
$maildata{'Message'} = "--".$boundary."\n"."Content-Type: text/plain\n".$body.
"\n--".$boundary."\nContent-Transfer-Encoding: base64\nContent-Type:
application/pdf; name=\"invoice.pdf\"\n".$pdfencoded."\n--".$boundary."--";
sendmail(%maildata) || print STDERR $Mail::Sendmail::error;
This gave me a hand built MIME format for the body content.
Thanks for the help !

Invalid Content-Type 'multipart/; boundary

I am using Perl and Email::MIME to access an email account via IMAP. When I try to download the attachment, I get this error:
Invalid Content-Type 'multipart/;
boundary="===============6113972194662902815=="' at
/path/to/class/Reader.pm line 99.
Here is my code:
my $env = $self->env();
my $imap = $self->imap();
my $logger = $self->logger();
my ($OUT, $out_file);
# parse attachment
my $message_string = $imap->message_string($imap_id);
Email::MIME->new($message_string)->walk_parts(sub {
my ($part) = #_;
if ($part->content_type =~ /octet\-stream/) {
($OUT, $out_file) = tempfile();
binmode $OUT;
print $OUT $part->body;
close $OUT;
$logger->info("downloaded attached report: [$out_file]");
}
});
Here is the end of the dump of the $part:
MixOLE4sTiwwLE4NCiI9IiIyMDE4LTA0LTE5IDA5OjU5OjI5LjQ5NCIiIixhZTJhNDEzMy1hYWRj
LTQ4ZjgtYWY0My1jYjdhMGEzYzQzMzIsIkFwcGxpYW5jZSBwYXJ0cywgaG91c2Vob2xkIixBcHBs
aWFuY2UgU2FsZXMsTUlETE9USElBTixWQSxWYWN1dW0gU3lzdGVtcyw4MDQ1NDg0MTgxLCAsYTEx
OGUxODIyMTAwNzJkYSxEVVJBVElPTiw0NSwyLjQ3ODQsTixOLE4sMCxODQoiPSIiMjAxOC0wNC0x
OSAwOTo1OToyOS45MSIiIiwxZTNlZGQzZi02NGM0LTQ3M2UtODk2Yy00MTI3ZTVhYzIwYWUsRklO
QU5DRXxGSU5BTkNJQUwgSU5TVElUVVRJT05TfEJBTktTLEJhbmtzLEdSRUVOU0JVUkcsUEEsRmly
c3QgRmVkZXJhbCBTYXZpbmdzICYgTG9hbiBBc3NvY2lhdGlvbiBPZiAgR3JlZW4sODQ0MjU1MTk0
MSwgLDgzNDA0NmQ2MWEyYzRkY2UsRFVSQVRJT04sNDUsNS4wLE4sTixOLDAsTg0KIj0iIjIwMTgt
MDQtMTkgMDk6NTk6MjkuOTE1IiIiLGY1YTBmOTc1LTg1MmUtNDAwNC05YTY1LWEzYzgyNDJlYTQy
NywiaW5zdXJhbmNlLCBsZW5kZXJzLCBtb3J0Z2FnZXMiLExvYW5zICYgTW9ydGdhZ2VzLE1hbmFz
cXVhbixOSixDb21tZXJjaWFsIE1vcnRnYWdlIEFzc29jaWF0ZXMgSW5jLiw3MzI0NTEzMzU0LCAs
ZGQ5YTQ4OGVmYjJiY2NlMSxEVVJBVElPTiw0NSwzLjQ0MzUsTixOLE4sMCxODQo=
--===============6113972194662902815==--
",
ct => {
attributes => {
charset => "us-ascii"
},
composite => "plain",
discrete => "text",
subtype => "plain",
type => "text"
},
encode_check => 1,
header => Email::MIME::Header,
mycrlf => "
",
parts => []
}
}
I have tried several different methods of downloading this file and I keep getting that error. Let me know if there are any questoins

Perl parse email and attachments from Outlook inbox

I'm using Mail::IMAPClient to connect to our Outlook mail server. I can get the mail just fine and print the text version of that mail to a file. But I'm having trouble using MIME::Parser to parse through the email.
I've tried giving the parser a file handle to the text file that I wrote the email to. I've tried giving the parser just the text of the email but it won't work how I'm expecting it to work. The entity parts always equals 0.
When I dump the entity skeleton I get
Content-type: text/plain
Effective-type: text/plain
Body-file: NONE
--
I can see all of the parts of the email in the file. The two PDFs that are attached are there, encoded in base64, so I know that the script is actually retrieving the email and the attachments. I've also tried parse and parse_data.
my $msgCount = 0;
$msgCount = $imap->message_count();
#or abortMission("", "Could not get message count: ". $imap->LastError );
if ( $msgCount > 0 ) {
#get all the messages from the inbox folder
my #msgseqnos = $imap->messages
or abortMission("", "Could not retreive messages:". $imap->LastError);
my ($x, $bh, $attachment, $attachmentName);
foreach my $seqno ( #msgseqnos ) {
my $input_file;
my $parser = new MIME::Parser;
my $emailText = $imap->body_string($seqno) # should be the entire email as text.
or abortMission("", "Could not get message string: " . $imap->LastError);
$parser->ignore_errors(1);
$parser->output_to_core(1);
open my $emailFileHandle, ">", "invoiceText.txt";
print $emailFileHandle $emailText;
#$imap->message_to_file($emailFileHandle, $seqno);
my $entity = $parser->parse_data($emailText);
$entity->dump_skeleton;
if ( $entity->parts > 0 ) {
for ( my $i = 0; $i < $entity->parts; $i++ ) {
my $subentity = $entity->parts($i);
# grab attachment name and contents
foreach $x ( #attypes ) {
if ( $subentity->mime_type =~ m/$x/i ) {
$bh = $subentity->bodyhandle;
$attachment = $bh->as_string;
$attachmentName = $subentity->head->mime_attr('content-disposition.filename');
open FH, ">$attachmentName";
print FH $attachment;
close FH;
#push #attachment, $attachment;
#push #attname, $subentity->head->mime_attr('content-disposition.filename');
}
}
}
}
else {
stillAGo("eData VehicleInvoices problem", "Perl can't find an attachment in an email in the VehicleInvoices folder of eData email address");
}
close $emailFileHandle;
# say $emailText;
# next;
#open OUT_FILE, ">invoiceText.txt";
#print OUT_FILE $emailText;
#print OUT_FILE $imap->bodypart_string($seqno,1);
#close OUT_FILE;
#print $emailText;
}
}
I'm trying to retrieve the attachments from emails automatically and save them to disk to be processed by another job.
I'd like to include the invoiceText.txt file so people can see the actual output but it's 1200 lines long. I'm not sure where to upload a file to link in here.
The body_string method doesn't return the entire email. As the documentation describes, and the name implies, it returns the body of the message, excluding the headers. That is why dump_skeleton shows no headers apart from the defaults
What you probably want, although I haven't tried it, is message_string, which does return the entire email
I see you've used message_to_file but commented it out. That would probably have worked if you got MIME::Parse to read from the file

How to split email addresses with ; in a qw

my #TeamRecipients = qw(
mike#domain.com
john#domain.com
ken#domain.com
rajah#domain.com
);
sub sendEmailToTeam
{
my ($title, $msg) = #_;
my $email = new Common::Email(
'Sender' => 'rajah#domain.com',
'To' => \#TeamRecipients,
'Subject' => $title,
'Message' => $msg
);
if (not $ENV{'NIGHTLY_RUN_DEBUG_MODE'})
{
$email->send();
}
}
In the code above, the domain is my company name and individual team members are receiving emails without any issue but they can't reply in the same thread because the email address are in combined as shown: "mike#domain.com john#domain.com ken#domain.com rajah#domain.com" , instead of "mike#domain.com; john#domain.com; ken#domain.com; rajah#domain.com"
I modified TeamRecipients to
my #TeamRecipients = qw(
mike#domain.com;
john#domain.com;
ken#domain.com;
rajah#domain.com;
);
but it didn't work. Team members didn't get the initial email either.
I also introduce a intermediate array like bellow
my #MyTeamRecipients = map { split(/;/, $_)} #TeamRecipients;
but it didn't work either.
Experts,
Is there any solutions?
Thanks,
Rajah
Use join:
'To' => join(';', #TeamRecipients),

error on attachment file over email sent via perl script

I'm using this script to send an email with attachment using perl
The issue I do have is that I'm sending a csv file with some formatting, the send part goes very well without any error but when I receive the email, the attachment that has NO formatting with funny characters.
screen the file that I send - http://imgur.com/Gkkz26W
screen the file that I received - http://imgur.com/UMlXp3F
Anyone understand why this is happening?
#!/usr/bin/perl
use MIME::Lite;
use Net::SMTP;
### Adjust sender, recipient and your SMTP mailhost
my $from_address = 'martin dot zahn at akadia dot ch';
my $to_address = 'martin dot zahn at akadia dot ch';
my $mail_host = 'mailhost.domain.com';
### Adjust subject and body message
my $subject = 'A message with 2 parts ...';
my $message_body = "Here's the attachment file(s) you wanted";
### Adjust the filenames
my $my_file_csv = 'my_file.csv';
my $your_file_csv = 'your_file.csv';
### Create the multipart container
$msg = MIME::Lite->new (
From => $from_address,
To => $to_address,
Subject => $subject,
Type =>'multipart/mixed'
) or die "Error creating multipart container: $!\n";
### Add the text message part
$msg->attach (
Type => 'TEXT',
Data => $message_body
) or die "Error adding the text message part: $!\n";
### Add the CSV file
$msg->attach (
Type => 'text/csv',
Path => $my_file_csv,
Filename => $your_file_csv,
Disposition => 'attachment'
) or die "Error adding $file_csv: $!\n";
### Send the Message
MIME::Lite->send('smtp', $mail_host, Timeout=>60);
$msg->send;