Introduction


Sending automated e-mails is a often requested task. However, the generation and formatting of these e-mails to meet todays standards is not exactly trivial: Using HTML, including inline images and supporting multi-byte languages (i.e. Japanese) are becoming common requirements. Below are several ready-to-run example programs in Perl to get started with the tasks.

Perl libraries for mail handling


Several Perl libraries exist in CPAN to help us with basic e-mail functions. I looked at Mail::Sendmail and MIME::Lite. Both modules have their limitations.

  1. Mail::Sendmail is perhaps the easiest module to use. It doesn't have MIME functions like boundary generation or content-type conversion for multi-part messages. In its latest version it can handle SMTP authentication, but no SSL/TLS encryption that is typical in setups for most mailservers today.
  2. MIME::Lite has functions to help with MIME multipart handling and it supports SMTP authentication. SSL is not included, and the data auto-convertion based on the content-type could be unwanted in rare cases.

To install these modules it is best to check your Linux package management system first, in many cases they are selectable there. If not, then a installation through CPAN is perhaps the second best choice. In OpenSUSE, the packages are:

# rpm -q -a |grep "perl-Mail-Sendmail\|perl-MIME-Lite"
perl-Mail-Sendmail-0.79-102.1.i586
perl-MIME-Lite-3.024-2.1.i586

Generating a simple ASCII text E-mail with Perl


The first example program send_mail_text.pl below shows how to send a simple ASCII text e-mail from a Perl program using Mail::Sendmail.

#!/usr/bin/perl -w
# ############################ send_mail_text.pl #################
# Date    : Dec 31 2010
# Purpose : Perl example script for sending out text e-mails
# Author  : Frank Migge (support at frank4dd dot com)
# URL     : http://fm4dd.com/programming
# License : GPL - http://www.fsf.org/licenses/gpl.txt
# Version : 1.0 initial release
# #################################################################
use Mail::Sendmail;
use strict;
use warnings;

# #################################################################
# define the e-mail participants, server and content
# #################################################################
# create the main e-mail hash structure [REQUIRED]
my %mail;
# set the smtp mail server [REQUIRED]
$mail{smtp}    = "127.0.0.1";
# set the recipients (to) address [REQUIRED]
$mail{To}      = "Test Receiver ";
# set the mail sender address [REQUIRED]
$mail{From}    = "Test Sender ";
$mail{Sender}  = "Test Sender ";
# set the carbon copy (cc) recipient address
# $mail{Cc}      = undef;
# set the blind carbon copy (bcc) recipient address
# $mail{Bcc}     = undef;
# set the  mail subject line
$mail{subject} = "Test message";
# set the mail content
$mail{body}    = "The test messsage is having this body line inside.";
# set the mail encoding type
$mail{'content-type'} = qq(text/plain; charset="utf-8");

# #################################################################
# send the e-mail out and return verbose information
# #################################################################
sendmail(%mail) or die $Mail::Sendmail::error;
print "The sendmail log reports:\n".$Mail::Sendmail::log."\n";
exit 0;

A testrun of the script will produce the following output:

# ./send_mail_text.pl
The sendmail log reports:
Mail::Sendmail v. 0.79 - Tue Apr 19 14:49:29 2011
Date: Tue, 19 Apr 2011 14:49:29 +0900
Server: 127.0.0.1 Port: 25
From: test@frank4dd.com
Subject: Test message
To: support@frank4dd.com
    
Result: 250 2.0.0 Ok: queued as 03E6843B5D

The generated text e-mail example, viewed within a e-mail client program:

send_mail_text.pl example e-mail

Adding in SMTP authentication


Because the e-mail protocol SMTP has been abused for SPAM, mail servers often require SMTP authentication before accepting a e-mail to send out. If the connection is made without authentication, mail servers return a error message such as this:

550-Please turn on SMTP Authentication in your mail client...

There are three widely used authentication methods:

Below we add the PLAIN authentication method to the previous code and create a new test program send_mail_auth.pl. The authentication settings below are marked in red. Then we re-run our test.

#!/usr/bin/perl -w
# ############################ send_mail_auth.pl #################
# Date    : Dec 31 2010
# Purpose : Perl example sending out text e-mails w authentication
# Author  : Frank Migge (support at frank4dd dot com)
# URL     : http://fm4dd.com/programming
# License : GPL - http://www.fsf.org/licenses/gpl.txt
# Version : 1.0 initial release
# #################################################################
use Mail::Sendmail;
use strict;
use warnings;

