Skip to page content or skip to Accesskey List.
Search evolt.org
evolt.org login: or register

Work

Main Page Content

Using files to send emails with IIS, part 1 of 2

Rated 4.2 (Ratings: 12) (Add your rating)

Log in to add a comment
(2 comments so far)

Want more?

  • More articles in Code
  • More articles by sgd
 
Picture of sgd

Scott Dexter

Member info | Full bio

User since: April 26, 1999

Last login: December 11, 2009

Articles written: 10

Don't use CDONTS (even the acronym is a good reminder). Don't use ASPMail. Don't worry about talking to the email server directly. Do it the fast smooth —asynchronous— way: drop a file off and let the SMTP server handle it.

Why?

  1. Performance — Writing to a file may be faster (depending on the run-time environment) than talking to a mail server, especially when you may not be able to control the availability/response time of the mail server. On top of that, if the attachment is large, your page has to sit and wait for the mail server to slurp in all the data. Its not much slower than our reading-and-rewriting, but something to consider. --Add in multiple recipients and you see where this can get out of hand and we should just treat this as...
  2. Asynchronous — Let's say we have to send out 2 emails. Using CDONTS or a third party component like ASPMail will be fine. Let's say we have to send out 2,000. 200,000. 2,000,000? —Having a process (in our examples an ASP page) connected to a mail server that long (1) isn't efficient, and (2) most likely won't run to completion in one session because email servers have limits to the number of messages they'll accept from one connection at one time (spam discouraging, and while you're connected noone else can). Using this method, we can write 10,000 emails to disk and let the mail server get to them when it can. We're no longer bound to the SMTP server's performance and in my mind that makes it...
  3. Scalable — If you handle large amounts of outgoing email, you can (relatively) easily migrate from the local disk to a shared storage solution or something of the sort to handle the file I/O. Keeping things in a uniform paradigm (dealing with files) as when working with rejected emails and (if you dare) incoming email in addition to just sending them is a plus to me. Its easier to administer. And, if you need to handle multiple MIME types, or send binary files...
  4. You can ditch the home-spun code but keep the process. Mabry has an ActiveX COM object that will handle all your MIME email generation needs, including writing the output to file (http://www.mabry.com/mailx.htm).

Why not?

  1. Scalability is a concern, but not a show stopper. Say you've got 4 web servers, and one email server. You're left with using UNC names and drive shares for the 4 web servers to write to the email server's Pickup directory, and that introduces permissions headaches, enough to warrant converting the code into an ActiveX DLL so it can be loaded in MTS where you can specify the identity under which it executes, and this can also lead to problems with...
  2. Maintainability. —Got a new MIME type you wanna send? Open the code. Wanna send a binary file? open the code (our code here does text attachments). We are kinda re-inventing the wheel here, which is to be expected when you work on something that others are too. With some spare cash (probably amounting to less than the amount of billable time you spend writing this), you can grab ActiveX objects to handle the dirty work for you (http://www.mabry.com/mailx.htm).

That being said, we use this process to handle our emails. Its robust, dern fast, and we send a ton of emails this way.

The recipe

Here's what you need to cook this up:
  • IIS 4
  • IIS' SMTP Server installed on the same server
  • ASP and VBScript (Part 3 could be putting this stuff into a component (dll), ask me)
  • a little MIME knowledge

Working backwards, let's start with a little MIME, shall we?
<insert image of midget in white face-paint trying to get out of an invisible box>

MIME (Multi-purpose Internet Mail Extensions) is an extension of SMTP (Simple Mail Transfer Protocol), that allows some description of the data to cue the recipient into using the right application to view the contents. You may have already dealt with MIME types and not even know it. "text/html" anyone? how about "text/css" ? --Those are MIME types, and tell the recipient (your browser) how to handle the data.

What does knowing MIME do for us? Well, in order to just drop a file off and go, and expect IIS' SMTP server to send it out, we have to write the file in raw MIME format. It's not that tough, but ya gots to know what goes where. —We get to generate our own email headers. On top of that, if we want to send attachments, we have to play with MIME and know the right way to package it.

Email process overview:

  1. we gotta generate a unique filename
  2. enumerate through our headers and write it to file (with the filename above)
  3. we move that file to the SMTP Pickup directory, where the SMTP Server nabs it

Creating a unique filename

   Because we may have multiple people firing this off, we need to ensure we don't step on anyone else:

<%
Function GenMIMEName()
   Randomize   ' Initialize random-number generator.
   RandomNbr = Int((1000000 * Rnd) + 0)
   RandomNbr = Right("0000000" + RandomNbr, 6)
   GenMIMEName = Request.ServerVariables("REMOTE_ADDR") & "_" & RandomNbr & ".email"
End Function
%>

Enumerate through file

We need to have some specific headers and such, here's what a MIME email looks like before its sent out:

Return-Path:<support@ti3.com>
Date: Mon, Aug 28 2000 17:41 -0600
To: <sgd@ti3.com>
From: <support@ti3.com>
Subject: [Friday Freebie] Look, ma no CDONTS!
MIME-Version: 1.0
Content-Type: text/plain; charset="US-ASCII"
Content-transfer-encoding: 7bit

This is the body of the email. I'm the company liability!

— So this is pretty straightforward then, right? We gotta worry about a couple things: creating the date in the right format, and making sure we have at least one blank line after the headers to indicate the start of the body. To create the date, we need to spit it out in "ddd, dd mmm yyyy hh:nn gggg" —try this:

<?php
function GenMIMEDate(byval mydatebyval gggg)
         
' mydate is already a date type, no error checking here!
         ' 
gggg is expected to already be in offset from GMT formati.e"-0600"
         ' we need to return ddd, dd mmm yyyy hh:nn gggg
         ddd = weekdayname(weekday(mydate),True)
         mmm = monthname(month(mydate),True)
         ' 
make sure we have a leading zero on single digit dates
         dd 
zeropad(day(mydate),2)
         
yyyy year(mydate)
         
hh hour(mydate)
         
nn minute(mydate)
         
GenMIMEDate ddd &", "dd &" "mmm &" "yyyy &" "hh &":"nn &" "gggg
end 
function


Function 
zeropad(byval valbyval ceiling)
if 
len(val)<ceiling then  pad with zeros to make ceiling chars long
    
for i=1 to ceiling-Len(val)
        
val "0"&val
    Next
end 
if
zeropad val
End 
Function
?>

— I played with dummy proofing the offset, but for now let's just assume that we already now it, as its not the point of this tip ;)

So here's what I have to generate our headers and email:

<%
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: text/plain; charset=""US-ASCII""" &
vbNewLine
GenMIMEHeaders = headers & "Content-transfer-encoding: 7bit" & vbNewLine &
vbNewLine
end function

function GenMIMEEmail(byval from, byval mto, byval subject, byval body)
GenMIMEEmail = GenMIMEHeaders(from,from,mto,subject) & body
end function
%>

—Notice we're able to take in a comma delimited list of To: addresses. That way we can send to multiple people at once (from the department of redundancy department)

Also notice I've separated creating the headers with attaching the body. This on purpose, because in the next part we're gonna expand to include attachments.

File handling

Now we gotta write it to a file, in a temporary spot. This is because if we create the file in IIS's SMTP Server Pickup directory, it will try to slurp it up before we're done, and that's Bad. Oh yeah, where the hell is this Pickup directory? —\InetPub\MailRoot\Pickup (using the default install name for the Inet root). Create a directory for the temp spot; we use D:\InetPub\MailRoot\Pickup\TempMail.

Let's write it to file, shall we?

<%
Sub WriteEmail(byval email, byval filename)
Dim ForAppending,fs,a,logstr
' drop the email to a file
ForAppending = 8
filename = "D:\InetPub\Mailroot\Pickup\tempmail\" & filename
Set fs = CreateObject("Scripting.FileSystemObject")
Set a = fs.OpenTextFile(filename, ForAppending, True)
a.Write(email)
a.Close
Set a = Nothing
Set fs = Nothing
End Sub
%>

Move the temp file into the Pickup directory

Well, so far we gots the email being generated with all the MIME we need (for now), and we're writing it to the file system with no probs. Now we gotta move it to the Pickup directory to actually get it sent. I'll reopen the WriteEmail subroutine and tweak a few things to get it done:

<%
Sub WriteEmail(byval email, byval filename)
Dim ForAppending,fs,a,logstr
' drop the email to a file
tempdir ="tempmail\"
pickupdir = "D:\InetPub\Mailroot\Pickup\"
ForAppending = 8
tempfilename = pickupdir & tempdir & filename
Set fs = CreateObject("Scripting.FileSystemObject")
Set a = fs.OpenTextFile(tempfilename, ForAppending, True)
a.Write(email)
a.Close
Set a = Nothing
fs.MoveFile tempfilename, pickupdir & filename
Set fs = Nothing
End Sub
%>

Phew.

Now, let's see the function calls in action:

<%
' use _ to use multiple lines in VBScript
email = genMIMEEmail("support@ti3.com", _ 
                     "sgd@ti3.com", _ 
                     "[Friday Freebie] Look, ma no CDONTS!", _ 
                     "This is the body of the email. I'm the company liability!")
WriteEmail email,GenMIMEName
%>

Gee, that was simple, wasn't it? In the next installment, we'll add in attachments.

Submitted by sgd on November 21, 2000 - 11:01.

Part 2 is now online. Sorry about the wait....

login or register to post comments

Great Article Series, Scott!

Submitted by Seth on May 24, 2001 - 08:13.

This is a great article! These concepts carry over into other development languages very easily, too. Thanks!

login or register to post comments

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

evolt.orgEvolt.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.