Zurück

How to send email attachments using Perl / MIME::Lite


Contents

1.   Introduction
2.   Internet email protocols
3.   Email Anatomy
4.   Email attachments using Perl / MIME::Lite


 
1.    Introduction

This article will answer some of the basic questions, and examine the anatomy of email and its attachments. At the end we will show an example using Perl, Cygwin and MIME:Lite to send an email with attachment. In order to get the most from this article, you may need to review some of the email protocols discussed in Internet email protocols.

2.    Internet email protocols

RFC 822

The standard for text based email is based on RFC 822. This standard defines the format for the syntax and headers that make up email messages. In a nutshell, an email message consists of specific headers, each on a line by itself, a blank line, and the message body. A message ends with a period '.' following a blank line after the message body. Here's an example, showing an email message in its most basic form:

To: martin dot zahn at akadia dot ch
From: martin dot zahn at akadia dot ch
Subject: Test message

Hello world!

.

As you can see, the format is very simple. It's been in place for a number of years, and works great if you're sending plain text messages in 7-bit US-ASCII characters. But it fails miserably if you want to send binary files, or if you want to use a character set for languages other than standard English. Clearly, there needs to be another solution.

MIME

The solution is MIME, short for Multipurpose Internet Mail Extensions, and it's used as a superset for RFC 822 based email messages. MIME adds some important headers that allow more control over what you can include in email, and how it should be interpreted.

Here is an example of what an email message with an attachment looks like. (For the sake of clarity, we shortened the actual binary data for test_file.zip. Normally, that section would be a lot bigger.)

Content-Transfer-Encoding: 7bit
Content-Type: multipart/mixed; boundary="_----------=_10167391557129230"
MIME-Version: 1.0
Date: Thu, 21 Mar 2002 19:32:35 UT
From: martin dot zahn at akadia dot ch
To: martin dot zahn at akadia dot ch
Subject: MIME test
X-Mailer: MIME::Lite 2.106  (B2.11; Q2.03)

This is a multi-part message in MIME format.

--_----------=_10167391557129230
Content-Transfer-Encoding: binary
Content-Type: text/plain

Hello world!

--_----------=_10167391557129230
Content-Transfer-Encoding: base64
Content-Type: application/zip; name="test_file.zip"

UEsDBBQAAAAIAEAUmicKJJts5wUAANkOAAAHAAAAaG93LmNnaa1XbW/bRgz+
7AD5D6ziJPJmR8k+DEj8snWd0wRonS51VwxNYcj22bpZ1qm6cx0v8H77SN5J
APsNAAAAAA==

--_----------=_10167391557129230--

.

As you can see, the basic format is the same: headers, blank lines, message data, and a period '.' to end the message. The part in the middle is a bit more complicated, and that's what we'll be looking at in this article. Let's start with some of the important MIME headers.

MIME-Version

The MIME-Version field tells email programs that a MIME-compliant message is being sent. This field must be present if you're sending a message that contains anything other than 7-bit US-ASCII, since that's what will be assumed as the default. Currently, the header must appear as follows:

MIME-Version: 1.0

Content-type

Here's a familiar header field. Perl programmers are well aware of this one, since it must be included in their CGI scripts. You can omit this field in your email if you're using 7-bit US-ASCII characters. However, you'll need to specify it for other types of data, such as binary, image, audio, video, or character sets for languages other than standard English. In the example email on the previous page, we attached a zip file, and set the header like this:

Content-Type: application/zip; name="test_file.zip"

The Content-Type header field specifies both the type and subtype of data in the message. For instance the media type image/gif specifies a message body that contains a GIF image. But this header can also contain additional information, called a parameter, which is separated by a semicolon. In the example message we sent a zip file. The type and subtype is application/zip and the parameter is the file name.

It's important to note that there are no default values for subtype, therefore they're not optional. There are many MIME types, and since it's constantly changing you might want to take a look at the complete list.

Content-transfer-encoding

Because commonly used email transfer protocols, such as SMTP, assume 7-bit US-ASCII as the basis for text messages, there has to be a way to encode other data, such as binary or 8-bit characters. Although there are several standard ways to do it, the most reliable way is to use base64.

The important point for this article is that the encoding scheme needs to be specified in the email message so that email programs will know how to un-encode the data once it arrives. The example email used base64 to encode the zip file data, and set the header like this:

Content-Transfer-Encoding: base64

Now that we know a little more about how to set up an email with attachments, let's dig into the details to see how it all fits together. Let's take apart the example email, and walk through it step by step.

3.    Email Anatomy

The easiest way to think about email attachments is to conceptually break the message into its component parts. First, we'll need an "envelope", since that's the part the mail transfer programs will pay attention to when they send the message to its destination. Remember that this part consists of the headers defined by RFC 822 as well as the new MIME headers. Here's the part we're concerned with.

