Build your own List Server

Aside from traveling around migrating companies to Exchange, I write articles to describe my findings in the field and run an online Outlook and Exchange resource site named OutlookExchange.com. Initially, I needed a way to allow anyone subscribe to the newsletter that is sent out once a week. The choice of manually entering data in the Active Directory was as appealing as a slobbering dog and my budget couldn’t afford a dog anyway. Moreover, I wanted to keep it in-house and have some control of the list itself.

Obviously, my Active Directory and Exchange 2000 signals went off and this infrastructure guy put on his fledgling development cap and went to work. I decided to give the Workflow Event sink a go since it could offer some flexibility later in respect to authorization and approval for deletions. I will first go over what the listserver workflow does and how to use it, and then I will give a brief overview of how to install and configure it. If there is some, I will go over some of the code as well.

To begin with, there is a new version of the Exchange Workflow Designer from Microsoft. The Office Developer CD for Office XP includes the new .NET flavor of the Exchange Workflow Designer. For this exercise, we will use the Exchange Workflow Designer that comes with Office 2000. With this version, the entire workflow script can be exported and imported as an XML file. We are providing that file as a downloadable resource associated with this article. I will walk you through the installation and setup.

So here is how it works; a user sends a message to a mail-enabled Exchange 2000 public folder. In order to be processed, the subject line of the message must contain the word subscribe followed by the name of the list they wish to join. The Workflow Event Sink then processes the new folder item by identifying the sender’s SMTP address and extracting the display name fields from the header. Using the extracted SMTP field, the Workflow Event Sink locates an AD Global Catalog and searches the email address field for a match. If a match exists, the sink adds the existing account to the distribution group. If there is no account with that match, the sink creates a new hidden contact in a specific AD OU that we identify in the code and then adds that new account to the group. The Sink then sends a confirmation to the sender that they have been added to the group. Unsubscribe works in the same manner only the code never removes a contact. Also, because the entries are written into the public folders, there is a record of all requests.

To get things setup, first we need to make sure you have the necessary rights to install and execute the workflow sink. Open the component services and verify that your account has permissions to install the code. In my example, I also create a new account named Workflow and gave it the necessary permissions to add and remove accounts in the Active Directory.

aFigure1

Using the Component Services, open the properties of the Workflow Sink and change the account information in the Identity tab to assign the workflow account’s credentials.

afigure2

In order to set up the Active Directory, simply create an OU named Lists in the root of your Active Directory (or modify the code in the workflow to point to a different OU). Next, we need to enable workflow in a mail-enabled folder and import the sample XML script. For my example, I used the Exchange System Manager to create a folder named Lists. I then modified the properties of the folder to mail-enable it. By default, anonymous has contributor rights so anyone can send mail to the folder from the Internet. In my example, lists@outlookexchange.com is used for my new subscription folder.

Now we need to workflow enable the folder and import the script. Using the Microsoft Workflow Designer for Exchange 2000 that comes on the Office 2000 Developers CD, connect to the public folder you created; public/lists in my eafigure3xample. Launch the Designer, and verify the server name, then type public/ in the folder box and click the browse button to view the folders.

 

afigure 4
Expand the  folder list and double-click the public/lists/ folder and click OK. Click OK again to accept the choice and connect to the folder.

You can either import the sample code or follow this process to manually recreate the list server process. To manually rebuild the process, create a new blank workflow process from the file menu and past the following code into the shared code section:

 

Function IsSubscribe()

Dim sTest

   IsSubscribe = False

   sTest = LCase(Trim(workflowsession.fields("urn:schemas:httpmail:subject").Value))

   If InStr(sTest, "subscribe") > 0 And InStr(sTest, "unsubscribe") = 0 Then IsSubscribe = True

End Function

'****************************************************************************************

Function IsUnSubscribe()

Dim sTest

   IsUnSubscribe = False

   sTest = LCase(Trim(workflowsession.fields("urn:schemas:httpmail:subject").Value))

   If InStr(sTest, "unsubscribe") > 0 Then IsUnSubscribe = True

End Function

'****************************************************************************************

Sub CreateContactAndSubscribeIt

On Error Resume Next

dim group

group = LCase(Trim(mid(workflowsession.fields("urn:schemas:httpmail:subject").Value, 10)))

dim folderemail

folderemail = "lists@theproexchange.com"

dim email

dim name

email =   workflowsession.fields( "urn:schemas:httpmail:fromemail" ).value

name =   workflowsession.fields( "urn:schemas:httpmail:fromname" ).value

Dim oConnection

Dim oRecordset

Dim strQuery

Dim stremail

Dim oCont

Dim oGC

Dim strADsPath

Set oCont = GetObject("GC:")

For Each oGC In oCont

  strADsPath = oGC.ADsPath

Next

Set oConnection = CreateObject("ADODB.Connection")

Set oRecordset = CreateObject("ADODB.Recordset")

oConnection.Provider = "ADsDSOObject"  'The ADSI OLE-DB provider

oConnection.Open "ADs Provider"

strQuery = "<" & strADsPath & ">;(&(objectCategory=person)(mail=" & email & "));mail,cn,distinguishedName;subtree"

