Beefy Boxes and Bandwidth Generously Provided by pair Networks
Syntactic Confectionery Delight
 
PerlMonks  

Send mail with attachment using only SMTP

by Mad Ant (Novice)
on May 29, 2008 at 17:06 UTC ( #689054=perlquestion: print w/ replies, xml ) Need Help??
Mad Ant has asked for the wisdom of the Perl Monks concerning the following question:

Hello fellow Monks.

I need to write a script which checks maintenance for a program running on a distant machine. I have limited rights on this machine, so no installing various CPAN modules.

The mail needs to be sent with data in the mail body, and the same data in a CSV file that will be sent as an attachment.
I thought I had it down pat, but the mail sends, and their is an attachment, but it is almost empty, unreadable file.

Could someone please tell me where I'm going wrong ? Any help is much appreciated.

Here's the code refering to the CSV file :

#!/usr/bin/perl use strict; use DBI; use Net::SMTP; use utf8; my @addr_to_send = qw/anton.harris@beirtelecom.com/; my $from_addr = 'systemplus@joursheureux.be'; my $smtp_addr = 'smtp.skynet.be'; my $csvfile = "appelerr.csv"; ... # gets info from a database, creates a CSV file foreach my $to_send (@addr_to_send) { print "Sending mail to $to_send\n"; my $smtp = Net::SMTP->new($smtp_addr); $smtp->mail($from_addr); $smtp->to($to_send); $smtp->data(); $smtp->datasend("Subject: Appels S+ erronés"); $smtp->datasend("\n"); $smtp->datasend("MIME-Version: 1.0\n"); $smtp->datasend("Content-type: multipart/mixed; boundary=\"frontier\"\n"); $smtp->datasend("--frontier\n"); $smtp->datasend("Content-type: text/plain\n"); $smtp->datasend("Explication sur cet e-mail :\n"); $smtp->datasend("Cet e-mail contient la liste des appels effectués par + les pensionnaires et qui ne se sont pas terminés normalement.\n"); $smtp->datasend("Il peut s'agir d'un moment où l'infirmière a pu résou +dre les raisons de l'appel sans devoir appeler la chambre,\n"); $smtp->datasend("puis effectue un reset, ou que le pensionnaire, impat +ient, sonne de manière répétitive,\n"); $smtp->datasend("mais cela peut également etre un temps de réponse bea +ucoup trop long.\n\n"); if ($#badcalls < 0) { $smtp->datasend("Aucun appel erroné n'a été détecté.\n"); } else { for (sort @badcalls) { print "Bad call in line : $_ : $dbtime$_ - $dbroomid$_ - $dbevent$_\n" +; $smtp->datasend("$dbtime$_ - numèro de local : $rooms{$dbroomid$_}\n") +; } # THIS IS WHERE THE ATTACHMENT IS DONE $smtp->datasend("--frontier\n"); $smtp->datasend("Content-Disposition: attachment; filename=\"$csvfile\ +"\n"); $smtp->datasend("Content-Type: application/text; name= $csvfile "); $smtp->datasend("\n"); open CSVFH, "< $csvfile"; while (<CSVFH>) { chomp; $smtp->datasend("$_\n"); } close CSVFH; } $smtp->datasend("--frontier--\n"); $smtp->dataend(); $smtp->quit; }

NB : the $_ that appear as links to other nodes should really just be '$_' but in between brackets.

Comment on Send mail with attachment using only SMTP
Download Code
Re: Send mail with attachment using only SMTP
by almut (Canon) on May 29, 2008 at 17:52 UTC
    there is an attachment, but it is almost empty, unreadable file

    If you look at the raw mail message, does it look like you would expect?

    $smtp->datasend("Content-type: multipart/mixed; boundary=\"frontier\"\n");

    Is that line wrap after multipart/mixed; just an artifact of cut-n-pasting, or is that in your actual code like this? (hint: the Content-type header should be one line)

    ___

    (BTW, if you closed your code tags like this </code>, you wouldn't have the problem with [$_] being turned into links... :)

Re: Send mail with attachment using only SMTP
by tachyon-II (Chaplain) on May 29, 2008 at 18:43 UTC

    Email really only deals with the ASCII charset. As a result anything outside of this charset really needs to be base 64 encoded. You are already torturing the mailserver by sending non ascii chars in the body (ie e acute). It may be your CSV contains non ASCII chars and this is causing the mail server to barf.

    However, the problem is probably this:

    $smtp->datasend("Content-type: multipart/mixed; boundary=\"frontier\"\n"); # needs to be all on one line or like this: $smtp->datasend("Content-type: multipart/mixed; boundary=\"frontier\"\n");

    The whitespace before boundary indicates that is the line continuation (it is), but in your code you send it as a header line (it isn't). It does not really explain your symptoms as described, but is wrong and should fail.

    As you seem hell bent on rolling your own solution I suggest you base64 encode your CSV and send it as binary. You can just cut and paste the < 30 lines of code from MIME::Base64 encode routine to do this. BTW I would not use just "frontier" as your multipart boundary. "==frontier==", which will become "--==frontier==" would seem a far more improbable boundary.

    You can make your code a lot more readable if you use a HEREDOC.

    my $body = .... my $attach = .... # base 64 encoded data my $a_name = ..... my $email =<<EMAIL; MIME-Version: 1.0 Content-Type: multipart/mixed; boundary="==frontier==" This is a multi-part message in MIME format. --==frontier== Content-Transfer-Encoding: binary Content-Type: text/plain $body --==frontier== Content-Transfer-Encoding: base64 Content-Type: application/octet-stream; name="$a_name" $attach --==frontier==-- EMAIL $smtp->datasend($email) $smtp->dataend() $smtp->quit;
Re: Send mail with attachment using only SMTP
by Mad Ant (Novice) on May 29, 2008 at 19:11 UTC

    Found the problems.

    Replace lines :
    $smtp->datasend("--frontier\n");
    with
    $smtp->datasend("\n--frontier\n");

    Swap the 2 following lines :
    $smtp->datasend("Content-Disposition: attachment; filename=\"$csvfile\"\n");
    $smtp->datasend("Content-Type: application/text; name= $csvfile ");

    And finally, replace
    $smtp->datasend("Content-Type: application/text; name= $csvfile ");
    with
    $smtp->datasend("Content-Type: application/text; name=\"$csvfile\"\n");

    Hope this helps anyone elso who may encounter the problem.

      @ almut : the line you mentionned appeared on 2 lines when copy/pasting, it normally appears on one line. And I formot to close the <code> tags at the end. I'll get them right next time :)

      @ tachyon-II : I'm still not 100% comfortable with all the ins and outs of Perl, coming from a ASM/C/C++/Java background. So I'll have to look into the HERECDOC stuff soon. thanks for the heads-up.

        You can edit your node and have it right this time. When you do you ought add: "Update: fixed code tags" to your node so later visitors are not confused by almut's reply.


        Perl is environmentally friendly - it saves trees
      I think I made the corrections correctly. Going to give this a try in one of my programs so I thoguht I would pass it along to anyone else who stumbles across it. Thanks for the update Mad Ant!
      #!/usr/bin/perl use strict; use DBI; use Net::SMTP; use utf8; my @addr_to_send = qw/anton.harris@beirtelecom.com/; my $from_addr = 'systemplus@joursheureux.be'; my $smtp_addr = 'smtp.skynet.be'; my $csvfile = "appelerr.csv"; ... # gets info from a database, creates a CSV file foreach my $to_send (@addr_to_send) { print "Sending mail to $to_send\n"; my $smtp = Net::SMTP->new($smtp_addr); $smtp->mail($from_addr); $smtp->to($to_send); $smtp->data(); $smtp->datasend("Subject: Appels S+ erronés"); $smtp->datasend("\n"); $smtp->datasend("MIME-Version: 1.0\n"); $smtp->datasend("Content-type: multipart/mixed; boundary=\"frontier\"\n"); $smtp->datasend("\n--frontier\n"); $smtp->datasend("Content-type: text/plain\n"); $smtp->datasend("Explication sur cet e-mail :\n"); $smtp->datasend("Cet e-mail contient la liste des appels effectués par + les pensionnaires et qui ne se sont pas terminés normalement.\n"); $smtp->datasend("Il peut s'agir d'un moment où l'infirmière a pu résou +dre les raisons de l'appel sans devoir appeler la chambre,\n"); $smtp->datasend("puis effectue un reset, ou que le pensionnaire, impat +ient, sonne de manière répétitive,\n"); $smtp->datasend("mais cela peut également etre un temps de réponse bea +ucoup trop long.\n\n"); if ($#badcalls < 0) { $smtp->datasend("Aucun appel erroné n'a été détecté.\n"); } else { for (sort @badcalls) { print "Bad call in line : $_ : $dbtime$_ - $dbroomid$_ - $dbevent$_\n" +; $smtp->datasend("$dbtime$_ - numèro de local : $rooms{$dbroomid$_}\n") +; } # THIS IS WHERE THE ATTACHMENT IS DONE $smtp->datasend("--frontier\n"); $smtp->datasend("Content-Type: application/text; name=\"$csvfile\"\n") +; $smtp->datasend("Content-Disposition: attachment; filename=\"$csvfile\ +"\n"); $smtp->datasend("\n"); open CSVFH, "< $csvfile"; while (<CSVFH>) { chomp; $smtp->datasend("$_\n"); } close CSVFH; } $smtp->datasend("--frontier--\n"); $smtp->dataend(); $smtp->quit; }

      Elda Taluta; Sarks Sark; Ark Arks

Log In?
Username:
Password:

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

How do I use this? | Other CB clients
Other Users?
Others contemplating the Monastery: (14)
As of 2014-08-21 14:45 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    The best computer themed movie is:











    Results (136 votes), past polls