Content-Transfer-Encoding: 7bit
Content-Type: multipart/mixed; boundary="_----------=_10167391557129230"
MIME-Version: 1.0
Date: Thu, 21 Mar 2002 19:32:35 UT
From: martin dot zahn at akadia dot ch
To: martin dot zahn at akadia dot ch
Subject: MIME test
X-Mailer: MIME::Lite 2.106 (B2.11; Q2.03)

The headers include information on where the email is supposed to go. They also announce that this message is MIME-compliant. The Content-Type header is of type multipart/mixed, which is how you tell email programs that there is an attachment.

The parameter for this field is boundary. This is the delimiter that divides the body of the message into different parts, or attachments. The actual value for boundary is user defined, and is generally a random value set by your script. Because the value can't also appear in the body of the message, it's important to pick something that is unlikely to show up elsewhere. If you are using Perl and MIME::Lite, the value will be set for you, so you won't have to worry about it.

Dividing the message body

Now we get to the fun part, dividing the message body into the message and the attachment. Basically, all that needs to happen is to use the boundary value to designate the different parts of the message.

Each part of the message body is preceded by boundary delimiter lines, which consists of two hyphen characters '--' followed by the boundary value.

In the example message, the boundary value is

_----------=_10167391557129230

Therefore, the boundary delimiter lines are:

--_----------=_10167391557129230

The last part of the message ends with two hyphen characters '--', the boundary value, and two more hyphens:

--_----------=_10167391557129230--

So you see, an email message with an attachment is nothing more than headers and a message body divided with boundary delimiter lines. In other words, the email message can be conceptually viewed as:

Headers

--BoundaryValue
email Message

--BoundaryValue
email Message - Attachment 1

--BoundaryValue
email Message - Attachment 2

--BoundaryValue--

.

4.    Email attachments using Perl / MIME::Lite

Cygwin

OK, let's learn how to send email attachments with Perl and MIME::Lite. This sample uses Windows 2000 and Cygwin, a UNIX environment for Windows. Download it from http://www.cygwin.com. For more Information about Cygwin see: http://www.akadia.com/services/cygwin_tools.html.

MIME::Lite

MIME::Lite is intended as a simple, standalone module for generating (not parsing!) MIME messages, specifically, it allows you to output a simple, decent single- or multi-part message with text or binary attachments. It does not require that you have the Mail:: or MIME:: modules installed. Download ist from: http://www.cpan.org/modules/by-module/MIME/. More information can be found here: http://search.cpan.org/dist/MIME-Lite/

Install the module as follows:

perl Makefile.PL
make
make test
make install

Example

In the sample we are sending two attachments, a GIF-File (my_file.gif) and a ZIP-File (my_file.zip).

#!/usr/bin/perl

# Akadia AG, Arvenweg 4, CH-3604 Thun                 send_attachment.pl
# ----------------------------------------------------------------------
#
# File:       send_attachment.pl
#
# Autor:      Martin Zahn / 05.01.2003
#
# Purpose:    Email attachments in Perl
#
# Location:   $ORACLE_HOME\Database
#
# Certified:  Perl 5.6.1, MIME-Lite-2.117 on Cygwin / Windows 2000
# ----------------------------------------------------------------------


use MIME::Lite;
use Net::SMTP;

### Adjust sender, recipient and your SMTP mailhost
my $from_address = 'martin dot zahn at akadia dot ch';
my $to_address = 'martin dot zahn at akadia dot ch';
my $mail_host = 'mailhost.domain.com';

### Adjust subject and body message
my $subject = 'A message with 2 parts ...';
my $message_body = "Here's the attachment file(s) you wanted";

### Adjust the filenames
my $my_file_gif = 'my_file.gif';
my $your_file_gif = 'your_file.gif';
my $my_file_zip = 'my_file.zip';
my $your_file_zip = 'your_file.zip';

### Create the multipart container
$msg = MIME::Lite->new (
  From => $from_address,
  To => $to_address,
  Subject => $subject,
  Type =>'multipart/mixed'
) or die "Error creating multipart container: $!\n";

### Add the text message part
$msg->attach (
  Type => 'TEXT',
  Data => $message_body
) or die "Error adding the text message part: $!\n";

### Add the GIF file
$msg->attach (
   Type => 'image/gif',
   Path => $my_file_gif,
   Filename => $your_file_gif,
   Disposition => 'attachment'
) or die "Error adding $file_gif: $!\n";

### Add the ZIP file
$msg->attach (
   Type => 'application/zip',
   Path => $my_file_zip,
   Filename => $your_file_zip,
   Disposition => 'attachment'
) or die "Error adding $file_zip: $!\n";

### Send the Message
MIME::Lite->send('smtp', $mail_host, Timeout=>60);
$msg->send;

Save the code in the file send_attachment.pl and execute it as follows:

$ ./send_attachment.pl

Now you will receive the email with both attachments included: