Beefy Boxes and Bandwidth Generously Provided by pair Networks
Clear questions and runnable code
get the best and fastest answer
 
PerlMonks  

Using Net::SMTP to send email attachments

by astrobal (Acolyte)
on Apr 30, 2017 at 06:44 UTC ( #1189197=perlquestion: print w/replies, xml ) Need Help??

astrobal has asked for the wisdom of the Perl Monks concerning the following question:

I crave the wisdom of the Perl Monks

I am trying write a cgi program which sends an email to a customer with the invoice (in pdf format) attached. My previous experiences in sending emails has reduced me to using Net::SMTP as the only module that works - but I am open to enlightenment!

The script below has been cribbed from previous questions on this topic, and the attachment of a binary file came from and example answer posted back in 2008. And it works - sort of. That is, the text part of the email comes through fine, but the ascii encoded binary file is just tagged on to the text part of the email rather than appearing as an attachment with it paperclip, as it should. I give the whole script below and then what is received as an email:

Thanks for your consideration.

#!/usr/bin/perl print "Content-type: text/html\n\n"; use CGI qw(:standard); use CGI::Carp qw(warningsToBrowser fatalsToBrowser); use warnings; use Net::SMTP; use MIME::Base64; my ($buf, $picture); my $company = 'my_company.com'; my $path = "/home/sites/$company/public_html"; my $attachBinaryFile = "image.jpg"; my $boundary = 'frontier'; my $passwd = "password"; my $contact = "name"; my $email = "info\@$company"; $smtp = Net::SMTP->new("mail.$company", Timeout => 30,Debug => 0,); $smtp->datasend("AUTH LOGIN\n"); $smtp->response(); $smtp->datasend(encode_base64("$contact\@$company") ); $smtp->response(); $smtp->datasend(encode_base64("$passwd") ); $smtp->response(); $smtp->mail("$contact\@$company"); $smtp->to($email); $smtp->cc(); $smtp->data(); $smtp->datasend("From: $contact\@$company\n"); $smtp->datasend("To: $email\n"); $smtp->datasend("Cc: info\@$company\n"); $smtp->datasend("Subject: To see if this will come through\n"); $smtp->datasend("\n"); $smtp->datasend(" This is some text. "); $smtp->datasend("--$boundary\n"); $smtp->datasend("Content-Type: image/jpeg; name=\"$attachBinaryFile\"\ +n"); $smtp->datasend("Content-Transfer-Encoding: base64\n"); $smtp->datasend("Content-Disposition: attachment; filename=\"$attachBi +naryFile\"\n"); $smtp->datasend("\n"); open(DAT, "$path/$attachBinaryFile") || die("Could not open binary fil +e!"); binmode(DAT); local $/=undef; while (read(DAT, $picture, 4096)) { $buf = &encode_base64( $picture ); $smtp->datasend($buf); } close(DAT); $smtp->datasend("\n"); $smtp->datasend("--$boundary\n"); $smtp->dataend(); $smtp->quit; print "Mail sent\n"; exit; print "</body></html>";

And here is what I get at the other end.

This is some text. --frontier Content-Type: image/jpeg; name="image.jpg" Content-Transfer-Encoding: base64 Content-Disposition: attachment; filename="image.jpg" AAABAAEAICAAAAEAIACoEAAAFgAAACgAAAAgAAAAQAAAAAEAIAAAAAAAABAAABILAAASCw +AAAAAA AAAAAAD4//L/9v7+//j7///6////+v/v////8////v7///39//L/9P/l/+7/6P/3//r+// +///P// ///5///69f/7//////b////3//////n/7//r/+j/+P/k////7/z////6////+f////P/// +/1////
8<----------snip
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAA== --frontier

Replies are listed 'Best First'.
Re: Using Net::SMTP to send email attachments
by huck (Parson) on Apr 30, 2017 at 07:18 UTC

      Thanks Huck, after reading your second link to Alecjandro's 2014 posting, and slavishly putting it the multipart/mixed line and other lines that were not in my scrip, it worked. I got the text message to print out as text in the resulting email and then the binary file to be paperclipped as an attachment. I figured it was probably a Content-Type problem, but could not put my finger on it.

      Thanks very much again.

        I figured it was probably a Content-Type problem, but could not put my finger on it.

        Right. A message with attachment is Content-Type: multipart/mixed where the parts are divided by the boundary. - Instead of repeating $smtp->datasend() statements over and over as in alejandro's example, one could make use of HERE-documents. The parts begin after the boundary line followed by the sub-headers which define the type of the part and an empty line. It is good practice to announce the type of mail to non-MIME-aware clients before any part starts.

        ... use MIME::Base64; use Time::HiRes qw(gettimeofday); ... my $filename = "some.pdf"; my $pdfbody = do { local $/; open my $fh,'<', $filename or die "pdfread: $!"; <>; }; my $boundary = encode_base64( join('',gettimeofday), ''); # $smtp->datasend(<<"EOH"); From: $from To: $to Subject: $subject Content-Type: multipart/mixed; boundary="==$boundary"; MIME-Version: 1.0 Return-Path: postmaster\@your-domain.tld This is a message in MIME format. Please use a MIME capable mail client to read this message. --==$boundary Content-Type: text/plain; charset=iso-8859-1 Content-Transfer-Encoding: 8bit Hello $recipient, this is the mail body as displayed by your MIME capable mail client. blah blah The PDF file $filename is sent with this message as attachment. regards, $sender --==$boundary Content-Type: application/pdf; charset=utf-8; name="$filename" Content-Disposition: attachment; filename="$filename" Content-Transfer-Encoding: 8bit $pdfbody --==$boundary-- EOH

        This avoids repetitions and is easier to maintain.

        update: added last boundary delimiter as per afoken's comment

        perl -le'print map{pack c,($-++?1:13)+ord}split//,ESEL'

        As an update, I am finding that in different email clients, the text part of the email is also coming through as an attachment. So, from having no attachments I now have two attachments! It is probably a boundary problem, but I do not seem to be able to determine just where the problem is.

Re: Using Net::SMTP to send email attachments
by astrobal (Acolyte) on May 02, 2017 at 07:48 UTC

    Brother Monks

    This humble initiate has finally got the script to send emails with an attachment! The final script (suitably anonymised) is given below.

    I got the script to work OK on Compuserve (AOL) and on a Swiss email server with whom I have an account, but I could get nothing at all out of gmail, not even in the spam box. There was a clue, however. All emails sent to my Compuserve address are forwarded to my gmail account and there WAS a spam email which said a trial email I sent to the Compuserve email address, which did appear there and was forwarded to my gmail account, was being put into the spam box because of a suspected fake bounce. This is the cc line...

    $smtp->datasend("Cc: info\@$company\n");

    ...so, I commented out this line (see below) and tried my gmail address again, and got instant success!

    Anyhow, in the year of Our Lord 2017, I can commend this script to anyone who wants to use Net::SMTP to send an email with an attachment. This is a working script. (This is a cgi script in that it is being accessed via the web, so it has the appropriate cgi headers for that reason.)

    Many thanks to all those who made suggestions on getting this script to work, in particular Huck, but others too whose suggestions or links provided enlightenment.

    Geoffrey
    #!/usr/bin/perl print "Content-type: text/html\n\n"; use CGI qw(:standard); use CGI::Carp qw(warningsToBrowser fatalsToBrowser); use warnings; use Net::SMTP; use MIME::Base64; my ($buf, $picture); my $company = 'my_company.com'; my $path = "/home/sites/$company/public_html"; my $attachBinaryFile = "image.jpg"; my $boundary = 'frontier'; my $passwd = "password"; my $contact = "name"; my $email = "somebody\@somewhere.com"; $smtp = Net::SMTP->new("mail.$company", Timeout => 30,Debug => 0,); $smtp->datasend("AUTH LOGIN\n"); $smtp->response(); $smtp->datasend(encode_base64("$contact\@$company") ); $smtp->response(); $smtp->datasend(encode_base64("$passwd") ); $smtp->response(); $smtp->mail("$contact\@$company"); $smtp->to($email); $smtp->cc(); $smtp->data(); $smtp->datasend("To: $email\n"); $smtp->datasend("From: $contact\@$company\n"); $smtp->datasend("Subject: Try-out of email script\n"); #$smtp->datasend("Cc: info\@$company\n"); $smtp->datasend("MIME-Version: 1.0\n"); $smtp->datasend("Content-type: multipart/mixed;\n\tboundary=\"$boundar +y\"\n"); #$smtp->datasend("\n"); $smtp->datasend("--$boundary\n"); #$smtp->datasend("Content-type: text/plain; charset=\"UTF-8\"\n"); $smtp->datasend("Content-type: text/plain;\n"); $smtp->datasend("\nSome plain text here in the body of the email\n"); $smtp->datasend("\n"); $smtp->datasend("--$boundary\n"); $smtp->datasend("Content-Type: image/jpeg; name=\"$attachBinaryFile\"\ +n"); $smtp->datasend("Content-Transfer-Encoding: base64\n"); $smtp->datasend("Content-Disposition: attachment; filename=\"$attachBi +naryFile\"\n"); $smtp->datasend("\n"); open(DAT, "$path/$attachBinaryFile") || die("Could not open binary fil +e!"); binmode(DAT); local $/=undef; while (read(DAT, $picture, 4096)) { $buf = &encode_base64( $picture ); $smtp->datasend($buf); } close(DAT); $smtp->datasend("\n"); $smtp->datasend("--$boundary\n"); $smtp->dataend(); $smtp->quit; print "Mail sent\n"; exit; print "</body></html>";
      $smtp->datasend("\n"); $smtp->datasend("--$boundary\n"); $smtp->dataend(); $smtp->quit;

      This is still wrong, and might work only accidentally. The last boundary must be followed by --. See Re^5: Using Net::SMTP to send email attachments.

      Alexander

      --
      Today I will gladly share my knowledge and experience, for there are no sweeter words than "I told you so". ;-)
Re: Using Net::SMTP to send email attachments
by Anonymous Monk on Apr 30, 2017 at 06:50 UTC

Log In?
Username:
Password:

What's my password?
Create A New User
Node Status?
node history
Node Type: perlquestion [id://1189197]
Approved by Corion
help
Chatterbox?
and the web crawler heard nothing...

How do I use this? | Other CB clients
Other Users?
Others contemplating the Monastery: (6)
As of 2021-03-01 17:19 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?
    My favorite kind of desktop background is:











    Results (15 votes). Check out past polls.

    Notices?