# #################################################################
# define the e-mail participants, server and content
# #################################################################
# create the main e-mail hash structure [REQUIRED]
my %mail;
# set the smtp mail server [REQUIRED]
$mail{smtp}    = "127.0.0.1";
# set the smtp port if not 25, i.e. use the submission port 587
$mail{port}    = 587;
# set the recipients (to) address [REQUIRED]
$mail{To}      = "public1\@frank4dd.com";
# set the mail sender address [REQUIRED]
$mail{From}    = "test1\@frank4dd.com";
$mail{Sender}  = "test2\@frank4dd.com";
# set the carbon copy (cc) recipient address
# $mail{Cc}      = undef;
# set the blind carbon copy (bcc) recipient address
# $mail{Bcc}     = undef;

# #################################################################
# define the mail server authentication parameters
# #################################################################
$mail{auth} = {user => "smtpuser", password => "smtppass",
               method => "LOGIN PLAIN", required => 1};

# set the  mail subject line
$mail{subject} = "Test message";
# set the mail content
$mail{body}    = "The test messsage is having this body line inside.";
# set the mail encoding type
$mail{'content-type'} = qq(text/plain; charset="utf-8");

# #################################################################
# send the e-mail out and return verbose information
# #################################################################
sendmail(%mail) or die $Mail::Sendmail::error;
print "The sendmail log reports:\n".$Mail::Sendmail::log."\n";
exit 0;

Here is the test run of our updated test mail script:

fm@susie114:~> ./send_mail_auth.pl
The sendmail log reports:
Mail::Sendmail v. 0.79 - Wed Jan 23 10:54:57 2013
Date: Wed, 23 Jan 2013 10:54:57 +0900
Server: 127.0.0.1 Port: 25
From: test2@frank4dd.com
Subject: Test message
To: public1@frank4dd.com

Result: 250 2.0.0 Ok: queued as 7F29737C14

Generating a HTML-formatted E-mail with Perl


In order to send a HTML-formatted e-mail, only minimal changes in 2 lines (line 33 and line 35) are necessary to the script above.

...
# set the HTML mail content
$mail{body}    = "<html><body bgcolor=\"#669999\"><h2>Title</h2><hr><p>This is a HTML test
 messsage.</p></body></html>";
# set the mail encoding type
$mail{'content-type'} = qq(text/html; charset="utf-8");
...

Here is the second example program: send_mail_html.pl. The "send_mail_html.pl"-generated, HTML-formatted e-mail example as viewed in a e-mail client program, is shown below:

send_mail_html.pl example e-mail

Generating multi-part (MIME) E-mails with inline images from Perl


This is were it gets more challenging. For the ultimate end user experience with integrated pictures, we need to send MIME multi-part e-mails. Unlike the former simple creation of a single mail body, this type of e-mail requires the creation and bundling of separate mail parts: The message itself is a different part, each referenced picture to be displayed is another. These e-mail parts are separated by boundary markers and have different content types (text, picture, binary, etc) that help the mail client decide how to handle these email parts. For example, the first part of the e-mail is the HTML message with a picture file being included as a second, different part. In order to let the e-mail client program display the image from the second part inside the first parts HTML message (inline), the first HTML message part needs to have a reference to the second part. A unique identifier ID is needed to pick the right data from the e-mail parts, because we could have more then two.

To complicate things even more, pictures are expected to be send in uuencoded ASCII-format rather then in their original binary form (GIF, JPEG, etc). Several RFC cover all aspects: RFC 2045, RFC 2046, RFC 2183 among them.

Lets look at the example program: send_mail_multi.pl. We need approx. 2-3 times more lines of code to handle it. A testrun shows how the mail data looks when completed, with the inline reference highlighted in red:

# ./send_mail_multi.pl 

This is a multi-part message in MIME format.
--======fd709c40cfff1d107bf34be4======
Content-Type: text/html; charset=utf-8
Content-Transfer-Encoding: 8bit

<html><body bgcolor="#669999"><h2>Title</h2><hr>
<img src="cid:part.18.57.07673.27128.5fd70.9c40c.fff1d.107bf.34be@MAIL">
<p>This is a HTML test messsage.</p></body></html>

--======fd709c40cfff1d107bf34be4======
Content-Type: image/gif; name="logo.gif"
Content-Transfer-Encoding: base64
Content-ID: <part.18.57.07673.27128.5fd70.9c40c.fff1d.107bf.34be@MAIL>
Content-Disposition: inline; filename="logo.gif"

