Microsoft KB Archive/324021

= How to create a &quot;catchall&quot; mailbox sink for Exchange Server =

Article ID: 324021

Article Last Modified on 12/3/2007

-

APPLIES TO


 * Microsoft Exchange 2000 Server Standard Edition
 * Microsoft Exchange Server 2003 Enterprise Edition
 * Microsoft Exchange Server 2003 Standard Edition
 * Microsoft Windows Small Business Server 2003 Premium Edition
 * Microsoft Windows Small Business Server 2003 Standard Edition

-



This article was previously published under Q324021





SUMMARY
This article describes how to create an event sink to capture all e-mail messages that are sent to a particular domain, and then direct them to a single mailbox.

Note The sample event sink described in this article redirects all e-mail messages that are sent to a domain. For information about how to create more complex event sinks, see the Exchange Software Development Kit (SDK).

Create the script files
Create the following five scripts, and then store them in a folder on the Exchange computer.

Microsoft provides programming examples for illustration only, without warranty either expressed or implied. This includes, but is not limited to, the implied warranties of merchantability or fitness for a particular purpose. This article assumes that you are familiar with the programming language that is being demonstrated and with the tools that are used to create and to debug procedures. Microsoft support engineers can help explain the functionality of a particular procedure, but they will not modify these examples to provide added functionality or construct procedures to meet your specific requirements.

Catchall.cmd
The Catchall.cmd file calls the SMTPReg.vbs script and registers the Catchall event sink. To create this .cmd file, follow these steps:   Type or paste the following code into a text editor, such as Notepad: cscript smtpreg.vbs /add 1 onarrival SMTPScriptingCatchAll CDO.SS_SMTPOnArrivalSink &quot;mail from=*&quot;

cscript smtpreg.vbs /setprop 1 onarrival SMTPScriptingCatchAll Sink ScriptName d:\mec\catchall\catchall.vbs

cscript smtpreg.vbs /delprop 1 onarrival SMTPScriptingCatchAll Source Rule Note Modify the path to the Catchall.vbs file to reflect the folder location of the Catchall files.  Save the file as Catchall.cmd .

Enum.cmd
Run Enum.cmd to list event sinks that are registered on the server. To create this file, follow these steps:   Type or paste the following code into a text editor, such as Notepad: cscript smtpreg.vbs /enum |more  Save the file as Enum.cmd .

Catchall.vbs
The Catchall.vbs script is used to create the Catchall account. Customize this file for your Exchange Server environment.   Type or paste the following code into a text editor, such as Notepad. Save this file as Catchall.vbs. <SCRIPT LANGUAGE=&quot;VBSCRIPT&quot;> ' ' For information about this namespace, see '  http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cdosys/html/_cdosys_schema_smtpenvelope.asp ' Const RECIP_LIST = &quot;http://schemas.microsoft.com/cdo/smtpenvelope/recipientlist&quot; ' ' For information about the CdoEventStatus enumeration, see '  http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cdosys/html/_cdosys_cdoeventstatus_enum.asp ' Const CDO_RUN_NEXT_SINK = 0 ' ' OnArrival sink entry point ' Sub ISMTPOnArrival_OnArrival(ByVal Msg, EventStatus) On Error Resume Next Dim objFields Set objFields = Msg.EnvelopeFields objFields(RECIP_LIST).Value = FixupRecipList(objFields(RECIP_LIST).Value) objFields.Update Msg.DataSource.Save ' Commit changes EventStatus = CDO_RUN_NEXT_SINK End Sub ' ' Change any @example.com recipient(s) to bob@company.com ' Function FixupRecipList(strList) On Error Resume Next Dim strFixedList Dim nDomainPart Dim nNamePart Dim nNextAddress strFixedList = strList While (InStr(LCase(strFixedList),&quot;@example.com&quot;)) nDomainPart = InStr(LCase(strFixedList),&quot;@example.com&quot;) nNamePart = InStrRev(strFixedList,&quot;;&quot;,nDomainPart) nNextAddress = InStr(nDomainPart+Len(&quot;@example.com;&quot;),strFixedList,&quot;SMTP:&quot;) If (0 = nNamePart) Then ' @example.com is first name in recipient list If (0 = nNextAddress) Then ' @example.com is the last name in the recipient list strFixedList = &quot;SMTP:bob@company.com;&quot; Else ' @example.com is not the last name in the recipient list strFixedList = &quot;SMTP:bob@company.com;&quot; & Right(strFixedList,Len(strFixedList)-nNextAddress+1) End If   Else ' @example.com is not the first name in recipient list If (0 = nNextAddress) Then ' @example.com is the last name in the recipient list strFixedList = Left(strFixedList,nNamePart) & &quot;SMTP:bob@company.com;&quot; Else ' @example.com is not the last name in the recipient list strFixedList = Left(strFixedList,nNamePart) & &quot;SMTP:bob@company.com;&quot; & Right(strFixedList,Len(strFixedList)-nNextAddress+1) End If   End If  Wend FixupRecipList = strFixedList End Function

