How to Send Emails with Perl

On February 27, 2024
12min read
Piotr Malek Technical Content Writer @ Mailtrap

If you program in Perl, you’re not short on options for sending emails from a Perl app. You can try Perl’s generic libraries and use external modules. If you don’t like any of these options, you can even build your own module, and you won’t have to spend the next week doing so.

In this article, we cover several of the best approaches. We’ll explain who they will be the most useful for and how to implement them into your project.

Let’s start!

Sending emails with Sendmail

First things first, you might have come across the Sendmail utility inside your Perl application. It’s not a bad approach, but don’t expect excellent delivery rates. In our tests, several emails not only skipped the inbox, but they didn’t even “qualify” to be marked as spam.

Sendmail is super quick and simple to set up as long as you’re using Linux/Unix machines. For Windows and other devices, you’ll need to look at alternative approaches.

To use the `sendmail` service on Ubuntu, you will typically follow these steps to install, configure, and send an email using sendmail. Note that while sendmail is a traditional mail transfer agent, many users opt for alternatives like `Postfix` due to easier configuration and maintenance.

You can check if the sendmail service is working on the system by calling this command. `systemctl status sendmail`

Make sure you configure Sendmail properly and let’s try sending a few sample emails.

Sending a Plaintext message

Here’s a sample code for sending an email with Sendmail:

#!/usr/bin/perl
use strict;
use warnings;
my $to = 'jane@mailtrap.io';
my $from = 'piotr@mailtrap.io';
my $subject = 'w00t it works!';
my $message = 'Look at me, Jane. I’m sending emails from Perl!';
 
open(MAIL, "|/usr/sbin/sendmail -t");
 
# Email Header
print MAIL "To: $to\n";
print MAIL "From: $from\n";
print MAIL "Subject: $subject\n\n";
# Email Body
print MAIL $message;


my $result = close(MAIL);

if($result) {
  print "Email Sent, Bro!\n";
} else {
  print "There was a problem, Bro!\n";
}

What just happened? We configured the address we send from ($from) and the recipient’s address ($to). We also specified the subject and the message. 

We then opened the Sendmail utility, inserted all these details, and added the message to a sending queue on your local host. If you have a mail server set up, Jane should see our miserable attempt in her inbox in a few seconds. 

If you don’t, make sure that the recipient’s email service doesn’t block emails coming from your IP address.

Sending HTML emails

The sample copy above will be fine for testing or internal error reporting. However, if you want to send emails to your users (or just want to make a better impression on Jane), you will want to use some HTML in the email body.

By default, Sendmail sends emails in plain text. You can, however, enable HTML formatting by simply adding Content-type: text/html in the header part of the email.

#!/usr/bin/perl
use strict;
use warnings;
my $to = 'jane@mailtrap.io';
my $from = 'piotr@mailtrap.io';
my $subject = 'It\'s me again';
my $message = '<p>Hey Jane!</p>
<p>Still reading my emails?</p>
<p>If so, I brought you some flowers!</p>
<p><img src="https://media.giphy.com/media/JI2HICt36nqta/giphy.gif" /></p>
<p>See you Thursday night maybe?</p>
<p>P.</p>';
 
open(MAIL, "|/usr/sbin/sendmail -t");
 
# Email Header
print MAIL "To: $to\n";
print MAIL "From: $from\n";
print MAIL "Subject: $subject\n";
print MAIL "Content-type: text/html\n\n";

# Email Body
print MAIL $message;

close(MAIL);
print "Email Sent, Bro!\n";

Adding CC and BCC fields

It is no surprise that you can also add CC and BCC fields to your message headers.

#!/usr/bin/perl
use strict;
use warnings;

my $to = 'kate@mailtrap.io';
my $from = 'piotr@mailtrap.io';
my $cc = 'mary@mailtrap.io, ann@mailtrap.io, alice@mailtrap.io';
my $bcc = 'jane@mailtrap.io';
$subject = 'Hello, it\'s me';
$message = 'I was wondering if after all these years you\'d like to meet';
 