Set oRecordset = oConnection.Execute(strQuery)

Set grp = GetObject("LDAP://CN="+group+", OU=lists, DC=BRYANTFAMILY, DC=COM") 

If oRecordset.EOF And oRecordset.BOF Then

   Set ou = GetObject("LDAP://OU=lists, DC=BRYANTFAMILY, DC=COM") 

   Set usr = ou.Create("contact", "CN="+email)

   usr.Put "description", "Created via email subscription"

   usr.Put "mail", email

   usr.Put "displayname", name

   usr.Put "internetencoding", "1310720"

   usr.Put "legacyExchangeDN", "/o=First Organization/ou=First Administrative Group//cn=Recipients/cn="+email

   usr.Put "mailnickname", email

   usr.Put "MapiRecipient", "FALSE"

   usr.Put "msExchAlObjectVersion", "23"

   usr.Put "msExchHideFromAddressLists", "TRUE"

   usr.Put "targetaddress", "SMTP:"+email

   usr.SetInfo

   grp.Add ("LDAP://CN="+email+", OU=lists, DC=BRYANTFAMILY, DC=COM") 

Else

  While Not oRecordset.EOF

     grp.Add ("LDAP://"+oRecordset.Fields("distinguishedName"))

     oRecordset.MoveNext

Wend

End If

Set oCont = Nothing

Set oGC = Nothing

Set oRecordset = Nothing

Set oConnection = Nothing

 set msg = createobject("CDO.message")

 msg.To = email

  msg.From = folderemail

  msg.subject = "Welcome to "+group+cn

  msg.textbody = name+", thanks for subscribing to "+group+". If for any reason you would like to unsubscribe, send a message to "+folderemail+" and place the word UNSUBSCRIBE in the subject line. Thanks again! "

  msg.send

End Sub

'****************************************************************************************

Sub DeleteContactAndUnSubscribeIt

On Error Resume Next

dim group

group = LCase(Trim(mid(workflowsession.fields("urn:schemas:httpmail:subject").Value, 12)))

dim folderemail

folderemail = "lists@theproexchange.com"

email =   workflowsession.fields( "urn:schemas:httpmail:fromemail" ).value

name =   workflowsession.fields( "urn:schemas:httpmail:fromname" ).value

 set msg = createobject("CDO.message")

 msg.To = email

  msg.From = folderemail

  msg.subject = "Goodbye"

  msg.textbody = name+", If for any reason you would like to subscribe again, send a message to "+folderemail+" and place the word SUBSCRIBE in the subject line."

  msg.send

Set grp = GetObject("LDAP://CN="+group+", OU=lists, DC=BRYANTFAMILY, DC=COM")

Dim oConnection

Dim oRecordset

Dim strQuery

Dim stremail

Dim oCont

Dim oGC

Dim strADsPath

Set oCont = GetObject("GC:")

For Each oGC In oCont

  strADsPath = oGC.ADsPath

Next

Set oConnection = CreateObject("ADODB.Connection")

Set oRecordset = CreateObject("ADODB.Recordset")

oConnection.Provider = "ADsDSOObject"  'The ADSI OLE-DB provider

oConnection.Open "ADs Provider"

strQuery = "<" & strADsPath & ">;(&(objectCategory=person)(mail=" & email & "));mail,cn,distinguishedName;subtree"

Set oRecordset = oConnection.Execute(strQuery)

Set grp = GetObject("LDAP://CN="+group+", OU=lists, DC=BRYANTFAMILY, DC=COM")

If oRecordset.EOF And oRecordset.BOF Then

Else

  While Not oRecordset.EOF

     grp.Remove ("LDAP://"+oRecordset.Fields("distinguishedName"))

     oRecordset.MoveNext

Wend

End If

Set oCont = Nothing

Set oGC = Nothing

Set oRecordset = Nothing

Set oConnection = Nothing

End Sub

There are few alterations you will need to make to the shared code to run. All references to the domain and email addresses must be modified to match your environment. The text and message lines can be modified at will to change the messages in the notifications.

To create the process, build the following workflow process and create two actions:

aFigure 5

Create two create actions with the following settings:

The Create action should call the IsSubscribe function for the condition execute the CreateContactAndSubscribeIt sub if true.

The Remove action should call the IsUnSubscribe function for the condition execute the DeleteContactAndUnSubscribeIt sub if true.

Next, activate the code from the General tab of the List folder from the Designer.afigure6. Lastly, set the workflow as the default for the folder by selecting the General tab of the workflow process itself from within the Designer program. afigure 7 Save the settings and launch the Active Directory Users and Computers to add some sample groups you can use to test. For my tests, I created a couple of sample groups named Lista and listb.  To test, I email a message to the public folder with the words subscribe lista then another with subscribe listb. Experiment with unsubscribe as well. Now remember there is a limit of accounts that can be contained in a single distribution group in the Active Directory.

When I get some time, I will probably build a little management page similar to that of the AutoDL tool in the resource kit and a feature that would allow users to find out the available lists by sending a message with the subject line of request lists or something along those lines. If anything, I hope this sample shows you some of the strengths of the Workflow Sink in Exchange 2000 and some of the other tasks that are possible with some light coding.