/*++

Copyright (c) 1996  Microsoft Corporation
© 1998 Seagate Software, Inc.  All rights reserved.

Module Name:

    HsmConn.cpp

Abstract:

    This is the main implementation of the HsmConn dll. This dll provides
    functions for accessing Directory Services and for connecting to
    our services.

Author:

    Rohde Wakefield    [rohde]   21-Oct-1996

Revision History:

--*/


#include "stdafx.h"
#include <dsgetdc.h>
#include <lmcons.h>
#include <lmapibuf.h>

// Required by SECURITY.H
#define SECURITY_WIN32
#include <security.h>

// HsmConnPoint objects are used only here
#include "hsmservr.h"

//
// Tracing information
//

#define WSB_TRACE_IS WSB_TRACE_BIT_HSMCONN

//
// _Module instantiation for ATL
//

CComModule _Module;

/////////////////////////////////////////////////////////////////////////////

#define REG_PATH        OLESTR("SOFTWARE\\Microsoft\\RemoteStorage")
#define REG_USE_DS      OLESTR("UseDirectoryServices")

//  Local data
static OLECHAR CNEqual[]                = L"CN=";
static OLECHAR ComputersNodeName[]      = L"Computers";
static OLECHAR DCsNodeName[]            = L"Domain Controllers";
static OLECHAR ContainerType[]          = L"Container";
static OLECHAR DisplaySpecifierName[]   = L"displaySpecifier";
static OLECHAR GuidAttrName[]           = L"remoteStorageGUID";
static OLECHAR RsDisplaySpecifierName[] = L"remoteStorageServicePoint-Display";
static OLECHAR RsNodeName[]             = L"RemoteStorage";
static OLECHAR RsNodeType[]             = L"remoteStorageServicePoint";
static OLECHAR ServiceBindAttrName[]    = L"serviceBindingInformation";
static OLECHAR ServiceBindValue[]       = L"Rsadmin.msc /ds ";

static ADS_ATTR_INFO   aaInfo[2];    // For use in IDirectoryObject
static ADSVALUE        adsValue[2];  // For use in IDirectoryObject
static OLECHAR         DomainName[MAX_COMPUTERNAME_LENGTH];
static BOOL            DSIsWritable = FALSE;          // Default to "NO"
static BOOL            UseDirectoryServices = FALSE;  // Default to "NO"

#if defined(HSMCONN_DEBUG)
    //  We use this and OutputDebugString when we can't use our normal 
    //  tracing.
    OLECHAR dbg_string[200];
#endif