open(MAIL, "|/usr/sbin/sendmail -t");
 
# Email Header
print MAIL "To: $to\n";
print MAIL "From: $from\n";
print MAIL "Cc: $cc\n";
print MAIL "Bcc: $bcc\n";
print MAIL "Subject: $subject\n\n";

# Email Body
print MAIL $message;

close(MAIL);
print "Email Sent, Bro!\n";

Adding an attachment

It’s also possible to add an attachment to a message, but it’s a bit more complex. Note that each file will be loaded to the memory twice. So, adding large files is probably a poor idea.

Before running the code below, make sure the Mail::Sendmail module is already installed. 

use Mail::Sendmail;

%mail = (
  from => 'john@mailtrap.io',
  to => 'piotr@mailtrap.io',
  subject => 'This is the last warning'
);

$boundary = "====" . time() . "====";
$mail{'smtp'} = 'smtp_server:smth_port;
$mail{'auth'} = {user => "smtp_user", pass => "smtp_pass", method => 'auth_method'};
$mail{'content-type'} = "multipart/mixed; boundary=\"$boundary\"";

$message = encode_qp( "Piotr, you really need to stop emailing your female colleagues at work. Attached you will find the list of complaints I received." );


open (F, './complaints.txt') or die "Cannot read file!";
$mail{body} = encode_base64(<F>);
close F;


$boundary = '--'.$boundary;
$mail{body} = <<END_OF_BODY;
$boundary
Content-Type: text/plain; charset="iso-8859-1"
Content-Transfer-Encoding: quoted-printable

$message
$boundary
Content-Type: application/octet-stream; name="complaints.txt"
Content-Transfer-Encoding: base64
Content-Disposition: attachment; filename="complaints.txt"

$mail{body}
$boundary--
END_OF_BODY

sendmail(%mail) || print "Error: $Mail::Sendmail::error\n";

Sending emails with MIME::Lite

Let’s move on to different approaches because sending without Sendmail is definitely possible. Even better – alternative approaches such as MIME::Lite module offer more flexibility and are also available for non-Linux/Unix machines. You get all of this without sacrificing the simple process of sending emails.

To install, simply insert the following code:

cpan -i MIME::Lite

Alternatively, download the module and install it with the following code:

tar xvfz MIME-Lite-3.033.tar.gz
cd MIME-Lite-3.033
perl Makefile.PL
make
make install

Sending a plaintext message

As you would expect from a module with the word ‘lite’ in its name, sending plain text emails is extremely simple. Here’s an example:

#!/usr/bin/perl
use strict;
use warnings;
use MIME::Lite;
 
my $to = 'jane@mailtrap.io';
my $cc = 'john@mailtrap.io';
my $from = 'piotr@mailtrap.io';
my $subject = 'Apologies';
my $message = 'Hey, I’m really sorry for the 132 emails I sent you last quarter.';

my $msg = MIME::Lite->new(
                 From     => $from,
                 To       => $to,
                 Cc       => $cc,
                 Subject  => $subject,
                 Data     => $message
                 );
          
$msg->send;
print "Email Sent, Bro!\n";

Sending an HTML email

Since MIME:Lite uses the default Sendmail method for sending emails, an HTML email we composed earlier on will look very similar in this case too.

u#!/usr/bin/perl
use strict;
use warnings;
use MIME::Lite;
 
my $to = 'mary@mailtrap.io, ann@mailtrap.io, alice@mailtrap.io, kate@mailtrap.io';
my $cc = 'john@mailtrap.io';
my $from = 'piotr@mailtrap.io';
my $subject = 'Apologies';
my $message = '<h1>Girls, I\'m also very sorry about all the emails!</h1>
<p><img src="https://media.giphy.com/media/RWUqVYucDBD4A/giphy.gif" /></p>
';

my $msg = MIME::Lite->new(
                 From     => $from,
                 To       => $to,
                 Cc       => $cc,
                 Subject  => $subject,
                 Data     => $message
                 );
                 
