Having had lots of fun in the past writing VB to get under the covers of various Office applications, figured it can't be too hard to write custom mail filters for Outlook. So I spent a few hours to find out what is involved.
I do not claim that these results are sophisticated, or complex. I don't even claim that what's here is very useful as is. The idea is that since I spent a couple of hours to get this far, you, a reasonably intelligent programmer type, can take what's here and accomplish your custom mail filtering task in T minus a couple of hours, where T is the time you would have otherwise spent.
Off we go:
I wound up writing two functionally similar versions of the mail filter. The first version used the objects that are built-in to Outlook, and made available when you add a reference in your VB project to the "Microsoft Outlook 9.0 Object Library". I found that this version had certain shortcomings, such as I could not find a way to get access to SMTP headers, and I found that the HTMLBody property was not always populated with HTML as one would expect. The second version of the filter used MAPI objects which are available when you add a reference to the "Microsoft CDO 1.21 Library". Both versions are below, the second one being shown first.
To use this code, in Outlook Tools->Macro->Visual Basic Editor, paste the code below into the built-in project called "ThisOutlookSession". Use Tools->References in the VB editor to select libraries required as noted above.
To digitally sign the VBA so that Outlook will start without issuing that pesky macro warning:
MSDN documentation provided with VB6, and the Outlook programmer docs provided with Office were the essential references. Parts of the code below were cribbed directly from those references.
Here's the code. Have fun!
'One Outlook object to catch events
Dim WithEvents objInboxItems As Outlook.Items
'The rest is CDO stuff:
Dim objSession As MAPI.Session
Dim objInbox As MAPI.Folder
Dim objJunk As MAPI.Folder
'This causes the filtering to start when we launch Outlook.
Private Sub Application_Startup()
StartVBAFiltering
End Sub
' Run this code to start things up.
Sub StartVBAFiltering()
Dim objNameSpace As Outlook.NameSpace
Dim objInboxFolder As Outlook.MAPIFolder
'Instantiate Outlook objInboxItems.. this is the thing that gets the
' new item events.
Set objNameSpace = Application.Session
Set objInboxFolder = objNameSpace.GetDefaultFolder(olFolderInbox)
Set objInboxItems = objInboxFolder.Items
'Initialize the CDO stuff.
Set objSession = CreateObject("MAPI.Session")
'Assume there is an existing session to connect to:
objSession.Logon newSession:=False
Set objInbox = objSession.Inbox
'Of course the Junk Mail folder is assumed to exist in advance:
Set objJunk = objInbox.Folders.Item("Junk Mail")
'Run filters once at startup:
CDOfilterUnreadItems
End Sub
' Run this code to stop.
Sub StopVBAFiltering()
Set objInboxItems = Nothing
End Sub
'the event handler for ItemAdd
Private Sub objInboxItems_ItemAdd(ByVal Item As Object)
CDOfilterUnreadItems
End Sub
'Using CDO instead of the Outlook objects seems to give slighly better
'results. The data is more raw (ie. no fine categorizations such as HTMLBody)
'but seems more complete (ie I can get access to the message envelope).
'The VB reference selected to enable the use of the MAPI objects was called
'"Microsoft CDO 1.21 Library"
'Note that every time invoked we will look at all unread messages, so
'some messages will be filtered more than once.
Sub CDOfilterUnreadItems()
Dim objMessages As MAPI.Messages
Dim objMessage As MAPI.Message
On Error GoTo CDO_error
Set objMessages = objInbox.Messages
For Each objMessage In objMessages
If objMessage.UnRead Then
cdo_filter objMessage
End If
Next
Exit Sub
CDO_error:
MsgBox "Error in CDOfilterUnreadItems."
End Sub
Private Sub cdo_filter(msg As MAPI.Message)
cdo_dump_to_file msg
' Add CDO filter procs here
cdo_TextFindNMove msg, "I send you this file in order to have your advice", objJunk
cdo_TextFindNMove msg, "Connects Up To 100 Participants!", objJunk
End Sub
' Write interesting fields from a message to a disk file
Private Sub cdo_dump_to_file(msg As MAPI.Message)
Dim report As String
Dim fields_coll As MAPI.Fields
Dim my_field As MAPI.Field
Dim my_address As MAPI.AddressEntry
Dim DumpFile
Set fields_coll = msg.Fields
report = "***************mail record starts here******************" + vbCrLf
'Referencing things that are undefined will cause errors, so:
On Error GoTo next1
Set my_field = fields_coll.Item(CdoPR_TRANSPORT_MESSAGE_HEADERS)
report = report + "**Message Header:**" + vbCrLf + my_field.Value + vbCrLf
next1:
On Error GoTo next2
If msg.Sent Then
report = report + "**Sender**: " + vbCrLf + msg.Sender.Address + vbCrLf
End If
next2:
'todo: add logging for more fields
report = report + "**Subject**: " + vbCrLf + msg.subject + vbCrLf
report = report + "**Text**: " + vbCrLf + msg.text + vbCrLf
'Note that we will often get more than one message logged per file..
DumpFile = "C:\CDO_MAIL_DUMP_" & Format(Date, "ddmmyyyy") & "_" & Format(time, "hhmmss") & ".txt"
Open DumpFile For Append As #1
Print #1, report
Close #1
End Sub
' Look for a text string in a message body and if found, move the message to dest_folder
Private Sub cdo_TextFindNMove(msg As MAPI.Message, target As String, dest_folder As MAPI.Folder)
Dim Loc As Long
Loc = InStr(1, msg.text, target, vbTextCompare)
If Loc Then
msg.MoveTo (dest_folder.ID)
End If
End Sub
Dim WithEvents objInboxItems As Outlook.Items
'Dim objDestinationFolder As Outlook.MAPIFolder
'This causes the filtering to startup when we launch Outlook.
Private Sub Application_Startup()
StartVBAFiltering
End Sub
' Run this code to start your rule.
Sub StartVBAFiltering()
Dim objNameSpace As Outlook.NameSpace
Dim objInboxFolder As Outlook.MAPIFolder
Set objNameSpace = Application.Session
Set objInboxFolder = objNameSpace.GetDefaultFolder(olFolderInbox)
Set objInboxItems = objInboxFolder.Items
'Set objDestinationFolder = objInboxFolder.Folders("Temp")
End Sub
' Run this code to stop your rule.
Sub StopVBAFiltering()
Set objInboxItems = Nothing
End Sub
' This code is the actual rule.
Private Sub objInboxItems_ItemAdd(ByVal Item As Object)
HTMLFindNReplace Item, "myemail@cs.washington.edu" 'prevent a widely used method of address validation
BodyFindNMove Item, "I send you this file in order to have your advic" 'code red still popping up..
End Sub
'Search for the target string in the Body part of the item.
'if found, move the item to the "Junk Mail" folder (which is assumed to exist in advance),
'and dump the mail to a text file.
Private Sub BodyFindNMove(myItem As Object, target As String)
Dim Loc As Long
Dim myNameSpace As Outlook.NameSpace
Dim myInbox As Outlook.MAPIFolder
Dim myDestFolder As Outlook.MAPIFolder
Loc = InStr(1, myItem.Body, target, vbTextCompare)
If Loc Then
DumpItemToFile myItem
Set myNameSpace = Application.Session
Set myInbox = myNameSpace.GetDefaultFolder(olFolderInbox)
Set myDestFolder = myInbox.Folders("Junk Mail")
'MsgBox ("Warning! The target string: " + target + " was found in the message with " + _
' "subject: " + myItem.subject + " The item will be moved to the junk mail folder.")
myItem.Move myDestFolder
End If
End Sub
'Search for the target string in the HTMLBody part of Item. If found,
'warn, replace with a string of descriptive text, and dump the mail to a text file.
Private Sub HTMLFindNReplace(myItem As Object, target As String)
Dim Loc As Long
Loc = InStr(1, myItem.HTMLBody, target, vbTextCompare)
If Loc Then
DumpItemToFile myItem
MsgBox ("Warning! The target string: " + target + " was found in the message with " + _
"subject: " + myItem.subject + " The string will be replaced.")
myItem.HTMLBody = Replace(myItem.HTMLBody, target, "***A string here was replaced by your friendly VBA mailfilter***")
myItem.Save
End If
End Sub
'dump some of the interesting fields of a mail item to a text file at C:\
Private Sub DumpItemToFile(Item As Object)
Dim report As String
Dim DumpFile
report = ""
report = report + "**To**: " + vbCrLf + Item.To + vbCrLf
report = report + "**SenderName**: " + vbCrLf + Item.SenderName + vbCrLf
report = report + "**Subject**: " + vbCrLf + Item.subject + vbCrLf
report = report + "**SentOnBehalfOfName**: " + vbCrLf + Item.SentOnBehalfOfName + vbCrLf
report = report + "**ReplyRecipientNames**: " + vbCrLf + Item.ReplyRecipientNames + vbCrLf
report = report + "**ReceivedOnBehalfOfName**: " + vbCrLf + Item.ReceivedOnBehalfOfName + vbCrLf
report = report + "**ReceivedByName**: " + vbCrLf + Item.ReceivedByName + vbCrLf
report = report + "**ReceivedByEntryID**: " + vbCrLf + Item.ReceivedByEntryID + vbCrLf
report = report + "**OutlookVersion**: " + vbCrLf + Item.OutlookVersion + vbCrLf
report = report + "**MessageClass**: " + vbCrLf + Item.MessageClass + vbCrLf
report = report + "**HTMLBody**: " + vbCrLf + Item.HTMLBody + vbCrLf
report = report + "**EntryID**: " + vbCrLf + Item.EntryID + vbCrLf
report = report + "**ConversationTopic**: " + vbCrLf + Item.ConversationTopic + vbCrLf
report = report + "**Companies**: " + vbCrLf + Item.Companies + vbCrLf
report = report + "**CC**: " + vbCrLf + Item.CC + vbCrLf
report = report + "**Categories**: " + vbCrLf + Item.Categories + vbCrLf
report = report + "**BCC**: " + vbCrLf + Item.BCC + vbCrLf
report = report + "**Body**: " + vbCrLf + Item.Body + vbCrLf
DumpFile = "C:\MAIL_DUMP_" & Format(Date, "ddmmyyyy") & "_" & Format(time, "hhmmss") & ".txt"
Open DumpFile For Output As #1
Print #1, report
Close #1
End Sub