represents the id of the e-mail address in your database which is related to the bounce.
Now it is important to understand that there are two types of bounces: hard and soft. Permanent failures, such as a nonexistent account or domain, are considered hard bounces. Other failures, such as a full mailbox or blocked domain, are considered soft bounces. Instead of flagging your addresses as good or bad, your database can keep a running count of hard and soft bounces for each address. That way, your mailing application can be more intelligent about determining which addresses to exclude from future mailings. For example you might only want to send mail to any addresses with less than 8 soft bounces and less than two hard bounces. I usually do not like to exclude someone from future mailings unless they have more than one hard bounce. Just to be sure that the address is really invalid, I look for at least two hard bounces.
Your application will have to scan the text of the bounced messages looking for phrases that indicate the reason for the bounce. It will look for such phrases as "delivery failure", "box full", etc... (The downloadable sample code includes a database of the phrases we have discovered in typical bounced messages.) Your app will determine if each bounce is hard or soft based on the phrase it finds in the message.
Once your app determines if the bounce is hard or soft, it can increment the bounce_hard and bounce_soft fields in the database accordingly. It can then delete the message from the bounce box. If your app can not determine if the message is a hard or soft bounce the message can be left in the bounce box. Periodically the messages remaining in the bounce box can be analyzed by a human who can visually determine why they were not identified by the phrase scanner algorithm. The algorithm can then be updated to catch this type of message. Once your app is run again, it should handle this message properly and clear it from the bounce box. As time goes on, your phrase scanning algorithm should improve more and more. If you start with the phrases included with the downloadable sample code, your app should immediately id just about every bounced messages.
The Samples
The following VB Script samples interface with an Access database that contains the e-mail addresses. The second sample also interfaces with an XML file that contains the phrases typically found in bounced messages. The downloadable code includes the source code shown below along with the Access and XML files. The samples listed on this page vary slightly from the downloadable code, as the code below has been edited to fit the newsletter format.
SAMPLE 1: Constructing and sending the message...
In this sample, we will send a message with a friendly address in the From: header, and our bounce address specified as the reverse-path. This example uses VB Script and the EasyMail SMTP object. The The SMTP object contains a FromAddr property, and by default the SMTP object will use the value specified by this property for both the reverse-path and automatic creation of the From: header. We will override this behavior by setting the OptionFlags property to 1 which turns off the automatic creation of the From: header. We will then create the From: header ourselves with the AddCustomHeader() method.
'To do: Set the following variables:
strLicenseKey = "Newsletter Sample/02V4BFDSFFDFSD62"
strMailServer="mail.yourdomain.com"
strBounceBoxDomain="yourdomain.com"
strFriendlyFromName="Joe Sender"
strFriendlyFromAddress="joe.sender@domain.com"
'End To Do
Dim objSMTP, Data, RS, nRetVal
'create EasyMail SMTP object and set basic properties
Set objSMTP = CreateObject("EasyMail.SMTP")
objSMTP.LicenseKey = strLicenseKey
objSMTP.MailServer = strMailServer
objSMTP.OptionFlags = 1
objSMTP.AddCustomHeader "From", _
"""" & strFriendlyFromName & """" &_
" <" & strFriendlyFromAddress & ">"
objSMTP.Subject = "Subject..."
objSMTP.BodyText = "Message text"
'setup database and select addresses.
'This sample uses a access database.
Set cnnData = CreateObject("ADODB.Connection")
strConnection = "DBQ=email_database.mdb"
cnnData.Open "DRIVER=" &_
"{Microsoft Access Driver (*.mdb)};" &_
strConnection
Set RS = CreateObject("ADODB.RecordSet")
RS.Open "SELECT hard_bounces,id, name, address" &_
" FROM email_table" &_
" where hard_bounces < 2" &_
" and soft_bounces < 4", cnnData, 1, 3"
'send to each address selected
Do While RS.EOF = False
'encode record id in from address
objSMTP.FromAddr = "bounce_" & RS("id") &_
"@" & strBounceBoxDomain
objSMTP.AddRecipient RS("name"), RS("address"), 1
nRetVal = objSMTP.Send
'if the recipients address fails right
'away then we mark it as a hard bounce now.
If nRetVal = 8 Then
RS("hard_bounces") = RS("hard_bounces") + 1
End If
'remove the recipients
objSMTP.Clear 1
RS.MoveNext
Loop
'free remaining resources
RS.Close
cnnData.Close
Sample 2: Scanning the bounced messages and updating your database...
This sample uses the EasyMail POP3 object to download each message in our bounce box. Each message is parsed and the body text is scanned for specific phrases to determine if the message is a hard or a soft bounce. Once the code determines the type of bounce, it parses the id off of the To: address which identifies the address in our database. If the To: address does not begin with "bounce" it scans the received headers for the bounce address by using the TimeStamps collection. The sample then updates the bounce_soft and bounce_hard fields in the database accordingly before deleting the message from the bounce box. If the type of bounce can not be determined it is left in the bounce box for human analysis which will be used to improve the phrase scanning code in the future. The phrases used to identify bounced messages are read from an XML file.
'To do: Set the following variables:
strLicenseKey = "Newsletter Sample/02E00220B529204B62"
strMailServer= "mail.yourdomain.com"
strAccount= "bounce_account"
strPassword= "bounce_password"
'End To Do
Main
Sub Main()
Dim objPOP3, nCnt
Dim nBounceType, nId, nPos1, nPos2
Dim strBodyText, strToAddr, nOrdinal
Dim strConnection, nRetVal
'create the EasyMail POP3 object and assign
'the basic properties
Set objPOP3 = CreateObject("EasyMail.POP3")
objPOP3.LicenseKey = strLicenseKey
objPOP3.MailServer = strMailServer
objPOP3.Account = strAccount
objPOP3.Password = strPassword
'connect to the mail server
nRetVal = objPOP3.Connect()
If Not nRetVal = 0 Then
MsgBox "Error connecting to mail server."
exit sub
End If
'prepare the database and select our e-mail table
Set cnnData = CreateObject("ADODB.Connection")
strConnection = "DBQ=email_database.mdb"
cnnData.Open "DRIVER=" &_
"{Microsoft Access Driver (*.mdb)};" &_
strConnection
Set rs = CreateObject("ADODB.RecordSet")
rs.Open "SELECT * FROM email_table", cnnData, 1, 3
'get the count of messages waiting in the
'bounce box and download and process each one
nCnt = objPOP3.GetDownloadableCount()
For x = 1 To nCnt
nOrdinal = objPOP3.DownloadSingleMessage(x)
If nOrdinal < 0 Then
MsgBox "There was an error downloading " &_
"the message. " & nOrdinal
exit sub
End If
strBodyText = objPOP3.Messages(nOrdinal).BodyText
'get id from To: address
set objMsgs = objPOP3.Messages
For Each Recip In objMsgs(nOrdinal).Recipients
strToAddr = Recip.Address
If LCase(Left(strToAddr, 6)) = "bounce" Then
Exit For
End if
Next
'if address is not found then try searching
'timestamps (AKA received headers)
If Not LCase(Left(strToAddr, 6)) = "bounce" Then
For Each TimeS In objMsgs(nOrdinal).Timestamps
strToAddr = TimeS.For
If LCase(Left(strToAddr, 6)) = "bounce" Then
Exit For
End if
Next
End If
'if it is a bounce message we will process it
If Left(strToAddr, 6) = "bounce" And _
InStr(strToAddr, "_") Then
nPos1 = InStr(strToAddr, "_") + 1
nPos2 = InStr(strToAddr, "@")
If nPos2 > nPos1 Then
nId = Mid(strToAddr, nPos1, nPos2 - nPos1)
End If
'call the IdentifyBounce routing which scans
'the bodytext for the phrases found in our
'xml file
nBounceType = IdentifyBounce(strBodyText)
If nBounceType > 0 Then
'the message has been identified as a hard
'or soft bounce so update the database
rs.Find ("id=" & nId)
If rs.EOF = False and rs.BOF=False Then
If nBounceType = 1 Then
rs("soft_bounces")=rs("soft_bounces")+1
Else
rs("hard_bounces")=rs("hard_bounces")+1
End If
'update changes
rs.update
End If
'delete the message from the bounce box
objPOP3.DeleteSingleMessage x
elseif nBounceType = 0 then
'If nBounceType is 0 then it is a warning
'message or auto-responsea so we will
'delete the message from the bounce box.
objPOP3.DeleteSingleMessage x
End If
End If
'free resources used by the parsed message. This
'call does not delete messages from the server.
objPOP3.Messages.DeleteAll
Next
'disconnect from mail server
'and free remaining resources
objPOP3.Disconnect
rs.Close
msgbox "Operation Complete."
End sub
Function IdentifyBounce(strBodyText)
Set st = CreateObject("ADODB.Stream")
Set rs = CreateObject("ADODB.RecordSet")
st.Open
st.LoadFromFile ("bounce_signatures.xml")
rs.Open st
rs.Sort = "weight DESC"
IdentifyBounce = -1
Do While Not rs.EOF
If InStr(1, strBodyText, rs("signature"), _
vbTextCompare) Then
IdentifyBounce = rs("weight")
End If
rs.MoveNext
Loop
rs.Close
End Function
Conclusion
I hope you found this article useful in your efforts to clean your address list. If you have any suggestions for future topics, please let me know. You can find my contact information at the bottom of this page.
Bonus. Measuring failures from a specific mailing...
Some of our customers want to measure the count of delivery failures for each mailing they do. We showed you how to embed an id into the "reverse-path" so that it is easy to match the bounced message up with the address in your database, but you can even go a step further by inserting a mailing identifier as well.
Lets say you want to keep track of the number of bounced messages for a specific mailing, and lets assume that each mailing is represented by a row in a table. The row has a unique id field which is the mailing identifier. You can encode the mailing identifier onto the account portion of the reverse-path like this: bounce_1063_34@yourdomain.com, where 1063 is the id of the address and 34 is the id of the mailing. You can then modify your database update routine to flag the number of hard and soft bounces for each mailing as well as each address.
This article can be found at http://www.quiksoft.com/newsletter/issue001/
©2002 Quiksoft Corporation. All rights reserved. Unauthorized duplication or distribution prohibited. Quiksoft, EasyMail, EasyMail Objects, EasyMail .Net Edition, EasyMail Advanced API, EasyMail SMTP Express, and MailStore are trademarks of Quiksoft Corporation. Other trademarks mentioned are the property of their legal owner.