Platform SDK: Exchange Server

How the Sample Works

The following list gives the names of the ASP sample files and a brief description of each.

File Manifest

File Description
ADS.inc Contains VBScript routines used to access and update the Exchange Server directory using ADSI objects.
App.inc Contains shared VBScript code used to build the user interface pages. This file is shared across most of the new ASP samples.
Constants.inc Contains declarations for various constants used.
Global.asa This standard ASP file contains code to handle the Application and Session starting and stopping events.
handlecmds.asp Contains code for processing the requests the user makes with the browser.
Logon.asp Sets up the initial code on the client's browser and initializes the logon process.
Maintenance.asp Contains code used to set up the Browser UI.
NTAcct.inc Contains VBScript routines used to create, delete and manipulate SIDs and Descriptors when creating mailboxes.
Properties.asp Contains the code used to display the properties for the Mailbox.
SelExchServer.asp Contains the code used to display choices when selecting the Exchange Server to create the Mailbox for.

COM Components Used

Name Description
AcctCrt (AcctCrt.dll) This COM component is used to provide Windows NT account maintenance functionality, such as getting account security identifiers and descriptors for transport to the Exchange directory through ADSI (LDAP). The component provides the AcctMgmt COM class for this purpose.
ADSLDP (adsldp.dll)

(ADSI LDAP Provider)

This component is the ADSI LDAP provider. It is used to bind ADSI objects and communicate with the Exchange Server LDAP service. It provides services used to handle the "LDAP" ADSI namespace, parse display names provided within that namespace, and return the appropriate COM objects used to access and manipulate Exchange directory objects. All of the low-level data management and LDAP details are encapsulated within bound opaque ADSI objects, shielding the programmer from these unnecessary details.
MSADO15

(msado15.dll)

(ADO 1.5 Component)

An ADSI OLE DB provider is used to search the Exchange Directory. The ADSI LDAP provider implements the necessary OLE DB interface to the underlying LDAP search functionality. The ActiveX Data Objects 1.5 component (MSADO15) provides client applications written in high-level languages a set of dual-interfaced COM classes that can be used to access OLE DB providers. Instances (objects) of these classes are used in the sample to access the ADSI OLE DB provider and search for recipients.

Discussion

Associated Windows NT Domain Account

All Exchange Server mailboxes require an associated Windows NT domain account to provide a security context and ownership. The account is associated by placing its security descriptor and identifier into a Recipient directory object's NT-Security-Descriptor and Assoc-NT-Account attributes respectively. In C++ applications, Win32 API calls can be used to get this information about the account, and then DAPI or MAPI can be used to transport the information into the directory. From languages where access to the Win32 and MAPI APIs is prohibitive, setting this account information is difficult. Another problem is that both the descriptor and the identifier are expressed using C++ types that are not oleautomation compatible. The AcctCrt COM component provides services that can be used by higher-level languages such as Visual Basic, Java, or scripting languages to add or delete domain accounts, and retrieve (or generate) account security descriptors and identifiers for transport through ADSI to the Exchange Server directory.

Creating a new Account

The following abbreviated code example shows how to use the AcctMgmt object to create a new account. See the sample file NTAcct.inc for a complete implementation.

Set mntAcct = CreateObject("MSExchange.AcctMgmt") 
    Dim strDomain = ""
    Dim strUser  ="JohnD"
 
    ' HERE we use the COM object MSExchange.AcctMgmt
    Call mntAcct. NtAccountCreate (strDomain, strUser, pstrPassword, gstrNone, gstrNone)
     
    ' Destroy if finished
    Set mntAcct = Nothing
End Sub

Getting the Windows NT Account Security Descriptor and Identifier

The abbreviated code below demonstrates the steps used to first get the NT account security descriptor and identifier for an account. For a complete example, see the sample file NtAcct.inc.

Set mntAcct = CreateObject("MSExchange.AcctMgmt") 
strDomain = ""
strUser  = "JohnD"
Call mntAcct.GetSidFromName(pstrDomain, pstrUser, pvarSecurityID)
Call mntAcct.GenerateSecDescriptor(pstrDomain, pstrUser, pvarSecurityDescriptor)
' Destroy if finished
Set mntAcct = Nothing

Transport to the Exchange Server 5.5 Directory using ADSI 2.0