$msg->attr("content-type" => "text/html");         
$msg->send;
print "Email Sent, Bro!\n";

Sending email with attachments

One of the key advantages of this module is the simplicity of adding attachments. You can include multiple files in a single email with the attach() method.

#!/usr/bin/perl
use strict;
use warnings;

use MIME::Lite;
 
my $to = 'piotr@mailtrap.io';
my $cc = 'ann@mailtrap.io, alice@mailtrap.io, kate@mailtrap.io, john@mailtrap.io';
my $from = 'mary@mailtrap.io';
my $subject = 'Re: Apologies';
my $message = '<h1>Will you stop emailing us already???</h1>';


# Add your text message.
$msg->attach(
  Type     => 'TEXT',   
  Data     => $message
);

# Specify your file as attachment.
$msg->attach(
  Type        => 'application/pdf',
  Path        => './lawsuit.pdf',
  Filename    => 'lawsuit.pdf',
  Disposition => 'attachment'
);

$msg->send;


print "Email Sent, Bro!\n";

Sending emails with MIME::Lite:TT::HTML

There’s also one variation of MIME::Lite that has gained some momentum. MIME::Lite::TT::HTML looks and works very similarly. What makes it different is the ability to use templates instead of hardcoding the content of messages.

This makes it easy to quickly update the body of an email without touching the code base. Whether this means tiny changes or a complete redesign of your emails, it’s much easier done on templates.

If you’re planning to send only simple plain text emails, you’re probably better off with the methods we discussed before. If you need more than that, read on.

Building templates

Since some email clients don’t display HTML versions of incoming emails (by design or by the user’s choice), it’s good to send both plain text and HTML versions of an email. If HTML is supported, the respective version will be displayed. If not, a plain text copy will be loaded.

notice.txt.tt

Hello [% first_name %],

This is a legal notice of an ongoing case against you, filled by [% client_name %]. I'll be in touch shortly.

Have a pleasant day (it won’t last for long)
Attorney Margaret Smith

notice.html.tt

<p>
  Hello [% first_name %],
  <br /><br />
  This is a *legal notice* of an ongoing case against you, filled by [% client_name %]. I'll be in touch shortly.
</p>
<p>
  <img src="https://media.giphy.com/media/l0HlxAqevo9mKD3UY/giphy.gif" />
  <br /><br />
  Have a pleasant day (it won’t last for long)
  <br />
  Attorney Margaret Smith
</p>

Sending a plaintext/HTML example

Don’t forget to install this module:

cpan -i MIME::Lite::TT::HTML

An example email using both templates will look as follows:

#!/usr/bin/perl

         use strict;
         use warnings;
         use MIME::Lite::TT::HTML;

         my %params;

         $params{first_name} = 'Piotr';
         $params{client_name}  = 'Jane';

         my %options;
         $options{INCLUDE_PATH} = '/path/to/templates';



         my $msg = MIME::Lite::TT::HTML->new(
                    From        =>  'margaret@awesomelawyers.com',
                    To          =>  'piotr@mailtrap.io',
                    Subject     =>  'ICYMI',
                    Template    =>  {
                                        text    =>  'notice.txt.tt',
                                        html    =>  'notice.html.tt',
                                    },
                    TmplOptions =>  \%options,
                    TmplParams  =>  \%params,
         );



$msg->send;


print "Email Sent, Bro!\n";

Adding an attachment

If you want to include a file, it’s very simple.

#!/usr/bin/perl

         use strict;
         use warnings;
         use MIME::Lite::TT::HTML;

         my %params;

         $params{first_name} = 'Piotr';
         $params{client_name}  = 'Jane';

         my %options;
         $options{INCLUDE_PATH} = '/path/to/templates';


         my $msg = MIME::Lite::TT::HTML->new(
                    From        =>  'margaret@awesomelawyers.com',
                    To          =>  'piotr@mailtrap.io',
                    Subject     =>  'ICYMI',
                    Template    =>  {
                                        text    =>  'notice.txt.tt',
                                        html    =>  'notice.html.tt',
                                    },
                    TmplOptions =>  \%options,
                    TmplParams  =>  \%params,
         );

         $msg->attr("content-type"  => "multipart/mixed");
         $msg->attach(  Type        =>  'application/pdf',
                        Path        =>  '/path/to/prison_map.pdf',
                        Filename    =>  'prison_map.pdf',
                        Disposition =>  'attachment'
         );


$msg->send;


print "Email Sent, Bro!\n";

Notice the five new lines before the send() function that we added. You should pay special attention to the content-type specified in the code. If Margaret was to send a prison plan in a .png file instead of a pdf, she should opt for image/png type.

Sending emails in Perl via SMTP 

By default, all three methods we covered use localhost for sending emails. Though localhost is an okay option for personal correspondence, it’s certainly not suitable for bulk sending. It can compromise email deliverability, which means important messages such as order confirmations or password resets may not reach recipients’ inboxes. 

And that’s where Mailtrap Sending comes in. 

Firstly, Mailtrap allows you to send with an impressive sending throughput of 10,000 emails per second.

It also provides developers with an email infrastructure with high deliverability rates and allows you to control email statistics with helicopter-view dashboards, in-depth analytics, and alerts.

Mailtrap email sending stats overview

There are also dedicated IPS, auto IP warmups, suppression lists, and other features that are designed to make sure your emails reach your recipients’ inboxes.

Additionally, with Mailtrap, you can dig deeper into email extended history.

Mailtrap email sending email logs

Here’s what you need to do to start sending emails from Perl with Mailtrap Email API/SMTP: 

  • Register and log in to your account; 
  • Click ‘Sending Domains’ to add and verify your domain. You can find detailed instructions here
  • Once the domain is verified, go to the ‘SMTP/API Settings’ tab
  • Choose the stream you’d like to use – Transactional or Bulk (Here we’ll use Transactional)
  • Then select ‘SMTP’
  • Copy your credentials and go back to your Perl project; 
  • Adjust the code in your MIME modules and provide SMTP credentials in send() method:
$msg->send('smtp', "send.smtp.mailtrap.io", AuthUser=>"your_username", AuthPass=>"your_api_token" );

If you were to use MIME::Lite, the complete code would look like this: 

#!/usr/bin/perl
use strict;
use warnings;

use MIME::Lite;
 
my $to = 'mary@mailtrap.io, ann@mailtrap.io, alice@mailtrap.io, kate@mailtrap.io';
my $cc = 'john@mailtrap.io';
my $from = 'piotr@mailtrap.io';
my $subject = 'Can’t wait to see you';
my $message = '<h1>Girls, I\'m getting out of prison soon!</h1>
<p><img src="https://media.giphy.com/media/RWUqVYucDBD4A/giphy.gif" /></p>
';

my $msg = MIME::Lite->new(
                 From     => $from,
                 To       => $to,
                 Cc       => $cc,
                 Subject  => $subject,
                 Data     => $message,
                 Encoding => ‘quoted-printable’
                 );
                 
$msg->attr("content-type" => "text/html");         
$msg->send('smtp', "sandbox.smtp.mailtrap.io", AuthUser=>"your_username", AuthPass=>"your_api_token", SSL => 1, Port => 465 );
print "Email Sent, Bro!\n";

Sending emails in Perl using API

Mailtrap also offers an email API with which you can automate your email sending.

First, register an account and verify your email sending domain name. Then, navigate to the SMTP/API Settings tab in the Sending Domains section, where you’ll find your API key/token. As a reminder – make sure you’re using the correct sending stream.

Mailtrap email API settings

After getting the token credentials and storing them, to start sending via API, all you have to do is copy the following Perl script:

