Beefy Boxes and Bandwidth Generously Provided by pair Networks
Think about Loose Coupling
 
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 exploiting the Monastery: (10)
As of 2014-10-23 15:08 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    For retirement, I am banking on:










    Results (125 votes), past polls