Once we have the Windows NT domain account security identifier and descriptor, we can then associate the Exchange Server mailbox by putting them into the directory object's attributes. The code below demonstrates this in VBScript. Note that both Assoc-NT-Account and NT-Security-Descriptor attributes have syntax class Octet-String. The ADSLDP (ADSI LDAP Provider) component supports putting values of this class using either a BSTR (String in Visual Basic) or a VARIANT subtype VT_ARRAY|VT_UI1. The methods GenerateSecDescriptor and GetSidFromName return the descriptor and identifier in the latter format, VARIANT subtype VT_ARRAY|VT_UI1. Although Visual Basic and VBScript do not easily support the unsigned char type, they do support the VARIANT (Variant) type. (All information is stored in Variants in VBScript) As long as one does not try to manipulate the values in the contained SAFEARRAY, the VARIANT can be passed to calls to other methods without trouble. For comparison purposes, the setting of the Deliv-Ext-Cont-Types attribute using ADSI is shown as well, where the value is stored and handed off as a BSTR (String in Visual Basic).

' Create the MSExchange.AcctMgmt object
Set mntAcct = CreateObject("MSExchange.AcctMgmt") 

' Here we get the security descriptor for the associated NT account.
pstrDomain = ""
pstrUser   = "JohnD"
Call mntAcct.GetSidFromName (pstrDomain, pstrUser, pvarSecurityID)
Call mntAcct.GenerateSecDescriptor(pstrDomain, pstrUser, pvarSecurityDescriptor)

' now we have the identifier and descriptor ready to go. Create the director object
' and populate mandatory attributes

strLDAPPath = "LDAP://servername/cn=Recipients,ou=YourSite,o=YourOrg"
Set cnRecipients = GetObject(strLDAPPath)
Set recipient = cnRecipients.Create("organizationalPerson", "cn="; & "JohnnyD")

'  populate the object's attributes
recipient.put "Assoc-NT-Account", (pvarSecurityID)
recipient.put "NT-Security-Descriptor", (pvarSecurityDescriptor)

' The Deliv-Ext-Cont-Types attribute is also
' an octet string in the directory.  Here we show
' using a BSTR to pass the value rather than 
' a safearray of unsigned chars (bytes)

strDelivContType = "2A864886F7140501"
recipient.put "Deliv-Ext-Cont-Types", strDelivContType
'  go on to populate the rest of the attributes
'  see the sample for full details

recipient.setinfo

The parentheses used in the Put method calls above are required when sending multi-valued (SAFEARRAY) properties to the directory provider. They force the VBScript scripting engine to send the VARIANT holding the value, not the address of the VARIANT to the method. When you use variable names in VBScript as arguments to methods, the scripting engine creates a new VARIANT that references the address of the VARIANT that actually holds the value you wish to send to the method. This means a VARIANT subtype VT_VARIANT|VT_BYREF is sent to the method, not VARIANT subtype VT_ARRAY|VT_UI1. (this is referred to as passing the VARIANT by reference, not by value) The Put implementation does not correctly deal with this indirection for VARIANT variables that contain a SAFEARRAY, and an exception is thrown.

Searching the Directory with ADO

The sample uses the OLE DB provider to perform searches on the directory when populating the Recipients box in the HTML page. This procedure is shown in the abbreviated code listed below using the ActiveX Data Objects (ADO) 1.5 Connection and Recordset objects. The ADO objects in turn call the OLE DB provider to send the query. The OLE DB provider in turn uses the LDAP API to query the Exchange directory.

Set conn = CreateObject("ADODB.Connection")
    conn.Open "Provider=ADSDSOObject"
    '
    ' Find mail boxes and  internet custom recipients matching the selection criteria.
    '
    strQuery = "<" & strADsPath(gstrNONE) & ">;" _
                & "(&(" & mstrADS_COL_DISP_NAME & "=" & strLDAPSearch(pstrSelCriteria) & "*)" _
                        & "(|(objectClass=" & gstrADS_CLASS_MB & ")(objectClass=" & gstrADS_CLASS_CR_INTERNET &")));" _
                & mstrADS_COL_DISP_NAME & "," & mstrADS_COL_ADS_PATH & ";OneLevel"

    Set rsRecipient = CreateObject("ADODB.Recordset")
    rsRecipient.PageSize = mcADS_RS_PAGE_SIZE
   ' its important to set the page size to less than the max
   ' rows returned LDAP setting for Exchange Server
    rsRecipient.Open strQuery, conn

    '  use the recordset rows

    rsRecipient.Close
    conn.Close

    Set rsRecipient = Nothing
    Set conn = Nothing