Skip to page content or Skip to Accesskey List.

Work

Main Page Content

Using Files To Send Emails With Iis Part 2 Of 2

Rated 4.13 (Ratings: 6)

Want more?

  • More articles in Code
 
Picture of sgd

Scott Dexter

Member info

User since: 26 Apr 1999

Articles written: 10

(finally!)

In the first installment we saw how to create emails and drop them off in the file system instead of talking directly to the email server. In this piece, we build on the code and add the ability to send text attachments.

The Evidence

First, let's see what an email file with an attachment looks like as it sits

quietly, waiting for the SMTP server to come by and pick it up:

Return-Path:<sgd@thinksafely.com>

Date: Wed, Sep 27 2000 12:17 -0600

To: <sgd@ti3.com>

From: <sgd@thinksafely.com>

Subject: test -- attachment

MIME-Version: 1.0

Content-Type: multipart/mixed; boundary="XXXXMESSAGEBOUNDARYXXXX"

--XXXXMESSAGEBOUNDARYXXXX

Content-Type: text/plain; charset="US-ASCII"

Content-transfer-encoding: 7bit

this is an attachment test

--XXXXMESSAGEBOUNDARYXXXX

--XXXXMESSAGEBOUNDARYXXXX

Content-Type: text/plain; name="reghelp.htm"

Content-Transfer-Encoding: quoted-printable

Content-disposition: attachment; filename="reghelp.htm"

<html>

<head>

<title>Simple page</title>

</head>

<body>blow me</body>

</html>

--XXXXMESSAGEBOUNDARYXXXX--

Reconstructing the Crime

The first thing to notice is in order to make an attachment, we have to

change the MIME type so the email program on the receiving end knows to

parse it out and separate the attachment from the email body. The MIME type

is named "multipart/mixed" --a generic term that says, "Hey I've got

multiple pieces and each piece may have its own MIME type. Cool. But how do

we separate the pieces? Like so:

<%

Const MIMEBOUNDARY = "XXXXMESSAGEBOUNDARYXXXX"

mimeheader = "Content-Type: multipart/mixed; boundary=""" & MIMEBOUNDARY &

""""

%>

The separator is up to you. Make it lengthy, and convoluted. What

you don't want is a separator string that may actually be found elsewhere in

the file. Next, we get to piece up the contents into email body and

attachment. Looking at the format above, its pretty straightforward:

  1. parts are encapsulated in "--" + separator boundaries. The last separator

    in the file has trailing "--" to signify it's the last part and the EOF is

    coming up
  2. No line breaks between parts. Having anything --including line breaks--

    between parts will generate an additional attachment when the client reads

    it, and it will contain anything you put between separators (including just

    line breaks)
  3. redefine the MIME type of each part

For the body of the email (from what I understand the body is always the

first part in a multipart message), we use the MIME type from last time:

<%

bodyMIME = "Content-Type: text/plain; charset=""US-ASCII""" & vbNewLine

bodyMIME = bodyMIME & "Content-transfer-encoding: 7bit" & vbNewLine &vbNewLine

%>

For the attachment, there are options. We can attach anything, from a jpeg

file to an Access database. Since we're limiting this to attaching text

files (for demonstration), let's worry about just the minimum requirements:

<%

attachMIME = "Content-Type: text/plain; name=""reghelp.htm""" & vbNewLine

attachMIME = attachMIME & "Content-Transfer-Encoding: quoted-printable" &vbNewLine

attachMIME = attachMIME & "Content-disposition: attachment;filename=""reghelp.htm"""

attachMIME = attachMIME & vbNewLine & vbNewLine

%>

In the first line I added the name attribute to identify the local filename

(as it was named when the email was created). The transfer encoding is set

to "quoted-printable" in order to pass the email from server to server to

your email reader without changing the data. The difference between 7-bit

and quoted printable is that 7-bit encoding is really NO encoding, and it is

understood that the content is already in emailable format (lines 76

characters or less of US-ASCII data). Quoted printable is a way to keep

things intact. The last line is the magic one. We tell the email reader that

this is an attachment, and on top of that, we tell the email reader what to

name it. The cool thing here is that the name attribute in the first line

does not have to equal the filename attribute. Well, I found this a cool

thing. But I send out attachments that have to have a date appended to the

filename =)

So after we have our MIME schtuff in order, we have to actually attach

something, right? Let's slurp in a file from the file system, returning

Empty if it doesn't exist or encounter any errors (like the path not being

found):

<%

Function ReadAttachment(byval filename)

On Error Resume Next

Dim ForAppending,fs,a,logstr

' slurp in an attachment

ForReading = 1

Set fs = CreateObject("Scripting.FileSystemObject")

Set a = fs.OpenTextFile(filename, ForReading)

' first determine if it exists

if a.AtEndOfStream then

slurped = Empty

else

slurped = a.ReadAll

end if

a.Close

Set a = Nothing

Set fs = Nothing

ReadAttachment = slurped

End Function%>

The caveat here is that you have to know the FULL path to the file,