```perl
use strict;
use warnings;
use HTTP::Tiny;
use JSON;

my $url = 'https://send.api.mailtrap.io/api/send';
my $data = {
    'from' => {'email' => ‘sender@domain.com'},
    'to' => [{'email' => 'receiver@domain.com'}],
    'subject' => 'Test Email',
    'text' => 'This is a test email using Perl and Mailtrap API!',
};

my $json_payload = encode_json($data);

my $http = HTTP::Tiny->new();

my $response = $http->post($url, {
    'headers' => { 'Content-Type' => 'application/json', 'Api-Token' => 'token here'},
    'content' => $json_payload
});

if ($response->{success}) {
    print "Response: " . $response->{content};
} else {
    die "HTTP POST error: " . $response->{status} . " - " . $response->{reason}; 
}
```

Here’s a step-by-step explanation of the code:

  • use HTTP::Tiny; – imports the HTTP::Tiny module, which provides a minimalist HTTP client. This is used to execute the HTTP POST request to the Mailtrap API.
  • use JSON; – imports functions to encode and decode JSON. JSON encoding is necessary because the Mailtrap API expects the request payload in JSON format.
  • The script sends the POST request using $http->post($url, {…}). The request includes:
    • A headers hash reference specifying the `Content-Type` as `application/json ` and providing the Api-Token. The Api-Token is essential for authenticating the request with the Mailtrap API.
    • The `content` key with the value of $json_payload, which is the JSON-encoded email data.

Testing emails before sending them in Perl: why and how? 

If your emails are addressed to external recipients, testing them first would be a solid idea. Of course, you could simply insert your email address into a “To” field and check to see if everything works as expected. 

However, as your application grows and you start sending different types of emails, you should probably look for a more sophisticated testing environment. 

One such place on the internet is Mailtrap Email Sandbox. It’s a safe environment to test and debug emails before sending them to inboxes. It captures all the SMTP traffic from staging, making sure that test emails don’t spam users.

Try Mailtrap Email Sandbox for Free

The Email Sandbox checks the HTML/CSS in each email and gauges them against spam filters and blacklists.

Screenshot of Mailtrap's HTML Check feature

Mailtrap also checks your spam score, which, if you keep under 5, will prevent any deliverability issues when you move your app to production. Basically, with Mailtrap, you can make sure your emails land where they’re supposed to, instead of spam folders.

Mailtrap's Spam Report feature

Once you’re happy with the results, it lets you forward the emails to the real addresses manually or automatically.

Mailtrap email testing Auto Forwarding

Mailtrap Email Sandbox is also very easy to set up with Perl. You have two options: use SMTP credentials for any Perl module or copy ready-made code configurations.

SMTP 

If you go with the SMTP route, all you have to do is create an account and:

  • Navigate to the ‘Sandbox’ tab and press ‘My Inbox’; 
  • You’ll see the ‘SMTP Settings’ tab. Press ‘Show Credentials’ to reveal the host, port, your username, and password; 
  • Copy those credentials and paste them into the Perl module you’re using. 

Using the code configuration: 

Don’t feel like using credentials? You don’t have to—here’s a code configuration you can use instead:

  • Navigate to the ‘Sandbox’ tab and press ‘My Inbox’; 
  • Open the dropdown menu from the ‘Integrations’ tab and choose MIME::Lite or NET::SMTP; 
  • Modify parameters such as ‘To’ and ‘From’ addresses, as well as the message subject and body. 

When using MIME::Lite or MIME::Lite::HTML::TT, alter the send()function in the following way:

$msg->send('smtp', "smtp.mailtrap.io", Port=>2525, Hello=>"smtp.mailtrap.io", AuthUser=>"your_username", AuthPass=>"your_password" );
print "Email Sent, Bro!\n";

Here’s what a sample code would look like if you were using NET::SMTP module: 

#!/usr/bin/perl
use strict;

use Cwd;
use Data::Dumper;
use Net::SMTP;
use File::Slurp;

sub sendMail($$);


use constant DEBUG => 0;

my $startDir = getcwd;

my %email = ( 'smtp' => 'smtp.mailtrap.io',
              'hello' => 'smtp.mailtrap.io',
              'user'   => 'your_username',
              'pass'   => 'your_password',
              'no_mail' => 0,  # Don't send mail. Useful for checking what would be sent...
              'debug' => 1,    # Print extra smtp feedback.
              'from' => 'piotr@mailtrap.io',
              'to' => [ 'margaret@awesomelawyers.com' ],
              'subject' => 'I may need your help once more' );

my $bodyfile = $ARGV[0];

unless (-f $bodyfile){
  die "usage: $0 <file_containing_email_body>";
}

my $mail_body = read_file($bodyfile);

print "sending mail...\n"  if(DEBUG);
sendMail(\%email, $mail_body);

exit 0;

# =======================================================

sub sendMail($$){
  my $m = shift;
  my $out  = shift;


  my $toList = join ',', @{ $m->{'to'} };

  if( $m->{'no_mail'} ){
    print "sendMail(no_mail=1): NOT sending\n" . Dumper($m) . "\n" . Dumper($out) . "\n";
    return;
  }

  my $smtp = Net::SMTP->new( $m->{'smtp'}, 'Hello' => $m->{'hello'},
                              'Debug' => $m->{'debug'}, 'Port' => 2525 );


  $smtp->auth(  $m->{'user'}, $m->{'pass'} );

  $smtp->mail( $m->{'from'} );
  $smtp->to( @{ $m->{'to'} } );


  $smtp->data();
  $smtp->datasend("From: $m->{'from'}\n" );


  $smtp->datasend("To: $toList\n" );
  $smtp->datasend("Subject: $m->{'subject'}\n" );
  $smtp->datasend("\n");

  $smtp->datasend( "$out\n" );

  $smtp->dataend();

  $smtp->quit;

}

API

With Mailtrap API, you can automate your entire testing process, test automated sequences, and have complete control of your infrastructure before sending email. 

To integrate Mailtrap API with your application, simply use the following code:

use strict;
use warnings;
use HTTP::Tiny;
use JSON;

my $url = 'https://sandbox.api.mailtrap.io/api/send/inbox-id(integer);
my $data = {
    'from' => {'email' => 'sender@domain.com'},
    'to' => [{'email' => 'receiver@domain.com'}],
    'subject' => 'Test Email',
    'text' => 'This is a test email using Perl and Mailtrap API!',
};

my $json_payload = encode_json($data);

# print $json_payload;

my $http = HTTP::Tiny->new();

my $response = $http->post($url, {
    'headers' => { 'Content-Type' => 'application/json', 'Api-Token' => 'api-token'},
    'content' => $json_payload
});

if ($response->{success}) {
    print "Response: " . $response->{content};
} else {
    die "HTTP POST error: " . $response->{status} . " - " . $response->{reason}; 
}

Note: At the end of the API endpoint, you can notice the integer is added which is indicating the inbox of the email to be tested.

Wrapping up 

Perl has multiple modules that could be used for sending emails. In this guide, we covered the most common options such as Sendmail, MIME::Lite, and MIME::Lite::TT::HTML. We also discussed why it’s a good idea to send emails from Perl with a third-party SMTP/API service, such as Mailtrap Email API/SMTP.

It’s important to test your emails before sending them to users with a safe environment such as Mailtrap Email Sandbox

If there’s any other topic you’d like us to cover, let us know on our social media channels. 

Article by Piotr Malek Technical Content Writer @ Mailtrap

Comments

1 replies

ogogon

Greetings!

I tried your code from the “Sending Email with Attachments” section.
Mail is sent, but the following diagnostic messages are constantly issued:
“Use of uninitialized value in pattern match (m //) at /usr/local/lib/perl5/site_perl/MIME/Type.pm line 77.
Use of uninitialized value in string eq at /usr/local/lib/perl5/site_perl/MIME/Type.pm line 44. ”

Something is wrong?

Ogogon.

Comments are closed.