</SCRIPT> </li> Edit the Catchall.vbs file to replace occurrences of @example.com with @, where   is the domain from which you want to redirect the e-mail messages.</li> Replace all occurrences of bob@company.com with the SMTP address of the mailbox to which you want to redirect all e-mail messages for the domain that you specified in step 2.

Note The e-mail address to which you want to redirect all mail must be from a different domain than the domain from which you want to redirect the e-mail messages. For example, if the domain that you specify in step 2 is @, the e-mail address that you specify in step 3 cannot be @. If the domains are the same, the message will continuously loop and will eventually be returned to the sender as undeliverable.

If the recipient must have an e-mail address in the catchall domain (the domain from which you want to redirect the e-mail messages), such as, add an additional domain such as @  to the recipient policy for the user, and then add a SMTP address of   to the user's e-mail addresses. The address of  can then be used as the e-mail address to specify in step 3.</li></ol>

SMTPReg.vbs
Create a script to register the Catchall event sink. To do this, follow these steps:   Type or paste the following code into a text editor, such as Notepad: 'THIS CODE AND INFORMATION IS PROVIDED &quot;AS IS&quot; WITHOUT 'WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, 'INCLUDING BUT NOT LIMITED TO THE IMPLIED WARRANTIES 'OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR 'PURPOSE

'-- 'FILE DESCRIPTION: Script for registering for SMTP Protocol sinks. ' 'File Name: smtpreg.vbs ' ' ' Copyright (c) Microsoft Corporation 1993-1998. All rights reserved. '-- Option Explicit

' ' ' the OnArrival event GUID Const GUIDComCatOnArrival = &quot;{ff3caa23-00b9-11d2-9dfb-00C04FA322BA}&quot; ' the SMTP source type Const GUIDSourceType = &quot;{FB65C4DC-E468-11D1-AA67-00C04FA345F6}&quot; ' Const GUIDCat = &quot;{871736c0-fd85-11d0-869a-00c04fd65616}&quot; Const GUIDSources = &quot;{DBC71A31-1F4B-11d0-869D-80C04FD65616}&quot;

' the SMTP service display name. This is used to key which service to ' edit Const szService = &quot;smtpsvc&quot;

' the event manager object. This is used to communicate with the ' event binding database. Dim EventManager Set EventManager = WScript.CreateObject(&quot;Event.Manager&quot;)

' ' register a new sink with event manager ' ' iInstance - the instance to work against ' szEvent - OnArrival ' szDisplayName - the display name for this new sink ' szProgID - the progid to call for this event ' szRule - the rule to set for this event ' public sub RegisterSink(iInstance, szEvent, szDisplayName, szProgID, szRule) Dim SourceType Dim szSourceDisplayName Dim Source Dim Binding Dim GUIDComCat Dim PrioVal

