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.
- 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.
- 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:
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:
- AUTH LOGIN
- AUTH PLAIN
- AUTH CRAM-MD5
#!/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:
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:
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.