Sending mail and attachments from Perl using NET::SMTP

perlnetsmtp

Sending a mail attachment using NET::SMTP is pretty easy, the only really hard bit is making sure the module is installed on your server and that of course you can run Perl. I’ve written this primarily from doing this from a server but you could ust as easily use this on websites etc…

Basically to send an attachment we need to read the file into a string/ array in Perl which in turn MIME will then associate the content of this string to a reserved file to be attached. We also use MIME to send mutipart emails, so we’ll be using boundaries to separate each part, the headers, the message and the attachment. The code is below:

#!/usr/bin/perl
use Net::SMTP;
use strict;
use warnings;

my $from = 'you@yourserver.co.uk';
my $to = 'me@luckylarry.co.uk';
my $to2 = 'someoneelse@luckylarry.co.uk';
my $attachFile = 'myFile.csv';
my $boundary = 'frontier';
my $data_file="/path/to/file/myFile.csv";

open(DATA, $data_file) || die("Could not open the file");
  my @csv = DATA;
close(DATA);

my $smtp = Net::SMTP->new('smtp-out.yourserver.co.uk');

$smtp->mail($from);
$smtp->recipient($to,$to2, { SkipBad => 1 });
$smtp->data();
$smtp->datasend("Subject: Test mail from youn");
$smtp->datasend("MIME-Version: 1.0n");
$smtp->datasend("Content-type: multipart/mixed;ntboundary="$boundary"n");
$smtp->datasend("n");
$smtp->datasend("--$boundaryn");
$smtp->datasend("Content-type: text/plainn");
$smtp->datasend("Content-Disposition: quoted-printablen");
$smtp->datasend("nTest From Youn");
$smtp->datasend("--$boundaryn");
$smtp->datasend("Content-Type: application/text; name="$attachFile"n");
$smtp->datasend("Content-Disposition: attachment; filename="$attachFile"n");
$smtp->datasend("n");
$smtp->datasend("@csvn");
$smtp->datasend("--$boundary--n");
$smtp->dataend();
$smtp->quit;

exit;

So first up we tell our perl script the installation of where Perl should be with the shebang – #!/usr/bin/perl. Next we say we’re going to use strict and warnings which is just good sense when using Perl, it means the program will error if you haven’t referenced your variables properly or got a typo somewhere.

We then define our variables, who the mail is coming from the recipients – this can be an array instead of a variable per user. Next we the file that we want to attach and the MIME boundary, so every time the boundary variable is called this will be the separator between content types. After this we declare where the actual data file is which we’re going to then line read the csv file and write that to an array to store each line – probably not the most efficient way for larger files.

We finally declare the SMTP object which points to your mail server. We follow this with defining the data for the SMTP object.

$smtp->mail($from);
$smtp->recipient($to,$to2, { SkipBad => 1 });

Who is it coming from? Who is it going to? Skip any bad email addresses. You could use $smtp->to(); instead of recipients but I’m not sure that allows multiple mail addresses.

$smtp->data();
$smtp->datasend("Subject: Test mail from youn");
$smtp->datasend("MIME-Version: 1.0n");
$smtp->datasend("Content-type: multipart/mixed;ntboundary="$boundary"n");
$smtp->datasend("n");

Start the building of the data. First the mail subject, the MIME type and the content type – we’re stating that there will be multiple mixed content sections and each section will be split as defined by the boundary section.

$smtp->datasend("--$boundaryn");
$smtp->datasend("Content-type: text/plainn");
$smtp->datasend("Content-Disposition: quoted-printablen");
$smtp->datasend("nTest From Youn");
$smtp->datasend("--$boundaryn");

We separate that section with a boundary and then give the content type and disposition for our message in the email. Finishing with another boundary definition.

$smtp->datasend("Content-Type: application/text; name="$attachFile"n");
$smtp->datasend("Content-Disposition: attachment; filename="$attachFile"n");
$smtp->datasend("n");
$smtp->datasend("@csvn");
$smtp->datasend("--$boundary--n");

We then do the same but this time for the mail attachment giving a content type and disposition. In this section we then read back the contents of our csv file that we read into an array at the start, this content gets then associated to the reserved filename in our mail attachment content type. Finish this with another boundary.

$smtp->dataend();
$smtp->quit;

exit;

The final thing is to end the data stream/ definition for our NET::SMTP object , quit the SMTP object and then finally issue the exit command to exit our Perl script.

In this case I’m going to be calling my perl script from my bash script on the server, hence why I’m using exit as I really don’t want to keep this open!. And that’s it.

Perl documentation on NET::SMTP is here
More information for the MIME type for Email can be found here

36 Comments

  • Thanks, it helped to attach log files to my automated emails

    You made it simple to understand and use !

    http://www.ics.uci.edu/~rnahar

    • Thanks Rohit, glad it helped.

    • Larry,
      With lotus notes everything seems to be working but my message shows up as 4Kbyte empty space.
      My code follows
      $Smtp->datasend( “To: “.’mel.pama@necam.com’.”\n” );
      $Smtp->datasend( “From: “.’mel.pama@am.necel.com’.”\n” );
      $Smtp->datasend( “Subject: renewalReport_test\n” );
      $Smtp->datasend( “Mine-type: version 2.0\n”);
      $Smtp->datasend( “Content-type: multipart/mixed;\n\tboundary=\”$boundary\”\n” );
      $Smtp->datasend( “\n” );
      $Smtp->datasend( “–$boundary\n” );
      $Smtp->datasend( “Content-type: text/xhtml;”);
      $Smtp->datasend( “Content-Disposition: quoted-printable\n”);
      $Smtp->datasend( “$message\n” );
      $Smtp->datasend(“\n”);
      $Smtp->datasend( “–$boundary\n” );
      $Smtp->datasend( “Content-type: text/xhtml;”);
      $Smtp->datasend( “Content-Disposition: quoted-printable\n”);
      $Smtp->datasend( “$message\n” );
      $Smtp->datasend(“\n”);
      $Smtp->datasend( “–$boundary–\n” );
      $Smtp->datasend( “\n” );
      $Smtp->quit();

      Do you have any ideas about what might be wrong.
      Thanks,

      Mel Pama

      • Hey Mel,

        If you look in your code you’ve got the MIME-type declared as ‘Mine-type’ other than that check your mail server can handle what you’re doing – I can’t see any reason why it wouldn’t.

  • Larry,

    Thanks for the post, It worked perfectly fine for us. Just got another quick question? How can use this code to send multiple attachment.

    Thanks,
    Raj

    • Hi Raj,

      In a simple way you can duplicate the boundary part with attachments – Or create a loop to go through all attachments and create the mail – depends on how many you have of course. I did find that it was a bit buggy to get this working – I’ll post my code up tomorrow (I have a decent working version of this at work)

  • Thanks Larry!! I really appreciate your help.

    • Hi Raj,

      This should help you out, although for more than 3 attachments I’d look at doing a loop similar to what I do with the email addresses, e.g. just loop at this part:

      $smtp->datasend(“Content-Type: application/text; name=\”$attachFile1\”\n”);
      $smtp->datasend(“Content-Disposition: attachment; filename=\”$attachFile1\”\n”);
      $smtp->datasend(“\n”);
      $smtp->datasend(“@ronCsv\n”);
      $smtp->datasend(“–$boundary\n”);

      Anyway full code below, but without the loop. Hope that helps – if you get stuck then I guess I can write the loop.

      #!/usr/bin/perl
      
      use Net::SMTP;
      use strict;
      use warnings;
      
      my $from = 'DoNotReply@myEmail.com';
      my @toUK = ('ron@yourEmail.co.uk',
      'don@yourEmail.co.uk',
      'jon@yourEmail.co.uk'
      );
      
      my $attachFile1= 'ron.csv';
      my $attachFile2 = 'don.csv';
      my $attachFile3 = 'jon.csv';
      
      my $boundary = 'frontier';
      
      open(DAT, $attachFile1) || die("Could not open file!");
      my @ronCsv = ;
      close(DAT); 
      
      open(DAT, $attachFile2) || die("Could not open file!");
      my @donCsv = ;
      close(DAT);
      
      open(DAT, $attachFile3) || die("Could not open file!");
      my @jonCsv = ;
      close(DAT);
      
      my $smtp = Net::SMTP->new('smtp-out.myEmail.com');
      $smtp->mail($from);
      $smtp->recipient(@toUK, { SkipBad => 1 });
      $smtp->data();
      $smtp->datasend("Subject: Reports\n");
      $smtp->datasend("MIME-Version: 1.0\n");
      $smtp->datasend("Content-type: multipart/mixed;\n\tboundary=\"$boundary\"\n");
      $smtp->datasend("\n");
      $smtp->datasend("--$boundary\n");
      $smtp->datasend("Content-type: text/plain\n");
      $smtp->datasend("Content-Disposition: quoted-printable\n");
      $smtp->datasend("\nTodays reports are attached:\n");
      $smtp->datasend("\nHave a nice day! :)\n");
      $smtp->datasend("--$boundary\n");
      $smtp->datasend("Content-Type: application/text; name=\"$attachFile1\"\n");
      $smtp->datasend("Content-Disposition: attachment; filename=\"$attachFile1\"\n");
      $smtp->datasend("\n");
      $smtp->datasend("@ronCsv\n");
      $smtp->datasend("--$boundary\n");
      $smtp->datasend("Content-Type: application/text; name=\"$attachFile2\"\n");
      $smtp->datasend("Content-Disposition: attachment; filename=\"$attachFile2\"\n");
      $smtp->datasend("\n");
      $smtp->datasend("@donCsv\n");
      $smtp->datasend("--$boundary\n");
      $smtp->datasend("Content-Type: application/text; name=\"$attachFile3\"\n");
      $smtp->datasend("Content-Disposition: attachment; filename=\"$attachFile3\"\n");
      $smtp->datasend("\n");
      $smtp->datasend("@jonCsv\n");
      $smtp->datasend("--$boundary--\n");
      $smtp->dataend();
      $smtp->quit;
      
      exit;
      

      • i am getting below error while executing the above Perl script

        [/home/abc/cde/mnd] s-t$ test.pl
        Scalar found where operator expected at test.pl line 25, near “”Content-type: multipart/mixed;ntboundary=”$boundary”
        (Missing operator before $boundary?)
        String found where operator expected at test.pl line 25, near “$boundary”n””
        (Missing operator before “n”?)
        Scalar found where operator expected at test.pl line 32, near “”Content-Type: application/text; name=”$attachFile”
        (Missing operator before $attachFile?)
        String found where operator expected at test.pl line 32, near “$attachFile”n””
        (Missing operator before “n”?)
        Scalar found where operator expected at test.pl line 33, near “”Content-Disposition: attachment; filename=”$attachFile”
        (Missing operator before $attachFile?)
        String found where operator expected at test.pl line 33, near “$attachFile”n””
        (Missing operator before “n”?)
        Possible unintended interpolation of @csvn in string at test.pl line 35.
        syntax error at test.pl line 25, near “”Content-type: multipart/mixed;ntboundary=”$boundary”
        Global symbol “$boundaryn” requires explicit package name at test.pl line 27.
        Global symbol “$boundaryn” requires explicit package name at test.pl line 31.
        syntax error at test.pl line 32, near “”Content-Type: application/text; name=”$attachFile”
        syntax error at test.pl line 33, near “”Content-Disposition: attachment; filename=”$attachFile”
        Global symbol “@csvn” requires explicit package name at test.pl line 35.
        Bareword “DATA” not allowed while “strict subs” in use at test.pl line 15.
        Execution of test.pl aborted due to compilation errors.

  • Thanks Larry!! It worked out perfect for us.

    • cool 🙂

      Always glad when people can use what I’ve found out or find the same issues.

  • Hi Larry,
    I am new to perl coding, I get this error
    “Can’t call method “mail” on an undefined value at line 18″
    Could you please help

    Thanks

    • Hi Kumar,

      Make sure you’ve got NET::SMTP module installed for your Perl setup – Mail is a function of this module.

      Not sure what system your using (unix etc..) but if you look online for some instructions for installing NET::SMTP that should help you out.

  • Hi Larry,
    I am very new to perl scritp.I have tried your script to send an attachment in windows, Rather sending original file, It sends txt file with content “DATA”. I have no clue where I am making mistake. could you please help.

    use Net::SMTP;
    use strict;
    use warnings;

    my $from = “me\@domain.com”;
    my $to = “you\@domain.com”;

    my $attachFile = “ci.txt”;
    my $boundary = ‘frontier’;
    my $data_file=”c:\\ci.txt”;

    open(DATA, $data_file) || die(“Could not open the file”);
    my @txt = DATA;
    close(DATA);

    my $smtp = Net::SMTP->new(‘mailserver.domain.com’);
    $smtp->mail(‘me@domain.com’);
    $smtp->to(‘you@domain.com’);
    $smtp->data();
    $smtp->datasend(“To: you\@domain.com\n”);
    $smtp->datasend(“From: me\@domain.com\n”);

    #$smtp->mail($from);
    #$smtp->recipient($to { SkipBad => 1 });
    #$smtp->data();
    $smtp->datasend(“Subject: Test Report\n”);
    $smtp->datasend(“MIME-Version: 1.0\n”);
    $smtp->datasend(“Content-type: multipart/mixed;\n\tboundary=\”$boundary\”\n”);
    $smtp->datasend(“\n”);
    $smtp->datasend(“–$boundary\n”);
    $smtp->datasend(“Content-type: text/plain\n”);
    $smtp->datasend(“Content-Disposition: quoted-printable\n”);
    $smtp->datasend(“Report from Perl Script\n”);
    $smtp->datasend(“–$boundary\n”);
    $smtp->datasend(“Content-Type: application/text; name=\”$attachFile\”\n”);
    $smtp->datasend(“Content-Disposition: attachment; filename=\”$attachFile\”\n”);
    $smtp->datasend(“\n”);
    $smtp->datasend(“@txt\n”);
    $smtp->datasend(“–$boundary–\n”);
    $smtp->send;
    $smtp->dataend();
    $smtp->quit;

    exit;

    • Hi Anand,

      Try changing this line:

      open(DATA, $data_file) || die(“Could not open the file”);
      my @txt = DATA;
      close(DATA);

      to:

      open(DATA, $data_file) || die(“Could not open the file”);
      my @txt = ;
      close(DATA);

      • Hi Larry,
        Thanks for your reply. I tried changing the line but I got syntax error. I tried with and without space after “my @txt=” but no hopes.

        open(DATA, $data_file) || die(“Could not open the file”);
        my @txt=;
        close(DATA);

        Error :

        syntax error at attach.pl line 23, near “= ;”
        Execution of attach.pl aborted due to compilation errors.

        • Hi Anand,

          That line should read: my @txt=DATA;

          Thats why you have a compliation issue because you’re not assigning anything to the @txt variable. DATA or is the reference the data object that your streaming your csv etc… into.

          • Thanks Larry,
            Finally it works. I have modified the code like this.

            open (DATA, “$data_file”) || die(“Could not open the file”);
            my @txt = ;
            close DATA;

            Can we send msword file as attachment?

          • I found that the only way I could get this to work was to wrap the DATA like this <DATA>.

            For example:

            open(DATA, $data_file) || die(q{Could not open the file} . qq{\n});
            my @log = <DATA>
            close(DATA);

            Was banging my head against a wall for a long time with that.

  • open (DATA, “$data_file”) || die(“Could not open the file”);
    my @txt = ;
    close DATA;

    • Hi Larry,

      I am still having syntax error using the same code you mentioned. I don’t know why.
      Please help..

      open(DATA, “$file”) || exitErr “Could not open file : $file!”;
      my @new_arr = ;
      close DATA;

  • Hi larry,

    How about to send other type file such as *.tar.gz, is it possible if we only use NET::SNMP ?

    • That is possible, you just need to change the MIME type for the attachment

  • Pls help with this code… the csv file is opening but its empty.

    #!/usr/bin/perl -w
    use Net::SMTP;
    use warnings;

    my $from = myname.com;
    my $to = ‘myname.com’;
    my $attachFile = ‘Today_csv_file.csv’;
    my $boundary = ‘frontier’;
    my $data_file=”/home/mail/file201012.csv”;

    open(DATA, $data_file) || die(“Could not open the file”);
    my @csv = DATA ;
    close(DATA);
    my $smtp = Net::SMTP->new(‘servername.com’);
    $smtp->mail($from);
    $smtp->recipient($to, { SkipBad => 1 });
    $smtp->data();
    $smtp->datasend(“Subject: Todays file”);

    $smtp->datasend(“–$boundary\n”);
    $smtp->datasend(“Content-type: text/plainn”);
    $smtp->datasend(“Content-Disposition: quoted-printablen”);
    $smtp->datasend(“–$boundary\n”);
    $smtp->datasend(“Content-Type: application/text; name=\”$attachFile\”\n”);
    $smtp->datasend(“Content-Disposition: attachment; filename=\”$attachFile\”\n”);
    $smtp->datasend(“@csv\:n”);
    $smtp->dataend();
    $smtp->quit;
    exit;

    • if the CSV file is empty then that’ll mean that your variable used to hold the data is empty.

      Add some debug in there to check the content of $data_file and if that’s not populated with your data then there’s the issue.

  • Hi Lary,

    If I want to attach the image file not the text file. In this code,
    $smtp->datasend(“Content-Type: application/text; name=\”$attachFile\”\n”);
    $smtp->datasend(“Content-Disposition: attachment; filename=\”$attachFile\”\n”);

    How could I change this configure? Thanks

  • Hi,
    Thanks for the code for such a useful topic.

    I used this code for sending a mail with an .txt attachment, However data of the .txt file directly printed in the mail body, there is no attachment in the mail.

    I need to send the mail with attchment.

    Kindly help me.

    Thanks

  • How can I send a video using this code? I could only send txt files. I tried the mime type video/x-msvideo but didn’t work…Is it possible to use the same code as
    open (DATA, “$data_file”) || die(“Could not open the file”);
    my @txt = ;
    close DATA;

    Actually I couldn’t compile the above code and changed to
    open (DATA, “$data_file”) || die(“Could not open the file”);
    my @avi = ;
    close DATA;

    Thanks

    • I would imagine that you’d need to stream the code into the buffer to send it but I’ve not done this myself.

  • larry, thanks a ton. your code has been super helpful.
    my issue is that i am getting errors from what seems to be the data from the .xls being passed through for the attachment.

    Here is the code that i am using:
    open(DATA, $data_file) || die(“Could not open the file”);
    my @csv = ;
    close(DATA);

    sendEmail();

    sub sendEmail()
    {
    my $msgPrefix = “Attached is the full Risk Report\n\n”;
    my $msgSuffix = “\nIf you no longer need the full report daily, please let me know.”;

    my $smtp = Net::SMTP->new($smtpServer) or die “The error is: $!”;

    $smtp->mail($fromEmail);
    # $smtp->to($toEmail1);
    $smtp->to($toEmail2);
    $smtp->data();

    # $smtp->datasend(“To: $toEmail1\n”);
    $smtp->datasend(“To: $toEmail2\n”);
    $smtp->datasend(“Subject: Full Risk Report\n”);

    $smtp->datasend(“MIME-Version: 1.0\n”);

    # $smtp->datasend(“Content-type: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;ntboundary=”$boundary”n”);

    $smtp->datasend(“Content-Type: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet; name=” . $attachment);
    $smtp->datasend(“\n”);
    $smtp->datasend(“–$boundary\n”);
    $smtp->datasend(“Content-type: text/plain\n”);
    $smtp->datasend(“Content-Disposition: quoted-printable\n”);
    $smtp->datasend(“\nTest From Youn”);
    $smtp->datasend(“–$boundary\n”);
    # $smtp->datasend(“Content-Type: application/text; name=\”$attachment\”\n”);
    # $smtp->datasend(“Content-Disposition: attachment; filename=\”$attachment\”\n”);
    $smtp->datasend(“Content-Disposition: attachment; filename=” . $attachment . “\n”);
    $smtp->datasend(“\n”);
    $smtp->datasend(“@csv\n”);
    $smtp->datasend(“–$boundary–\n”);

    # $smtp->datasend(“Content-Disposition: attachment; filename=” . $attachment . “\n”);
    # $smtp->datasend(“Content-Type: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet; name=” . $attachment);

    $smtp->datasend($msgPrefix . $_[0] . $msgSuffix);
    $smtp->dataend();
    $smtp->quit;
    }
    exit;

    —————————————————————

    Here are the errors:

    Unrecognized escape \d passed through at fullRiskEmail.pl line 19
    Unrecognized escape \M passed through at fullRiskEmail.pl line 19
    unrecognized escape \R passed through at fullRiskEmail.pl line 19
    main::sendEmail() called too early to check prototype at fulRiskEmail.pl line 25
    syntax error at fullRiskEmail.pl line 21, near “= ;”
    Execution of fullRiskEmail.pl aborted due to complation errors

    *******************************

    thanks larry

  • actually after a couple of modifications (both to $data_file = $attachment, and to removing the sub routine) i am getting the following error only:

    syntax error at fullRiskEmail.pl line 29, near “= ;”
    Exectution of fullRiskEmail.pl aborted due to complilation errors

    I have also tried with @csv = DATA;
    and i get the following error:
    Barework “DATA” not allowed while “strict subs” in use at fullRiskEmail.pl line 29

    Thanks again

    • For the Bare word error, use
      @csv =

      this stipulates that the Filehandle, DATA be read into @csv.

      • strange… but i put anglebrackets each side of DATA but now cant see them on the website

        \<
        <>

    • For the Bare word error, use
      @csv = <>

      this stipulates that the Filehandle, DATA be read into @csv.

  • Hey Larry,

    Thanks for upload. I wanted to send multiple attachments instead. What modifications are needed.

    Thanks,
    Chirag

You must be logged in to post a comment.