' figure out which event they are trying to register with and set ' the comcat for this event in GUIDComCat select case LCase(szEvent) case &quot;onarrival&quot; GUIDComCat = GUIDComCatOnArrival case else WScript.echo &quot;invalid event: &quot; & szEvent exit sub end select ' enumerate through each of the registered instances for the SMTP source ' type and look for the display name that matches the instance display ' name set SourceType = EventManager.SourceTypes(GUIDSourceType) szSourceDisplayName = szService & &quot; &quot; & iInstance for each Source in SourceType.Sources if Source.DisplayName = szSourceDisplayName then ' You have found the instance that you want. Now add a new binding ' with the right event GUID. by not specifying a GUID to the ' Add method you get server events to create a new ID for this ' event set Binding = Source.GetBindingManager.Bindings(GUIDComCat).Add(&quot;&quot;) ' set the binding properties Binding.DisplayName = szDisplayName Binding.SinkClass = szProgID ' register a rule with the binding Binding.SourceProperties.Add &quot;Rule&quot;, szRule ' register a priority with the binding PrioVal = GetNextPrio(Source, GUIDComCat) If PrioVal < 0 then WScript.Echo &quot;assigning priority to default value (24575)&quot; Binding.SourceProperties.Add &quot;Priority&quot;, 24575 else WScript.Echo &quot;assigning priority (&quot; & PrioVal & &quot; of 32767)&quot; Binding.SourceProperties.Add &quot;Priority&quot;, PrioVal end if           ' save the binding Binding.Save WScript.Echo &quot;registered &quot; & szDisplayName exit sub end if   next end sub

' ' iterate through the bindings in a source, find the binding ' with the lowest priority, and return the next priority value. ' If the next value exceeds the range, return -1. ' public function GetNextPrio(oSource, GUIDComCat) ' it's possible that priority values will not be   ' numbers, so you add error handling for this case on error resume next

Dim Bindings Dim Binding Dim nLowestPrio Dim nPrioVal nLowestPrio = 0 set Bindings = oSource.GetBindingManager.Bindings(GUIDComCat) ' if the bindings collection is empty, then this is the first ' sink. It receives the highest priority (0). if Bindings.Count = 0 then GetNextPrio = 0 else ' get the lowest existing priority value for each Binding in Bindings nPrioVal = Binding.SourceProperties.Item(&quot;Priority&quot;) if CInt(nPrioVal) > nLowestPrio then if err.number = 13 then err.clear else nLowestPrio = CInt(nPrioVal) end if           end if        next ' assign priority values in increments of 10 so priorities ' can be shuffled later without the need to reorder all ' binding priorities. Valid priority values are 0 - 32767 if nLowestPrio + 10 > 32767 then GetNextPrio = -1 else GetNextPrio = nLowestPrio + 10 end if   end if end function

' ' Search for a previously registered sink with the passed in name ' ' iInstance - the instance to work against ' szEvent - OnArrival ' szDisplayName - the display name of the event to check ' bCheckError - Any errors returned public sub CheckSink(iInstance, szEvent, szDisplayName, bCheckError) Dim SourceType Dim GUIDComCat Dim szSourceDisplayName Dim Source Dim Bindings Dim Binding

bCheckError = FALSE select case LCase(szEvent) case &quot;onarrival&quot; GUIDComCat = GUIDComCatOnArrival case else WScript.echo &quot;invalid event: &quot; & szEvent exit sub end select

' find the source for this instance set SourceType = EventManager.SourceTypes(GUIDSourceType) szSourceDisplayName = szService & &quot; &quot; & iInstance for each Source in SourceType.Sources if Source.DisplayName = szSourceDisplayName then ' find the binding by display name. to do this, enumerate ' all of the bindings and try to match on the display name set Bindings = Source.GetBindingManager.Bindings(GUIDComCat) for each Binding in Bindings if Binding.DisplayName = szDisplayName then ' you have found the binding, now log an error WScript.Echo &quot;Binding with the name &quot; & szDisplayName & &quot; already exists&quot; exit sub end if           next end if   next bCheckError = TRUE end sub

' ' unregister a previously registered sink ' ' iInstance - the instance to work against ' szEvent - OnArrival ' szDisplayName - the display name of the event to remove ' public sub UnregisterSink(iInstance, szEvent, szDisplayName) Dim SourceType Dim GUIDComCat Dim szSourceDisplayName Dim Source Dim Bindings Dim Binding

select case LCase(szEvent) case &quot;onarrival&quot; GUIDComCat = GUIDComCatOnArrival case else WScript.echo &quot;invalid event: &quot; & szEvent exit sub end select

' find the source for this instance set SourceType = EventManager.SourceTypes(GUIDSourceType) szSourceDisplayName = szService & &quot; &quot; & iInstance for each Source in SourceType.Sources if Source.DisplayName = szSourceDisplayName then ' find the binding by display name. to do this, enumerate ' all of the bindings and try to match on the display name set Bindings = Source.GetBindingManager.Bindings(GUIDComCat) for each Binding in Bindings if Binding.DisplayName = szDisplayName then ' you have found the binding, now remove it                   Bindings.Remove(Binding.ID) WScript.Echo &quot;removed &quot; & szDisplayName & &quot; &quot; & Binding.ID               end if            next end if   next end sub

' ' add or remove a property from the source or sink propertybag for an event ' ' iInstance - the SMTP instance to edit ' szEvent - the event type (OnArrival) ' szDisplayName - the display name of the event ' szPropertyBag - the property bag to edit (&quot;source&quot; or &quot;sink&quot;) ' szOperation - &quot;add&quot; or &quot;remove&quot; ' szPropertyName - the name to edit in the property bag ' szPropertyValue - the value to assign to the name (ignored for remove) ' public sub EditProperty(iInstance, szEvent, szDisplayName, szPropertyBag, szOperation, szPropertyName, szPropertyValue) Dim SourceType Dim GUIDComCat Dim szSourceDisplayName Dim Source Dim Bindings Dim Binding Dim PropertyBag

select case LCase(szEvent) case &quot;onarrival&quot; GUIDComCat = GUIDComCatOnArrival case else WScript.echo &quot;invalid event: &quot; & szEvent exit sub end select

' find the source for this instance set SourceType = EventManager.SourceTypes(GUIDSourceType) szSourceDisplayName = szService & &quot; &quot; & iInstance for each Source in SourceType.Sources if Source.DisplayName = szSourceDisplayName then set Bindings = Source.GetBindingManager.Bindings(GUIDComCat) ' find the binding by display name. to do this, enumerate ' all of the bindings and try to match on the display name for each Binding in Bindings if Binding.DisplayName = szDisplayName then ' figure out which set of properties you want to modify ' based on the szPropertyBag parameter select case LCase(szPropertyBag) case &quot;source&quot; set PropertyBag = Binding.SourceProperties case &quot;sink&quot; set PropertyBag = Binding.SinkProperties case else WScript.echo &quot;invalid propertybag: &quot; & szPropertyBag exit sub end select ' figure out what operation you want to perform select case LCase(szOperation) case &quot;remove&quot; ' they want to remove szPropertyName from the ' property bag PropertyBag.Remove szPropertyName WScript.echo &quot;removed property &quot; & szPropertyName case &quot;add&quot; ' add szPropertyName to the property bag and ' set its value to szValue. if this value ' already exists then this will change the value ' it to szValue. PropertyBag.Add szPropertyName, szPropertyValue WScript.echo &quot;set property &quot; & szPropertyName & &quot; to &quot; & szPropertyValue case else WScript.echo &quot;invalid operation: &quot; & szOperation exit sub end select ' save the binding Binding.Save end if           next end if   next end sub

' ' this helper function takes an IEventSource object and a event category ' and dumps all of the bindings for this category under the source ' ' Source - the IEventSource object to display the bindings for ' GUIDComCat - the event category to display the bindings for ' public sub DisplaySinksHelper(Source, GUIDComCat) Dim Binding Dim propval ' walk each of the registered bindings for this component category for each Binding in Source.GetBindingManager.Bindings(GUIDComCat) ' display the binding properties WScript.echo &quot;   Binding &quot; & Binding.ID & &quot; {&quot; WScript.echo &quot;     DisplayName = &quot; & Binding.DisplayName WScript.echo &quot;     SinkClass = &quot; & Binding.SinkClass if Binding.Enabled = True then WScript.echo &quot;     Status = Enabled&quot; else WScript.echo &quot;     Status = Disabled&quot; end if

' walk each of the source properties and display them WScript.echo &quot;     SourceProperties {&quot; for each propval in Binding.SourceProperties WScript.echo &quot;       &quot; & propval & &quot; = &quot; & Binding.SourceProperties.Item(propval) next WScript.echo &quot;     }&quot;

' walk each of the sink properties and display them WScript.echo &quot;     SinkProperties {&quot; for each Propval in Binding.SinkProperties WScript.echo &quot;       &quot; & propval & &quot; = &quot; & Binding.SinkProperties.Item(Propval) next WScript.echo &quot;     }&quot; WScript.echo &quot;   }&quot; next end sub

' ' dumps all of the information in the binding database related to SMTP ' public sub DisplaySinks Dim SourceType Dim Source

' look for each of the sources registered for the SMTP source type set SourceType = EventManager.SourceTypes(GUIDSourceType) for each Source in SourceType.Sources ' display the source properties WScript.echo &quot;Source &quot; & Source.ID & &quot; {&quot; WScript.echo &quot; DisplayName = &quot; & Source.DisplayName ' display all of the sinks registered for the OnArrival event WScript.echo &quot; OnArrival Sinks {&quot; call DisplaySinksHelper(Source, GUIDComCatOnArrival) WScript.echo &quot; }&quot; next end sub

' ' enable/disable a registered sink ' ' iInstance - the instance to work against ' szEvent - OnArrival ' szDisplayName - the display name for this new sink ' public sub SetSinkEnabled(iInstance, szEvent, szDisplayName, szEnable) Dim SourceType Dim GUIDComCat Dim szSourceDisplayName Dim Source Dim Bindings Dim Binding

select case LCase(szEvent) case &quot;onarrival&quot; GUIDComCat = GUIDComCatOnArrival case else WScript.echo &quot;invalid event: &quot; + szEvent exit sub end select

' find the source for this instance set SourceType = EventManager.SourceTypes(GUIDSourceType) szSourceDisplayName = szService + &quot; &quot; + iInstance for each Source in SourceType.Sources if Source.DisplayName = szSourceDisplayName then ' find the binding by display name. to do this, enumerate ' all of the bindings and try to match on the display name set Bindings = Source.GetBindingManager.Bindings(GUIDComCat) for each Binding in Bindings if Binding.DisplayName = szDisplayName then ' You have found the binding, now enable/disable it                   ' You do not need &quot;case else' because szEnable's value ' is set internally, not by users select case LCase(szEnable) case &quot;true&quot; Binding.Enabled = True Binding.Save WScript.Echo &quot;enabled &quot; + szDisplayName + &quot; &quot; + Binding.ID                       case &quot;false&quot; Binding.Enabled = False Binding.Save WScript.Echo &quot;disabled &quot; + szDisplayName + &quot; &quot; + Binding.ID                       end select end if           next end if   next end sub

' ' display usage information for this script ' public sub DisplayUsage WScript.echo &quot;usage: cscript smtpreg.vbs &quot; WScript.echo &quot; commands:&quot; WScript.echo &quot;   /add <Instance> <Event> <DisplayName> <SinkClass> <Rule>&quot; WScript.echo &quot;   /remove <Instance> <Event> <DisplayName>&quot; WScript.echo &quot;   /setprop <Instance> <Event> <DisplayName> <PropertyBag> <PropertyName> &quot; WScript.echo &quot;            <PropertyValue>&quot; WScript.echo &quot;   /delprop <Instance> <Event> <DisplayName> <PropertyBag> <PropertyName>&quot; WScript.echo &quot;   /enable <Instance> <Event> <DisplayName>&quot; WScript.echo &quot;   /disable <Instance> <Event> <DisplayName>&quot; WScript.echo &quot;   /enum&quot; WScript.echo &quot; arguments:&quot; WScript.echo &quot;   <Instance> is the SMTP instance to work against&quot; WScript.echo &quot;   <Event> can be OnArrival&quot; WScript.echo &quot;   <DisplayName> is the display name of the event to edit&quot; WScript.echo &quot;   <SinkClass> is the sink class for the event&quot; WScript.echo &quot;   <Rule> is the rule to use for the event&quot; WScript.echo &quot;   <PropertyBag> can be Source or Sink&quot; WScript.echo &quot;   <PropertyName> is the name of the property to edit&quot; WScript.echo &quot;   <PropertyValue> is the value to assign to the property&quot; end sub

Dim iInstance Dim szEvent Dim szDisplayName Dim szSinkClass Dim szRule Dim szPropertyBag Dim szPropertyName Dim szPropertyValue dim bCheck

' ' this is the main body of our script. it reads the command line parameters ' specified and then calls the appropriate function to perform the operation ' if WScript.Arguments.Count = 0 then call DisplayUsage else Select Case LCase(WScript.Arguments(0)) Case &quot;/add&quot; if not WScript.Arguments.Count = 6 then call DisplayUsage else iInstance = WScript.Arguments(1) szEvent = WScript.Arguments(2) szDisplayName = WScript.Arguments(3) szSinkClass = WScript.Arguments(4) szRule = WScript.Arguments(5) call CheckSink(iInstance, szEvent, szDisplayName, bCheck) if bCheck = TRUE then call RegisterSink(iInstance, szEvent, szDisplayName, szSinkClass, szRule) End if           end if        Case &quot;/remove&quot; if not WScript.Arguments.Count = 4 then call DisplayUsage else iInstance = WScript.Arguments(1) szEvent = WScript.Arguments(2) szDisplayName = WScript.Arguments(3) call UnregisterSink(iInstance, szEvent, szDisplayName) end if         Case &quot;/setprop&quot; if not WScript.Arguments.Count = 7 then call DisplayUsage else iInstance = WScript.Arguments(1) szEvent = WScript.Arguments(2) szDisplayName = WScript.Arguments(3) szPropertyBag = WScript.Arguments(4) szPropertyName = WScript.Arguments(5) szPropertyValue = WScript.Arguments(6) call EditProperty(iInstance, szEvent, szDisplayName, szPropertyBag, &quot;add&quot;, szPropertyName, szPropertyValue) end if       Case &quot;/delprop&quot; if not WScript.Arguments.Count = 6 then call DisplayUsage else iInstance = WScript.Arguments(1) szEvent = WScript.Arguments(2) szDisplayName = WScript.Arguments(3) szPropertyBag = WScript.Arguments(4) szPropertyName = WScript.Arguments(5) call EditProperty(iInstance, szEvent, szDisplayName, szPropertyBag, &quot;remove&quot;, szPropertyName, &quot;&quot;) end if       Case &quot;/enable&quot; if not WScript.Arguments.Count = 4 then call DisplayUsage else iInstance = WScript.Arguments(1) szEvent = WScript.Arguments(2) szDisplayName = WScript.Arguments(3) call SetSinkEnabled(iInstance, szEvent, szDisplayName, &quot;True&quot;) end if       Case &quot;/disable&quot; if not WScript.Arguments.Count = 4 then call DisplayUsage else iInstance = WScript.Arguments(1) szEvent = WScript.Arguments(2) szDisplayName = WScript.Arguments(3) call SetSinkEnabled(iInstance, szEvent, szDisplayName, &quot;False&quot;) end if       Case &quot;/enum&quot; if not WScript.Arguments.Count = 1 then call DisplayUsage else call DisplaySinks end if       Case Else call DisplayUsage End Select end if

</li> Save the file as Smtpreg.vbs .</li></ol>

Removecatchall.cmd
Create a .cmd file to remove (uninstall) the Catchall event sink if you think that you may want to remove it later. To do this, follow these steps:   Type or paste the following code into a text editor, such as Notepad: cscript smtpreg.vbs /remove 1 onarrival SMTPScriptingCatchAll </li> Save this file as Removecatchall.cmd.

Note If you want to remove the Catchall event sink, you can do so by running Removecatchall.cmd from a command prompt.</li></ol>

Register the catchall event sink

 * 1) Verify that you have created a valid e-mail account with which to collect the redirected e-mail messages.
 * 2) Run the Catchall.cmd from the directory that contains the .vbs files that you created.
 * 3) Restart the SMTP service in the Exchange System Manager.
 * 4) Test the event sink by sending an e-mail message to an e-mail address in the catchall domain that you specified in step 2 of the &quot;Catchall.vbs&quot; section of this article. The message is delivered to the recipient mailbox with the address that you specified in step 3 of that same article section.

<div class="references_section">