extern "C"
{

//  Local functions
static void    GetDSState(void);
static HRESULT HsmConnectToServer(HSMCONN_TYPE type, 
        const OLECHAR * Server, REFIID riid, void ** ppv);
static const OLECHAR *HsmConnTypeAsString(HSMCONN_TYPE type);
static GUID    HsmConnTypeToGuid(IN HSMCONN_TYPE type);
static HRESULT HsmGetComputerNode(const OLECHAR * compName, 
        IADsContainer **pContainer);
static HRESULT HsmGetDsChild(IADsContainer * pContainer, const OLECHAR * Name,
        REFIID riid, void **ppv);




BOOL WINAPI
DllMain (
    IN  HINSTANCE hInst, 
    IN  ULONG     ulReason,
        LPVOID    /*lpReserved*/
    )

/*++

Routine Description:

    This routine is called whenever a new process and thread attaches
    to this DLL. Its purpose is to initialize the _Module object,
    necessary for ATL.

Arguments:

    hInst - HINSTANCE of this dll.

    ulReason - Context of the attaching/detaching

Return Value:

    non-zero - success
    
    0        - prevent action

--*/

{

    switch ( ulReason ) {

    case DLL_PROCESS_ATTACH:

        //
        // Initialize the ATL module, and prevent
        // any of the additional threads from sending
        // notifications through
        //

        _Module.Init ( 0, hInst );
        DisableThreadLibraryCalls ( hInst );
        GetDSState();
        break;

    case DLL_PROCESS_DETACH:

        //
        // Tell ATL module to terminate
        //

        _Module.Term ( );
        break;

    }

    return ( TRUE );

}


static HRESULT
HsmConnectToServer (
    IN  HSMCONN_TYPE type,
    IN  const OLECHAR * Server,
    IN  REFIID   riid,
    OUT void ** ppv
    )

/*++

Routine Description:

    Given a generic server (connected via HsmConnPoint class)
    connect to it and return back the requested interface 'riid'.

Arguments:

    type - Type of server to connect
    Server - Name of machine on which server is running.

    riid - The interface type to return.

    ppv - Returned interface pointer of the Server.

Return Value:

    S_OK - Connection made, Success.

    E_NOINTERFACE - Requested interface not supported by Server.

    E_POINTER - ppv is not a valid pointer.

    E_OUTOFMEMORY - Low memory condition prevented connection.

    HSM_E_NOT_READY - Engine is not running or not initialized yet

    FSA_E_NOT_READY - Fsa is not running or not initialized yet

--*/

{
    WsbTraceIn ( L"HsmConnectToServer",
        L"type = '%ls' , Server = '%ls', riid = '%ls', ppv = 0x%p",
        HsmConnTypeAsString ( type ), Server, WsbStringCopy ( WsbGuidAsString ( riid ) ), ppv );

    HRESULT hr = S_OK;

    try {

        //
        // Ensure parameters are valid
        //

        WsbAssert ( 0 != Server, E_POINTER );
        WsbAssert ( 0 != ppv,    E_POINTER );


        //
        // We will specify the provided HSM as the machine to contact,
        // so Construct a COSERVERINFO with Server.
        //

        REFCLSID rclsid = CLSID_HsmConnPoint;
        COSERVERINFO csi;

        memset ( &csi, 0, sizeof ( csi ) );
        csi.pwszName = (OLECHAR *) Server; // must cast to remove constness

        //
        // Build a MULTI_QI structure to obtain desired interface (necessary for 
        // CoCreateInstanceEx). In our case, we need only one interface.
        //

        MULTI_QI mqi[1];

        memset ( mqi, 0, sizeof ( mqi ) );
        mqi[0].pIID = &IID_IHsmConnPoint;

        //
        // The HsmConnPoint object ic created in the scope of the main HSM service and
        // provides access for HSM server objects
        //

        WsbAffirmHr( CoCreateInstanceEx ( rclsid, 0, CLSCTX_SERVER, &csi, 1, mqi ) );

        //
        // Put returned interfaces in smart pointers so we
        // don't leak a reference in case off throw
        //
        CComPtr<IHsmConnPoint> pConn;

        if( SUCCEEDED( mqi[0].hr ) ) {
            
            pConn = (IHsmConnPoint *)(mqi[0].pItf);
            (mqi[0].pItf)->Release( );

            hr   = mqi[0].hr;

#if 0  // This is now done at the COM process-wide security layer

        /* NOTE: In case that a per-connection security will be require,
           the method CheckAccess should be implemented in CHsmConnPoint */
                   
        //
        // Check the security first
        //
        WsbAffirmHr( mqi[1].hr );
        WsbAffirmHr( pServer->CheckAccess( WSB_ACCESS_TYPE_ADMINISTRATOR ) );
#endif

            // get the server object itself
            switch (type) {

            case HSMCONN_TYPE_HSM: {
                WsbAffirmHr( pConn->GetEngineServer((IHsmServer **)ppv) );
                break;
                }

            case HSMCONN_TYPE_FSA:
            case HSMCONN_TYPE_RESOURCE: {
                WsbAffirmHr( pConn->GetFsaServer((IFsaServer **)ppv) );
                break;
                }

            default: {
                WsbThrow ( E_INVALIDARG );
                break;
                }
            } 

        } else {

            // Make sure interface pointer is safe (NULL) when failing
            *ppv = 0;
        }

    } WsbCatchAndDo ( hr, 

        // Make sure interface pointer is safe (NULL) when failing
        *ppv = 0;
    
    ) // WsbCatchAndDo

    WsbTraceOut ( L"HsmConnectToServer",
        L"HRESULT = %ls, *ppv = %ls",
        WsbHrAsString ( hr ), WsbPtrToPtrAsString ( ppv ) );

    return ( hr );

}

HRESULT
HsmGetComputerNameFromADsPath(
    IN  const OLECHAR * szADsPath,
    OUT OLECHAR **      pszComputerName
)

/*++

Routine Description:

    Extract the computer name from the ADs path of the RemoteStorage node.
    The assumption here is that the full ADs path will contain this substring:
        "CN=RemoteStorage,CN=computername,CN=Computers"
    where computername is what we want to return.

Arguments:

    szADsPath       - The ADs path.

    pszComputerName - The returned computer name.

Return Value:

    S_OK    - Computer name returned OK.

--*/
{
    HRESULT  hr = S_FALSE;
    WCHAR*   pContainerNode;
    WCHAR*   pRSNode;

    WsbTraceIn(OLESTR("HsmGetComputerNameFromADsPath"),
        OLESTR("AdsPath = <%ls>"), szADsPath);

    //  Find the RemoteStorage node name and the computers node name
    //  in the ADs path. If the machine is a DC, then we have to 
    //  check for a "Domain Controllers" level instead.
    *pszComputerName = NULL;
    pRSNode = wcsstr(szADsPath, RsNodeName);
    pContainerNode = wcsstr(szADsPath, ComputersNodeName);
    if(!pContainerNode) {
        pContainerNode = wcsstr(szADsPath, DCsNodeName);
    }
    if (pRSNode && pContainerNode && pRSNode < pContainerNode) {
        WCHAR*  pWc;

        //  Find the "CN=" before the computer name
        pWc = wcsstr(pRSNode, CNEqual);
        if (pWc && pWc < pContainerNode) {
            WCHAR*  pComma;

            //  Skip the "CN="
            pWc += wcslen(CNEqual);

            //  Find the "," after the computer name
            pComma = wcschr(pWc, OLECHAR(','));

            //  Extract the computer name
            if (pWc < pContainerNode && pComma && pComma < pContainerNode) {
                int len;

                len = (int)(pComma - pWc);

                //  Remove '$' from end of name???
                if (0 < len && OLECHAR('$') == pWc[len - 1]) {
                    len--;
                }
                *pszComputerName = static_cast<OLECHAR *>(WsbAlloc(
                        (len + 1) * sizeof(WCHAR)));
                if (*pszComputerName) {
                    wcsncpy(*pszComputerName, pWc, len);
                    (*pszComputerName)[len] = 0;
                    hr = S_OK;
                }
            }
        }
    }

    WsbTraceOut (OLESTR("HsmGetComputerNameFromADsPath"),
        OLESTR("HRESULT = %ls, Computername = <%ls>"),
        WsbHrAsString(hr), (*pszComputerName ? *pszComputerName : OLESTR("")));

    return(hr);
}


static HRESULT HsmGetComputerNode(
    const OLECHAR * Name,
    IADsContainer **ppContainer
)

/*++

Routine Description:

    Return the computer node for the given computer name in the current Domain

Arguments:

    Name        - Computer name.

    ppContainer - Returned interface pointer of the node.

Return Value:

    S_OK - Success.

--*/
{
    HRESULT hr = S_OK;

    WsbTraceIn ( L"HsmGetComputerNode", L"Name = <%ls>", Name);

    try {
        WsbAssert(UseDirectoryServices, E_NOTIMPL);

        WCHAR         ComputerDn[MAX_PATH];
        CWsbStringPtr temp;
        ULONG         ulen;

        //  Construct the SamCompatible (whatever that means) name
        temp = DomainName;
        temp.Append("\\");
        temp.Append(Name);
        temp.Append("$");
        WsbTrace(L"HsmGetComputerNode: Domain\\computer = <%ls>\n", 
                static_cast<OLECHAR*>(temp));

        //  Translate that name to a fully qualified one
        ulen = MAX_PATH;
        ComputerDn[0] = WCHAR('\0');
        if (TranslateName(temp, NameSamCompatible,
                NameFullyQualifiedDN, ComputerDn, &ulen)) {
            WsbTrace(L"HsmGetComputerNode: ComputerDn = <%ls>\n", ComputerDn);

            //  Get the computer node
            temp = "LDAP://";
            temp.Append(ComputerDn);
            WsbTrace(L"HsmGetComputerNode: calling ADsGetObject with <%ls>\n", 
                    static_cast<OLECHAR*>(temp));
            WsbAffirmHr(ADsGetObject(temp, IID_IADsContainer, 
                    (void**)ppContainer));
        } else {
            DWORD  err = GetLastError();

            WsbTrace(L"HsmGetComputerNode: TranslateName failed; ComputerDn = <%ls>, err = %ld\n", 
                    ComputerDn, err);
            if (err) {
                WsbThrow(HRESULT_FROM_WIN32(err));
            } else {
                WsbThrow(E_UNEXPECTED);
            }
        }

        WsbTrace(OLESTR("HsmGetComputerNode: got computer node\n"));

    } WsbCatch( hr )

    WsbTraceOut ( L"HsmGetComputerNode", L"HRESULT = %ls, *ppContainer = '%ls'", 
        WsbHrAsString ( hr ), WsbPtrToPtrAsString ( (void**)ppContainer ) );

    return ( hr );
}


static HRESULT
HsmGetDsChild (
    IN  IADsContainer * pContainer,
    IN  const OLECHAR * Name,
    IN  REFIID          riid,
    OUT void **         ppv
    )

/*++

Routine Description:

    This routine returns back the requested child node.

Arguments:

    pContainer - The parent container.

    Name       - The childs name (i.e. value of Name attribute or CN=Name)

    riid       - Desired interface to return.

    ppv        - Returned interface.

Return Value:

    S_OK      - Connection made, Success.

    E_POINTER - Invalid pointer passed in as parameter.

    E_*       - Error

--*/

{
    WsbTraceIn ( L"HsmGetDsChild",
        L"pContainer = '0x%p', Name = '%ls', riid = '%ls', ppv = '0x%p'",
        pContainer, Name, WsbGuidAsString ( riid ), ppv );

    HRESULT hr = S_OK;

    try {
        CWsbStringPtr                 lName;
        CComPtr<IDispatch>            pDispatch;

        // Validate params
        WsbAssert ( 0 != pContainer, E_POINTER );
        WsbAssert ( 0 != Name,       E_POINTER );
        WsbAssert ( 0 != ppv,        E_POINTER );
        WsbAssert(UseDirectoryServices, E_NOTIMPL);

        //  Check to see if the child exists
        lName = Name;
        hr = pContainer->GetObject(NULL, lName, &pDispatch);
        if (FAILED(hr)) {
            hr = S_OK;
            lName.Prepend(CNEqual);
            WsbAffirmHr(pContainer->GetObject(NULL, lName, &pDispatch));
        }

        //  Convert to the correct interface
        WsbAffirmHr(pDispatch->QueryInterface(riid, ppv));

    } WsbCatch( hr )

    WsbTraceOut ( L"HsmGetDsChild", L"HRESULT = %ls, *ppv = '%ls'", 
        WsbHrAsString ( hr ), WsbPtrToPtrAsString ( ppv ) );

    return ( hr );
}


static const OLECHAR *
HsmConnTypeAsString (
    IN  HSMCONN_TYPE type
    )

/*++

Routine Description:

    Gives back a static string representing the connection type. 
    Note return type is strictly ANSI. This is intentional to make
    macro work possible.

Arguments:

    type - the type to return a string for.

Return Value:

    NULL - invalid type passed in.

    Otherwise, a valid char *.

--*/

{
#define STRINGIZE(_str) (OLESTR( #_str ))
#define RETURN_STRINGIZED_CASE(_case) \
case _case:                           \
    return ( STRINGIZE( _case ) );

    //
    // Do the Switch
    //

    switch ( type ) {

    RETURN_STRINGIZED_CASE( HSMCONN_TYPE_HSM );
    RETURN_STRINGIZED_CASE( HSMCONN_TYPE_FSA );
    RETURN_STRINGIZED_CASE( HSMCONN_TYPE_FILTER );
    RETURN_STRINGIZED_CASE( HSMCONN_TYPE_RESOURCE );

    default:

        return ( OLESTR("Invalid Value") );

    }
}

static GUID 
HsmConnTypeToGuid(IN HSMCONN_TYPE type)
{
    GUID serverGuid = GUID_NULL;

    switch ( type ) {
    case HSMCONN_TYPE_HSM:
        serverGuid = CLSID_HsmServer;
        break;
    case HSMCONN_TYPE_FSA:
    case HSMCONN_TYPE_RESOURCE:
        serverGuid = CLSID_CFsaServerNTFS;
        break;
    }
    return(serverGuid);
}


HRESULT
HsmConnectFromName (
    IN  HSMCONN_TYPE type,
    IN  const OLECHAR * Name,
    IN  REFIID riid,
    OUT void ** ppv
    )

/*++

Routine Description:

    When given an name, connect and return the object representing the 
    Server, providing the specified interface.

Arguments:

    type - The type of service / object we are connecting too.

    Name - UNICODE string describing the Server to connect to.

    riid - The interface type to return.

    ppv - Returned interface pointer of the Server.

Return Value:

    S_OK - Connection made, Success.

    E_NOINTERFACE - Requested interface not supported by Server.

    E_POINTER - ppv or Name is not a valid pointer.

    E_OUTOFMEMORY - Low memory condition prevented connection.

    E_INVALIDARG - The given name does not corespond to a known Server.

--*/

{
    WsbTraceIn ( L"HsmConnectFromName",
        L"type = '%ls', Name = '%ls', riid = '%ls', ppv = 0x%p",
        HsmConnTypeAsString ( type ), Name, WsbGuidAsString ( riid ), ppv );
    WsbTrace(OLESTR("HsmConnectFromName: UseDirectoryServices = %ls\n"),
        WsbBoolAsString(UseDirectoryServices));

    HRESULT hr = S_OK;

    try {
        BOOLEAN    done = FALSE;

        //
        // Ensure parameters are valid
        //

        WsbAssert ( 0 != Name, E_POINTER );
        WsbAssert ( 0 != ppv,    E_POINTER );

        if (!done) { // Try without Directory Services

            CWsbStringPtr ComputerName = Name;
            int           i;

            //  Get the computer/server name
            i = wcscspn(Name, OLESTR("\\"));
            ComputerName[i] = 0;

            if (HSMCONN_TYPE_RESOURCE == type) {
                CWsbStringPtr         Path;
                OLECHAR *             rscName;
                CComPtr<IFsaResource> pFsaResource;
                CComPtr<IFsaServer>   pFsaServer;

                WsbAffirmHr(HsmConnectToServer(type, ComputerName, 
                        IID_IFsaServer, (void**)&pFsaServer));

                // Determine if we have a logical name or a sticky name format.
                // The logical name is a format like ("server\NTFS\d") and a sticky name
                // format is like ("server\NTFS\Volume{GUID}\").
                // Find the start of the last section and determine if there is only a single
                // character after it or not.
                rscName = wcsstr ( Name, L"NTFS\\" );
                WsbAssert ( 0 != rscName, E_INVALIDARG );

                // Now point just past the "NTFS\" part of the string. So we are pointing at
                // either a single character, indicating the drive, or the "Volume{GUID}\".
                rscName += 5;       
                Path = rscName;
                if (wcslen (rscName) == 1)  {
                    //  Logical name ("server\NTFS\d") so convert to path and find resource
                    WsbAffirmHr(Path.Append(":\\"));
                    WsbAffirmHr(pFsaServer->FindResourceByPath(Path, &pFsaResource));
                }
                else {
                    // Sticky name ("server\NTFS\Volume{GUID}\") so find resource for it.
                    WsbAffirmHr(pFsaServer->FindResourceByStickyName(Path, &pFsaResource));
                }

                WsbAffirmHr(pFsaResource->QueryInterface(riid, ppv));

            } else {
                WsbAffirmHr(HsmConnectToServer(type, ComputerName, riid, ppv));
            }
        }

    } WsbCatch ( hr )

    WsbTraceOut ( L"HsmConnectFromName",
        L"HRESULT = %ls, *ppv = %ls",
        WsbHrAsString ( hr ), WsbPtrToPtrAsString ( ppv ) );

    return ( hr );

}


HRESULT
HsmConnectFromId (
    IN  HSMCONN_TYPE type,
    IN  REFGUID rguid,
    IN  REFIID riid,
    OUT void ** ppv
    )

/*++

Routine Description:

    Connects to the specified service or object. See HSMCONN_TYPE for
    the types of services and objects. 

Arguments:

    type - The type of service / object we are connecting too.

    rguid - The unique ID of the service / object to connect too.

    riid - The interface type to return.

    ppv - Returned interface pointer of the HSM Server.

Return Value:

    S_OK - Connection made, Success.

    E_NOINTERFACE - Requested interface not supported by HSM Server.

    E_POINTER - ppv or Hsm is not a valid pointer.

    E_OUTOFMEMORY - Low memory condition prevented connection.

    E_INVALIDARG - The given ID and type do not correspond to a known 
                   service or object.

--*/

{
    WsbTraceIn ( L"HsmConnectFromId",
        L"type = '%ls', rguid = '%ls', riid = '%ls', ppv = 0x%p",
        HsmConnTypeAsString ( type ), WsbStringCopy ( WsbGuidAsString ( rguid ) ),
        WsbStringCopy ( WsbGuidAsString ( riid ) ), ppv );
    WsbTrace(OLESTR("HsmConnectFromId: UseDirectoryServices = %ls\n"),
        WsbBoolAsString(UseDirectoryServices));

    HRESULT hr = S_OK;

    try {
        BOOLEAN          DSUsed = FALSE;
        CWsbVariant      guidVariant;
        CComPtr < IADs > pObject;
        CWsbStringPtr    serverName;
        CWsbVariant      serverVariant;

        //
        // Ensure parameters are valid
        //

        WsbAssert ( 0 != ppv,   E_POINTER );

        switch ( type ) {

        case HSMCONN_TYPE_HSM:
        case HSMCONN_TYPE_FSA:
        case HSMCONN_TYPE_FILTER:

            if (DSUsed) {

            } else {

                WsbAffirmHr( WsbGetComputerName( serverName ) );

            }

            //
            // Connect to the server
            //

            WsbAffirmHr ( HsmConnectToServer ( type, serverName, riid, ppv ) );

            break;

        case HSMCONN_TYPE_RESOURCE:
            {
                CComPtr< IFsaServer > pFsaServer;
                GUID serverGuid;
    
                serverGuid = HsmConnTypeToGuid(type);
                WsbAffirmHr ( HsmConnectFromId ( HSMCONN_TYPE_FSA, serverGuid, IID_IFsaServer, (void**)&pFsaServer ) );

                CComPtr< IFsaResource > pFsaResource;

                WsbAffirmHr ( pFsaServer->FindResourceById ( rguid, &pFsaResource ) );
                WsbAffirmHr ( pFsaResource->QueryInterface ( riid, ppv ) );
            }
            break;

        default:

            WsbThrow ( E_INVALIDARG );

        }

    } WsbCatch ( hr )

    WsbTraceOut ( L"HsmConnectFromId",
        L"HRESULT = %ls, *ppv = %ls",
        WsbHrAsString ( hr ), WsbPtrToPtrAsString ( ppv ) );

    return ( hr );

}


HRESULT
HsmPublish (
    IN  HSMCONN_TYPE type,
    IN  const OLECHAR * Name,
    IN  REFGUID rguidObjectId,
    IN  const OLECHAR * Server,
    IN  REFGUID rguid
    )

/*++

Routine Description:

    Publish (i.e. store) information about the service/object in
    Directory Services.

Arguments:

    type - The type of service/object.

    Name - Name (possibly preceded by a subpath) of the Service/Object.

    rguidObjectId - The ID that the object is known by.

    Server - The server (computer name) on which the service actually exists. 
            For resources, this will be NULL since it is implicit in the
            FSA specified by rguid.

    rguid - For resources, the ID of the FSA. For services, the CLSID of
            the service's class factory ie. CLSID_HsmServer.

Return Value:

    S_OK - Connection made, Success.

    E_POINTER - Name or Server is not a valid pointer.

    E_OUTOFMEMORY - Low memory condition prevented connection.

--*/

{
    HRESULT hr = S_OK;

    WsbTraceIn ( L"HsmPublish",
        L"type = '%ls', Name = '%ls', rguidObjectId = '%ls', Server = '%ls', rguid = '%ls'",
        HsmConnTypeAsString ( type ), Name, 
        WsbStringCopy ( WsbGuidAsString ( rguidObjectId ) ), Server,
        WsbStringCopy ( WsbGuidAsString ( rguid ) ) );
    WsbTrace(OLESTR("HsmPublish: UseDirectoryServices = %ls\n"),
        WsbBoolAsString(UseDirectoryServices));

    try {

        //
        // Ensure parameters are valid
        //

        WsbAssert ( 0 != Name, E_POINTER );
        WsbAssert ( ( HSMCONN_TYPE_RESOURCE == type ) || ( 0 != Server ), E_POINTER );

        // Perhaps we should output a log event if the DS is not writable

        //  We now only publish the Engine service.  Perhaps in the future we
        //  will publish additional information
        if (HSMCONN_TYPE_HSM == type && UseDirectoryServices && DSIsWritable) {
            CWsbStringPtr    pathToName;

            try {
                DWORD                         aSet;
                CWsbStringPtr                 guidString(rguidObjectId);
                HRESULT                       hrGetNode;
                CComPtr<IADsContainer>        pComputer;
                CComPtr<IDispatch>            pDispatch;
                CComPtr<IDirectoryObject>     pDirObj;
                CComPtr<IADs>                 pNode;

                //  Save the node name for the event log message
                pathToName = Name;
                pathToName.Append("\\");
                pathToName.Append(RsNodeName);

                //  Get the computer node
                WsbAffirmHr(HsmGetComputerNode(Name, &pComputer));

                //  See if we're already published
                hrGetNode = HsmGetDsChild(pComputer, RsNodeName, 
                        IID_IADs, (void**)&pNode);

                //  If not, add our node
                if (HRESULT_FROM_WIN32(ERROR_DS_NO_SUCH_OBJECT) == hrGetNode) {
                    CWsbBstrPtr                relPath(RsNodeName);

                    relPath.Prepend(CNEqual);

                    WsbAffirmHr(pComputer->Create(RsNodeType, relPath, &pDispatch));
                    WsbAffirmHr(pDispatch->QueryInterface(IID_IADs, 
                            (void**)&pNode));

                    //  Force info out of cache
                    WsbAffirmHr(pNode->SetInfo());
                } else {
                    WsbAffirmHr(hrGetNode);
                }

                //  Set the GUID & ServiceBinding values
                adsValue[0].dwType = ADSTYPE_CASE_IGNORE_STRING;
                adsValue[0].CaseIgnoreString = (WCHAR*)guidString;

                aaInfo[0].pszAttrName = GuidAttrName;
                aaInfo[0].dwControlCode = ADS_ATTR_UPDATE;
                aaInfo[0].dwADsType = ADSTYPE_CASE_IGNORE_STRING;
                aaInfo[0].pADsValues = &adsValue[0];
                aaInfo[0].dwNumValues = 1;

                adsValue[1].dwType = ADSTYPE_CASE_IGNORE_STRING;
                adsValue[1].CaseIgnoreString = ServiceBindValue;

                aaInfo[1].pszAttrName = ServiceBindAttrName;
                aaInfo[1].dwControlCode = ADS_ATTR_UPDATE;
                aaInfo[1].dwADsType = ADSTYPE_CASE_IGNORE_STRING;
                aaInfo[1].pADsValues = &adsValue[1];
                aaInfo[1].dwNumValues = 1;

                WsbAffirmHr(pNode->QueryInterface(IID_IDirectoryObject, 
                       (void**)&pDirObj));
                WsbAffirmHr(pDirObj->SetObjectAttributes(aaInfo, 2, &aSet));
                WsbTrace(L"HsmPublish: after SetObjectAttributes, aSet = %ld\n",
                        aSet);

                WsbLogEvent(WSB_MESSAGE_PUBLISH_IN_DS, 0, NULL,
                        static_cast<OLECHAR*>(pathToName), NULL);
            } WsbCatchAndDo(hr, 
                WsbLogEvent(WSB_MESSAGE_PUBLISH_FAILED, 0, NULL,
                        static_cast<OLECHAR*>(pathToName), NULL);
                hr = S_OK;  // Don't stop service just for this
            )
        }

    } WsbCatch ( hr )

    WsbTraceOut ( L"HsmPublish", L"HRESULT = %ls", WsbHrAsString ( hr ) );

    return ( hr );

}



//  GetDSState - determine if we're using Directory Services or not
static void GetDSState(void)
{

    BOOL                     CheckForDS = TRUE;
    DOMAIN_CONTROLLER_INFO * dc_info;
    DWORD                    size;
    OLECHAR                  vstr[32];
    DWORD                    status;

    UseDirectoryServices = FALSE;

    //  Should we attempt to use directory services in this module?
    //  (Setting the registry value to "0" allows us to avoid Directory
    //  Services completely

    if (S_OK == WsbGetRegistryValueString(NULL, REG_PATH, REG_USE_DS,
            vstr, 32, &size)) {
        OLECHAR *stop;
        ULONG   value;

        value = wcstoul(vstr,  &stop, 10 );
        if (0 == value) {
            CheckForDS = FALSE;
        }
    }

    //  Get the account domain name
    WsbGetAccountDomainName(DomainName, MAX_COMPUTERNAME_LENGTH );

#if defined(HSMCONN_DEBUG)
    swprintf(dbg_string, L"Account domain name = <%ls>\n", DomainName);
    OutputDebugString(dbg_string);
#endif

    //  Check if Directory Services is available
    if (CheckForDS) {
        status = DsGetDcName(NULL, NULL, NULL, NULL, 
                DS_DIRECTORY_SERVICE_REQUIRED | DS_IS_FLAT_NAME | 
                DS_RETURN_FLAT_NAME, &dc_info);

#if defined(HSMCONN_DEBUG)
        swprintf(dbg_string, L"DsGetDcName status = %d\n", status);
        OutputDebugString(dbg_string);
#endif

        if (NO_ERROR == status) {

#if defined(HSMCONN_DEBUG)
            swprintf(dbg_string, L"dc_info->DomainName = <%ls>\n", dc_info->DomainName);
            OutputDebugString(dbg_string);
#endif

            if (dc_info->Flags & DS_DS_FLAG) {
                wcscpy(DomainName, dc_info->DomainName);
                UseDirectoryServices = TRUE;
                if (dc_info->Flags & DS_WRITABLE_FLAG) {
                    DSIsWritable = TRUE;
                }
            }
            NetApiBufferFree(dc_info);
        }
    }

#if defined(HSMCONN_DEBUG)
    swprintf(dbg_string, L"dHsmConn - GetDSState: UseDirectoryServices = %ls\n", 
           WsbBoolAsString(UseDirectoryServices));
    OutputDebugString(dbg_string);
#endif
}

} // extern "C"