Problems with and solutions to sending e-mail with Perl. Short on time? Try Email::Sender::Simple. See also Email Hates The Living. Do not, I repeat, do not attempt to send e-mail by piping data directly to sendmail or mail(1).
Buggy Unportable Unscalable Error Prone Method
Repeated far too often in code, despite various horrible flaws and limitations. Do not use!
open SENDMAIL, "|/usr/sbin/sendmail -t"
or die "$0: fatal: could not open sendmail: $!\n";
print SENDMAIL "To: user\@example.org\n";
print SENDMAIL "Subject: message\n\n";
print SENDMAIL "Message body\n";
close SENDMAIL;
Problems include:
- Unportable sendmail call: Windows and other non-Unix systems lack sendmail.
- Unportable sendmail path: Solaris uses /usr/lib/sendmail.
- Buggy sendmail call: if the message contains a . on a line, anything following this line will not be sent. Add the -oi -oem options, or better yet, use a module instead.
- Fragile and error prone message creation: user\@example and newline infested strings require careful review to ensure no formatting errors slip by. Modules lack this problem.
- Unscalable: no accounting for message encoding or Multipurpose Internet Mail Extensions (MIME). I’ve seen code that pipes output to a uuencode call, then feeds that to the message. Terrible!
Net::SMTP is another module I dislike: it does not support Mail Exchange (MX) records, and thus to avoid a single point of failure—the single host the module connects to—requires wrapping with additional code that looks up MX records, handles various temporary or permanent send failures, queues temporarily failed messages, and otherwise begins to poorly implement a proper Mail Transport Agent (MTA). The portable, scalable method, below, passes e-mail off to a local MTA, which handles the (many!) delivery complications of the Simple Mail Transport Protocol (SMTP). If the code cannot be fixed to not rely on a single host, then time must be wasted setting up a highly available address, usually via some expensive network device or complicated software load balancer, and a likely higher risk of e-mail delivery failure accepted.
Portable Scalable Method
Among the many Perl modules available on Comprehensive Perl Archive Network (CPAN) for sending e-mail, I favor MIME::Lite. Easy to use, easy to attach new content, and easy to send the message elsewhere for debugging:
use MIME::Lite;
my $message = MIME::Lite->new(
To => 'user@example.org',
Subject => 'message',
Data => 'Message body'
);
$message->send();
- Need to add an attachment?
- Need to debug what the message looks like without sending?
$message->attach(
Type => 'image/gif',
Path => $filename,
Disposition => 'attachment'
);
$message->send();
if ($use_stdout) {
$message->print( \*STDOUT );
} else {
$message->send();
}
Consider also the Email::Sender::Simple module, or perhaps Email::Sender::Transport::SMTP if SMTP must be used or Email::Sender::Transport::SMTP::TLS for SMTP with TLS (SSL) support. Other options for MIME include Email::MIME. For more complex MIME messages, see Email::MIME::Kit::*.
Message Content
Never build up a message into a scalar, then print the scalar. HTML errors become difficult and time consuming to debug in longer messages, and extending or reformatting the look of the message is nearly impossible.
my $message_body = '<html></body>';
$message_body .= '<B>This is as ugly as it looks</B';
if ($printer_on_fire) {
$message_body .= "<p>More <a href=\"ugh\">horrors</a>";
}
Instead, template the message with HTML::Template or similar module. This abstracts the data properly away from the content. At the very least, stop backslashing doublequotes in doublequoted strings:
my $ugly = "<a href=\"$url\">$link_text</a>";
my $link = qq{<a href="$url">$link_text</a>};