otherwise you'll get an empty attachment. The other side of that coin is we

don't want the full path name in with the file name. I don't want the

recipient to know my directory structure. So to that end:

<%

Function StripPath(ByVal strFilename)

' grab the filename from the full path,

'basically everything after the last \

patharray = Split(strFilename, "\")

tmpname = patharray(UBound(patharray))

If InStr(tmpname, """") <> 0 Then ' take out double quotes

tmpname = Replace(tmpname, """", "")

End If

StripPath = tmpname

End Function

%>

Now that we know what to change, and we've got what we need to attach a

file, lets rip open the code from last time and add the changes. Let's make

our boundary string a constant and tweak functions GenMIMEHeaders() and

GenMIMEEmail():

<%

Const MIMEBOUNDARY = "XXXXMESSAGEBOUNDARYXXXX"

'********* GenMIMEHeaders ************

function GenMIMEHeaders(byval replyto, byval from, byval mto, byval subject)

replyto = "<"& replyto &">"

from = "<"& from &">"

sendto = split(mto,",")

for each addr in sendto

tolist = "<"& addr &">," & tolist

Next

tolist = Left(tolist,len(tolist)-1) ' take off the last comma

headers = "Return-Path:"&replyto & vbNewLine

headers = headers & "Date: " & GenMIMEDate(Now,"-0600") & vbNewLine

headers = headers & "To:"& tolist & vbNewLine

headers = headers & "From:"& from & vbNewLine

headers = headers & "Subject: "& subject & vbNewLine

headers = headers & "MIME-Version: 1.0" & vbNewLine

headers = headers & "Content-Type: multipart/mixed;

boundary="""&MIMEBOUNDARY&""""

GenMIMEHeaders = headers & vbNewLine & vbNewLine

end function

'********* GenMIMEEmail ************

function GenMIMEEmail(byval from, byval mto, byval subject, byval body,byval fileattach)

bodyMIME = "Content-Type: text/plain; charset=""US-ASCII""" & vbNewLine

bodyMIME = bodyMIME & "Content-transfer-encoding: 7bit" & vbNewLine & vbNewLine

fullmail = GenMIMEHeaders(from,from,mto,subject) & "--" & MIMEBOUNDARY & vbNewLine

' --add the body--

fullmail = fullmail & bodyMIME & body & vbNewLine

fullmail = fullmail & "--" & MIMEBOUNDARY

' Do we need to attach a file?

if isEmpty(fileattach) or fileattach="" then ' Nope, no file, close the

separator

GenMIMEEMail = fullmail & "--" & vbNewLine

else

' there's an attachment

attach = StripPath(fileattach)

attachMIME = "Content-Type: text/plain; name="""& attach &"""" & vbNewLine

attachMIME = attachMIME & "Content-Transfer-Encoding: quoted-printable" & vbNewLine

attachMIME = attachMIME & "Content-disposition: attachment; filename="""&attach&""""

attachMIME = attachMIME & vbNewLine & vbNewLine

fullmail = fullmail & vbNewLine & "--" & MIMEBOUNDARY & vbNewLine & attachMIME

fullmail = fullmail & ReadAttachment(fileattach) & vbNewLine

GenMIMEEMail = fullmail & "--" & MIMEBOUNDARY & "--" & vbNewLine

end if

end function

%>

So the finished function calls to get the job done look like this:

<%

' in VB and VBScript, we can use the _ to extend to the next line

email = GenMIMEEmail("sgd@ti3.com", _

"sgd@fastlane.net", _

"New Log", _

"Here is your log", _

"D:\logfiles

ewestlog.txt")

' without an attachment:

email = GenMIMEEmail("sgd@ti3.com", _

"sgd@fastlane.net", _

"New Log", _

"Here is your log", _

"") ' Note we've allowed Empty and "" to signify no file to attach

writeEmail email,GenMIMEName

%>

To note, the reason I use constructs like

<%

headers = headers & "To:"& tolist & vbNewLine

headers = headers & "From:"& from & vbNewLine

headers = headers & "Subject: "& subject & vbNewLine

%>

is for readability

(sorta)— it allows me to explain a portion of logic without having to look

for code hidden inside a long line. In production, you do want to reduce the

redundancy as much as you can, and place that stuff on one line. Or keep

them separated out, whichever you prefer. It is a minor performance hit to

spread it out like this, but we're not concentrating on that today =)

So that's that. We're sending emails, and even attachments if we're feeling

saucy.

-------

Sidebar

You've read about n-tier applications and separating "business logic" so it

can be re-used, right? This little project here *screams* to be done in a

COM object, where it is 1) compiled, and runs faster and 2) an object

available for use across your system, even for non web apps. Keep that in

mind as you write lengthy functions and 'classes' in your ASP files.

-------

The access keys for this page are: ALT (Control on a Mac) plus:

evolt.org Evolt.org is an all-volunteer resource for web developers made up of a discussion list, a browser archive, and member-submitted articles. This article is the property of its author, please do not redistribute or use elsewhere without checking with the author.