R0lGODlhQABAAPYAAAAAAAcHCwcJCggGBQoGCAsLCxINDRQTFBYVGRsWGBgYFhsbGyAeHCMjIyUl
KikkJCwrLCQxKDAxLjM0MzQ1ODg3NTg4Nzo6Oz8+QUA+QEBBP0NDQ0hHRktLS09QTFBPTlFQT1RU
VFdZV1daXVhYV11cXVphX2NfX2FhX2NjYmVlaWtra215c3R0c3l5dnx8e4ODg4WEiYWLiYyMjI2N
kJOTk5aWmJeZlZmal5ycnJ6eoKKfoKOjo6inp6ysrK+usa6zr7Gvr7Gzr7S0tLW6tby3ubq5tbu7
u729wMG/v8TExMjFw8nJx8zMzMrSztDQz9PT09fX29ra2t/f5d/k4eLi3uPk4+fn6Ojo5+zs7PT0
9Pb2+P399/7+/szMzAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH5
BAEAAF4ALAAAAABAAEAAAAf+gF6Cg4SFhoeIiYqLjI2Oj5CRkpOUlZaXmJmam5ydnp+goaKjpKWm
p6ipqqusra6vsLGys7S1rF24hLmYuF2ZWVBNSlG+XVJQUFe+lVdMQ01Zk8ZHVl5NKxsTNVpeXT4b
HDnRlUwiDS1Zy5BQPjw5XUwQAAAXP1pdMAIAHVCCvf+6enXr9cQcCyvqBAYEeCQHDRjw5AEIgOFI
lxcDAGzo18UKEyVMPhITpAXKECJPrDSBQsVLsyFPtnQDhkyKlXszoxzxMYRJlYY5fHQp4mDePBVW
YGTUAKXLkRYXJFSQYOHFk249SDiAEAJGhww0tCgZUVVKtyEnPnAIkaKGWSv+PDo4aNCgKhMoJXAh
kTgPQY0Y+phaWUEAwAMSKBQgmLFFSop5BRYgmFcC7gIAFpZk+fFhgIEHhRvQ2NIDQgAHJSoUsGBx
EN4ABS4UAAABxFIoWHCsSHGkihF5KawMsQBAwYseFwAUeOFU3gUmRTQQMABjyYrZG6rcEBAgBJIk
PIb0G6SFRgG/IyDPY2qMSZIZJCpkXLFlyAQAHJhweUGgwIwuTSTXQRU7GABABU0M9QAACwwxRFEF
NLBBCTokOEgSHTAYFHEZ7YPMClR1AAIIBwCgQhZKSIBfTPwN4MIWTWS4kQ8LbiBFFw8y2EMWOaxw
AgSFLdCCWd0A0QCDNHT+gYMCRnFgkooTAEHFERgEMAIVUqwwgHEz3AdACVcoQQEAExwBRQYANODD
FDSUSIESVSTxzBIwMLkBE/4cURQEPVhTglEXRBGEPA288AIGs5lw4xEqCoBAiV9ewcNlEgxhRQuz
SRBCUQK0cEUPFSTAAQ4zXJbCeAB2MEAFQ3RDRAcLFBACFE+gYMAAC0wAggQRsNAPjxtY4MJ1+0jR
RAgFdKAEgDBAsMABEnTQQhI4ksCAo7mmMIQ6WQzBgw83blFSDz0kkQ4TOcBQQ09EAOHEFlDgcMQT
T2QBGAAgSKHFETpY2s0VR/QQVBNWcOGFFk/wMAMNOTAhhUyCwJvDDkOICDzDxTwQwUQ1XXCB0z9d
KDFBA9LCMGYBMESj0EAC7QLyLoJcUYMFEEAggQQTVDCBzUMuAkULCAxwAAQIIKBCU5yIhUMOPOgQ
Q3g81BDeFYt0AUUOMcywQw42IM1JLgAVA7MivWzxj8G2pK322my37fbbcMct99x012333Xjnrffe
fPftdyGBAAA7
--======fd709c40cfff1d107bf34be4======--

The sendmail log reports:
Mail::Sendmail v. 0.79 - Wed Apr 20 18:37:55 2011
Date: Wed, 20 Apr 2011 18:37:55 +0900
Server: 127.0.0.1 Port: 25
From: test@frank4dd.com
Subject: Test message
To: support@frank4dd.com
    
Result: 250 2.0.0 Ok: queued as 415CC1C409

The send_mail_multi.pl generated, HTML-formatted e-mail example as viewed within a e-mail client program:

send_mail_multi.pl example e-mail

Generating a multi-part (MIME) E-mail with inline images using MIME::Lite


Let's generate the same e-mail with a different library, using MIME::Lite instead. We cannot use the pre-generated image data as a base64-encoded string, instead we need read the image file from a path. Here is the example program: send_mail_multi2.pl.


Content:

Source: