in reply to Re: Using Net::SMTP to send email attachments in thread Using Net::SMTP to send email attachments
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.
Re^3: Using Net::SMTP to send email attachments
by shmem (Chancellor) on Apr 30, 2017 at 15:25 UTC
|
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'
| [reply] [Watch: Dir/Any] [d/l] [select] |
|
I tried this script but I got an error message, "EOH" seems to be undefined here....?
And, is the email sent from the web server instead of being transferred to the email server? My understanding is that emails that do not originate from specific email servers are regarded as spam and so binned.
Nice if we could get this to work though...
Thanks.
| [reply] [Watch: Dir/Any] |
|
"I tried this script but I got an error message, "EOH" seems to be undefined here....?"
The second EOH needs to be on a line by itself, with no leading or trailing whitespace.
See "perlop: Quote-Like Operators: here-document" for details.
[For future reference,
please post error messages, in their entirety, between <code>...</code> tags,
so that we can see a verbatim copy:
a synoptic, prosaic rendering of the message is typically not that helpful.]
| [reply] [Watch: Dir/Any] [d/l] [select] |
|
I tried this script but I got an error message, "EOH" seems to be undefined here....?
What error message do you get?
And, is the email sent from the web server instead of being transferred to the email server?
The message is sent from the machine where the script runs. Whether it is sent directly or transferred to the mail exchanger of this machine's domain depends on your mail configuration.
perl -le'print map{pack c,($-++?1:13)+ord}split//,ESEL'
| [reply] [Watch: Dir/Any] |
|
|
Re^3: Using Net::SMTP to send email attachments
by astrobal (Acolyte) on Apr 30, 2017 at 18:51 UTC
|
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.
| [reply] [Watch: Dir/Any] |
|
The second attachment is empty, right? Remove the last boundary. It is followed by nothing, and thus produces an empty attachment. update: Instead, output the boundary with "--" (two hyphens) attached, as per afoken's advice below.
perl -le'print map{pack c,($-++?1:13)+ord}split//,ESEL'
| [reply] [Watch: Dir/Any] |
|
The second attachment is empty, right? Remove the last boundary. It is followed by nothing, and thus produces an empty attachment.
Wrong way. When avoiding perfectly working modules, one should at least read the relevant RFCs, in this case RFC 2046. It clearly states:
The boundary delimiter line following the last body part is a
distinguished delimiter that indicates that no further body parts
will follow. Such a delimiter line is identical to the previous
delimiter lines, with the addition of two more hyphens after the
boundary parameter value.
(Chapter 5.1.1. "Common Syntax", page 20)
So, the code line must be changed from
$smtp->datasend("--$boundary\n");
to
$smtp->datasend("--$boundary--\n");
Also, the boundary string should not appear anywhere else in the mail body. RFC 2046 states:
NOTE: Because boundary delimiters must not appear in the body parts
being encapsulated, a user agent must exercise care to choose a
unique boundary parameter value. The boundary parameter value in the
example above could have been the result of an algorithm designed to
produce boundary delimiters with a very low probability of already
existing in the data to be encapsulated without having to prescan the
data. Alternate algorithms might result in more "readable" boundary
delimiters for a recipient with an old user agent, but would require
more attention to the possibility that the boundary delimiter might
appear at the beginning of some line in the encapsulated part. The
simplest boundary delimiter line possible is something like "---",
with a closing boundary delimiter line of "-----".
And RFC 2045 adds:
Since the hyphen character ("-") may be represented as itself in the
Quoted-Printable encoding, care must be taken, when encapsulating a
quoted-printable encoded body inside one or more multipart entities,
to ensure that the boundary delimiter does not appear anywhere in the
encoded body. (A good strategy is to choose a boundary that includes
a character sequence such as "=_" which can never appear in a
quoted-printable body. See the definition of multipart messages in
RFC 2046.)
So, a "good" boundary string contains pseudo-random or hashed data and is not a single word.
Some boundaries found in my inbox:
Boundary string | User Agent | Comment |
----=_NextPart_000_0013_01D2C28B.E3F62DD0 | Microsoft Outlook 14.0 | Several hyphens, an equal sign, and some hashed data |
----=_Part_5917814_1894675906.1493199820931 | unknown, used by Amazon | Very similar to the above one |
------------F7B57802990546C5ABB340ED | Thunderbird 45.8.0 on Windows | Several hypens and a single hash |
B_3576043155_12903 | Microsoft-MacOutlook/14.7.3.170325 | No hyphens, decimal hash values |
b1_1513f22a95bb051912f3d082319cd009 | PHPMailer 5.2.14 | Again no hypehns, long hex hash value |
--_com.samsung.android.email_24350088718877150 | Unknown mail program running on a Samsung Android smartphone | "-_" as recommended in RFC 2045, class name of the main program, and a decimal hash |
_005_17997d6269704c37af1f86b072d23dc6pollinexchangepollindel_ | Unknown, with traces of MS Exchange | long hex hash value, plus the name of the outgoing mail server with all non-alphanumeric characters removed |
sA6u0I68pY2erg76iB=_hTGmQiLde4Zv1O | RogMailer | Looks like two concatenated base64 strings, or perhaps just randomly choosen characters. |
b1_f1e8d96630926a3eef6252dc28dfcf72 | Elaine 5.11 | Looks very similar to PHPMailer above |
Apple-Mail=_48151E9E-26D6-4966-B9BC-C015767CB661 | Apple Mail 2.3124 | Same idea as Samsungs Android app: Application name and some random hex values |
MIME::Lite constructs a boundary string like this:
# Generate a new boundary to use.
# The unsupported $VANILLA is for test purposes only.
sub gen_boundary {
return ( "_----------=_" . ( $VANILLA ? '' : int(time) . $$ ) . $B
+Count++ );
}
(Ignore $VANILLA.) What you get is a fixed string, plus a few concatenated numbers (timestamp, process ID, and a simple counter). Overall, it should be quite unique, and it is unlikely to occur elsewhere in a message. No, this is not exciting, but it works.
Alexander
--
Today I will gladly share my knowledge and experience, for there are no sweeter words than "I told you so". ;-)
| [reply] [Watch: Dir/Any] [d/l] [select] |
|
|
|
| [reply] [Watch: Dir/Any] [d/l] |
|
Here is the script I have been trying. I have tried commenting out various boundary lines, but without any improvement on the fact that the text part, "This is some text", is coming through as an attachment and the main body of the email is empty. The binary file attachment is coming through just fine though, so there is progress :-)</P
Thanks again for looking
#!/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("To: $email\n");
$smtp->datasend("From: $contact\@$company\n");
$smtp->datasend("Cc: info\@$company\n");
$smtp->datasend("Subject: Trial to see if this will come through\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-Disposition: quoted-printable\n");
$smtp->datasend("\n");
$smtp->datasend("\nThis is some text.\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>";
| [reply] [Watch: Dir/Any] [d/l] |
|
|
Re^3: Using Net::SMTP to send email attachments
by Anonymous Monk on Apr 30, 2017 at 23:07 UTC
|
| [reply] [Watch: Dir/Any] |
|
Hi there.
Believe me, I like an easy life. I would not be trying to hand-craft this email script if there was a neater, simpler way.
It has been written that there are 927 ways to send emails using Perl. MIME::Lite is a very nice example and I have used it (and still use it) very successfully. I use MIME::Lite to send the content of cgi forms to myself, at an email address which is local to the web server handling the form. The problem with 926 of these methods is that they were written 20 years ago, when spam was not the problem it is now and, email servers were happy to accept emails generated by non-local web servers. That is not the case today. What Net::SMTP does is to transfer the email to the local email server, from which it is then sent. That way the email is not then blocked by AOL and other email servers as not coming from an email server and so probably spam.
The other problem is that only MIME::Lite of the MIME variants is available on the suite of Perl modules available at my ISP. They like an easy life too, so they will not allow me to install modules of uncertain provenance - even if they do come from CPAN - that may be a potential threat to the security of their systems. I could a few years ago, but - like with emails - the world has moved on. So, I am stuck with what I have.
Thanks anyway for the suggestions...
| [reply] [Watch: Dir/Any] |
|
Hi there.
Believe me, I like an easy life. I would not be trying to hand-craft this email script if there was a neater, simpler way.
It has been written that there are 927 ways to send emails using Perl. MIME::Lite is a very nice example and I have used it (and still use it) very successfully. I use MIME::Lite to send the content of cgi forms to myself, at an email address which is local to the web server handling the form. The problem with 926 of these methods is that they were written 20 years ago, when spam was not the problem it is now and, email servers were happy to accept emails generated by non-local web servers. That is not the case today. What Net::SMTP does is to transfer the email to the local email server, from which it is then sent. That way the email is not then blocked by AOL and other email servers as not coming from an email server and so probably spam.
In the time it has taken you to write that you could have tested the "Net::SMTP" back end of "mimesender" to verify that it does/can use Net::SMTP
The other problem is that only MIME::Lite of the MIME variants is available on the suite of Perl modules available at my ISP. They like an easy life too, so they will not allow me to install modules of uncertain provenance - even if they do come from CPAN - that may be a potential threat to the security of their systems. I could a few years ago, but - like with emails - the world has moved on. So, I am stuck with what I have.
Thanks anyway for the suggestions...
In the time it has taken you to write that, you could have used "mimesender" to show you how your mime message is supposed to look exactly, no guessing
Also, in the same time, you could have installed these pure-perl modules right next to your .cgis
Yes, even you can use CPAN, A Guide to Installing Modules, Top 11 (GOOD) reasons not to use someone else's Modules, Top Seven (Bad) Reasons Not To Use Modules
| [reply] [Watch: Dir/Any] |
|
|
|
|
|