//---------------------------------------------------------------------------
//
//  Microsoft Windows
//  Copyright (C) Microsoft Corporation, 1992 - 1995
//
//  File:  cgroup.cxx
//
//  Contents:  Group object
//
//  History:   11-1-95     krishnag    Created.
//
//----------------------------------------------------------------------------

#include "winnt.hxx"
#pragma hdrstop

//  Class CWinNTGroup -> GlobalGroup DS class

DEFINE_IDispatch_ExtMgr_Implementation(CWinNTGroup)
DEFINE_IADsExtension_ExtMgr_Implementation(CWinNTGroup)
DEFINE_IADs_TempImplementation(CWinNTGroup)
DEFINE_IADs_PutGetImplementation(CWinNTGroup,GroupClass,gdwGroupTableSize)
DEFINE_IADsPropertyList_Implementation(CWinNTGroup, GroupClass,gdwGroupTableSize)

CWinNTGroup::CWinNTGroup():
        _pDispMgr(NULL),
        _pExtMgr(NULL),
        _pPropertyCache(NULL),
        _ParentType(0),
        _DomainName(NULL),
        _ServerName(NULL)
{
    ENLIST_TRACKING(CWinNTGroup);
}


HRESULT
CWinNTGroup::CreateGroup(
    BSTR Parent,
    ULONG ParentType,
    BSTR DomainName,
    BSTR ServerName,
    BSTR GroupName,
    ULONG GroupType,
    DWORD dwObjectState,
    PSID pSid,          // OPTIONAL
    REFIID riid,
    CWinNTCredentials& Credentials,
    void **ppvObj
    )
{
    CWinNTGroup FAR * pGroup = NULL;
    HRESULT hr = S_OK;

    BOOL fAccountSid = TRUE;

    hr = AllocateGroupObject(&pGroup);
    BAIL_ON_FAILURE(hr);

    ADsAssert(pGroup->_pDispMgr);


    hr = pGroup->InitializeCoreObject(
                Parent,
                GroupName,
                GROUP_CLASS_NAME,
                GROUP_SCHEMA_NAME,
                CLSID_WinNTGroup,
                dwObjectState
                );
    BAIL_ON_FAILURE(hr);

    hr = ADsAllocString(DomainName, &pGroup->_DomainName);
    BAIL_ON_FAILURE(hr);

    hr = ADsAllocString(ServerName, &pGroup->_ServerName);
    BAIL_ON_FAILURE(hr);

    pGroup->_ParentType = ParentType;
    pGroup->_GroupType = GroupType;

    hr = SetDWORDPropertyInCache(
         pGroup->_pPropertyCache,
         TEXT("groupType"),
         GroupType,
         TRUE           // fExplicit
         );

    BAIL_ON_FAILURE(hr);



    //
    // Try to determine if object corresponds to a account
    // domain
    //
    if (pSid) {

        //
        // A domain account sid has:
        //   (1) a identifier authority of SECURITY_NT_AUTHORITY
        //   (2) at least one subauth identifier
        //   (3) the first subauth identifier is SECURITY_NT_NON_UNIQUE
        //

        PSID_IDENTIFIER_AUTHORITY pSidIdentAuth = NULL;
        SID_IDENTIFIER_AUTHORITY NtAuthIdentAuth = SECURITY_NT_AUTHORITY;

        PDWORD pdwSidSubAuth = NULL;

        fAccountSid = FALSE;

        pSidIdentAuth = GetSidIdentifierAuthority(pSid);
        ADsAssert(pSidIdentAuth);

        if (memcmp(pSidIdentAuth, &NtAuthIdentAuth, sizeof(SID_IDENTIFIER_AUTHORITY)) == 0) {

            if (GetSidSubAuthorityCount(pSid) > 0) {

                pdwSidSubAuth = GetSidSubAuthority(pSid, 0);
                ADsAssert(pdwSidSubAuth);

                if (*pdwSidSubAuth == SECURITY_NT_NON_UNIQUE) {
                    fAccountSid = TRUE;
                }
            }
        }
    }

    pGroup->_Credentials = Credentials;
    hr = pGroup->_Credentials.Ref(ServerName, DomainName, ParentType);
    if (fAccountSid) {
        //
        // We permit this to fail if we can determine it is not a account
        // sid, since we won't be able to ref credentials on a non-existent
        // pseudo-domain like NT AUTHORITY (e.g., the well-known sids)
        //
        BAIL_ON_FAILURE(hr);
    }

    //
    // Load ext mgr and extensions
    //

    hr = ADSILoadExtensionManager(
                GROUP_CLASS_NAME,
                (IADsGroup *) pGroup,
                pGroup->_pDispMgr,
                Credentials,
                &pGroup->_pExtMgr
                );
    BAIL_ON_FAILURE(hr);

    ADsAssert(pGroup->_pExtMgr);

    //
    // Prepopulate the object
    //
    hr = pGroup->Prepopulate(TRUE,
                            pSid);
    BAIL_ON_FAILURE(hr);

    // check if the call is from UMI
    if(Credentials.GetFlags() & ADS_AUTH_RESERVED) {
    //
    // we do not pass riid to InitUmiObject below. This is because UMI object
    // does not support IDispatch. There are several places in ADSI code where
    // riid passed into this function is defaulted to IID_IDispatch -
    // IADsContainer::Create for example. To handle these cases, we always
    // request IID_IUnknown from the UMI object. Subsequent code within UMI
    // will QI for the appropriate interface.
    //
        if(3 == pGroup->_dwNumComponents) {
            pGroup->_CompClasses[0] = L"Domain";
            pGroup->_CompClasses[1] = L"Computer";
            pGroup->_CompClasses[2] = L"Group";
        }
        else if(2 == pGroup->_dwNumComponents) {
            if(NULL == DomainName) {
            // workstation services not started. See getobj.cxx.
                pGroup->_CompClasses[0] = L"Computer";
                pGroup->_CompClasses[1] = L"Group";
            }
            else if(NULL == ServerName) {
                pGroup->_CompClasses[0] = L"Domain";
                pGroup->_CompClasses[1] = L"Group";
            }
            else
                BAIL_ON_FAILURE(hr = UMI_E_FAIL);
        }
        else
            BAIL_ON_FAILURE(hr = UMI_E_FAIL);

        hr = pGroup->InitUmiObject(
                pGroup->_Credentials,
                GroupClass,
                gdwGroupTableSize, 
                pGroup->_pPropertyCache,
                (IUnknown *) (INonDelegatingUnknown *) pGroup,
                pGroup->_pExtMgr,
                IID_IUnknown,
                ppvObj
                );

        BAIL_ON_FAILURE(hr);

        //
        // UMI object was created and the interface was obtained successfully.
        // UMI object now has a reference to the inner unknown of IADs, since
        // the call to Release() below is not going to be made in this case.
        //
        RRETURN(hr);
    }

    hr = pGroup->QueryInterface(riid, ppvObj);
    BAIL_ON_FAILURE(hr);

    pGroup->Release();

    RRETURN(hr);

error:
    delete pGroup;

    RRETURN_EXP_IF_ERR(hr);

}

HRESULT
CWinNTGroup::CreateGroup(
    BSTR Parent,
    ULONG ParentType,
    BSTR DomainName,
    BSTR ServerName,
    BSTR GroupName,
    ULONG GroupType,
    DWORD dwObjectState,
    REFIID riid,
    CWinNTCredentials& Credentials,
    void **ppvObj
    )
{
    HRESULT hr = S_OK;

    hr = CWinNTGroup::CreateGroup(
                              Parent,
                              ParentType,
                              DomainName,
                              ServerName,
                              GroupName,
                              GroupType,
                              dwObjectState,
                              NULL,
                              riid,
                              Credentials,
                              ppvObj
                              );

    RRETURN_EXP_IF_ERR(hr);
}

CWinNTGroup::~CWinNTGroup( )
{
    ADsFreeString(_DomainName);
    ADsFreeString(_ServerName);

    delete _pExtMgr;            // created last, destroyed first

    delete _pDispMgr;

    delete _pPropertyCache;
}

//----------------------------------------------------------------------------
// Function:   QueryInterface
//
// Synopsis:   If this object is aggregated within another object, then
//             all calls will delegate to the outer object. Otherwise, the
//             non-delegating QI is called
//
// Arguments:
//
// iid         interface requested
// ppInterface Returns pointer to interface requested. NULL if interface
//             is not supported.
//
// Returns:    S_OK on success. Error code otherwise.
//
// Modifies:   *ppInterface to return interface pointer
//
//----------------------------------------------------------------------------
STDMETHODIMP CWinNTGroup::QueryInterface(
    REFIID iid,
    LPVOID *ppInterface
    )
{
    if(_pUnkOuter != NULL)
        RRETURN(_pUnkOuter->QueryInterface(
                iid,
                ppInterface
                ));

    RRETURN(NonDelegatingQueryInterface(
            iid,
            ppInterface
            ));
}

//----------------------------------------------------------------------------
// Function:   AddRef
//
// Synopsis:   IUnknown::AddRef. If this object is aggregated within
//             another, all calls will delegate to the outer object. 
//             Otherwise, the non-delegating AddRef is called
//
// Arguments:
//
// None
//
// Returns:    New reference count
//
// Modifies:   Nothing
//
//----------------------------------------------------------------------------
STDMETHODIMP_(ULONG) CWinNTGroup::AddRef(void)
{
    if(_pUnkOuter != NULL)
        RRETURN(_pUnkOuter->AddRef());

    RRETURN(NonDelegatingAddRef());
}

//----------------------------------------------------------------------------
// Function:   Release 
//
// Synopsis:   IUnknown::Release. If this object is aggregated within
//             another, all calls will delegate to the outer object.
//             Otherwise, the non-delegating Release is called
//
// Arguments:
//
// None
//
// Returns:    New reference count
//
// Modifies:   Nothing
//
//----------------------------------------------------------------------------
STDMETHODIMP_(ULONG) CWinNTGroup::Release(void)
{
    if(_pUnkOuter != NULL)
        RRETURN(_pUnkOuter->Release());

    RRETURN(NonDelegatingRelease());
}

//----------------------------------------------------------------------------

STDMETHODIMP
CWinNTGroup::NonDelegatingQueryInterface(
    REFIID iid,
    LPVOID FAR* ppv
    )
{
    HRESULT hr = S_OK;

    if (ppv == NULL) {
        RRETURN(E_POINTER);
    }

    if (IsEqualIID(iid, IID_IUnknown))
    {
        *ppv = (IADsGroup FAR *) this;
    }
    else if (IsEqualIID(iid, IID_IADsGroup))
    {
        *ppv = (IADsGroup FAR *) this;
    }
    else if (IsEqualIID(iid, IID_IADs))
    {
        *ppv = (IADsGroup FAR *) this;
    }
    else if (IsEqualIID(iid, IID_IADsPropertyList))
    {
        *ppv = (IADsPropertyList FAR *) this;
    }
    else if (IsEqualIID(iid, IID_IDispatch))
    {
        *ppv = (IADsGroup FAR *) this;
    }
    else if (IsEqualIID(iid, IID_ISupportErrorInfo))
    {
        *ppv = (ISupportErrorInfo FAR *) this;
    }
    else if( (_pDispatch != NULL) &&
             IsEqualIID(iid, IID_IADsExtension) )
    {
        *ppv = (IADsExtension *) this;
    }
    else if (_pExtMgr)
    {
        RRETURN( _pExtMgr->QueryInterface(iid, ppv));
    }
    else
    {
        *ppv = NULL;
        return E_NOINTERFACE;
    }
    AddRef();
    return NOERROR;
}

/* ISupportErrorInfo method */
STDMETHODIMP
CWinNTGroup::InterfaceSupportsErrorInfo(
    THIS_ REFIID riid
    )
{
    if (IsEqualIID(riid, IID_IADs) ||
        IsEqualIID(riid, IID_IADsGroup) ||
        IsEqualIID(riid, IID_IADsPropertyList)) {
        RRETURN(S_OK);
    } else {
        RRETURN(S_FALSE);
    }
}

/* IADs methods */

STDMETHODIMP
CWinNTGroup::SetInfo(THIS)
{
    HRESULT hr;
    NET_API_STATUS nasStatus;
    WCHAR szHostServerName[MAX_PATH];
    LPBYTE lpBuffer = NULL;
    DWORD dwGroupType = _GroupType;


    //
    // We need to see if the cache has changed a value for
    // groupType and use that info down the line.
    //

    hr = GetDWORDPropertyFromCache(
         _pPropertyCache,
         TEXT("groupType"),
         &dwGroupType
         );

    if (SUCCEEDED(hr)) {
        //
        // Verify the value
        //
        if ((dwGroupType != WINNT_GROUP_LOCAL)
            && (dwGroupType != WINNT_GROUP_GLOBAL)) {

            //
            // This is bad value so we need to BAIL
            //
            hr = E_ADS_BAD_PARAMETER;
        }
        else {
            if (GetObjectState() == ADS_OBJECT_UNBOUND)
                _GroupType = dwGroupType;
            else
               if (_GroupType != dwGroupType) {
                   hr = E_ADS_BAD_PARAMETER;
               }
        }

    } else {

        dwGroupType = _GroupType;
        hr = S_OK;
    }

    BAIL_ON_FAILURE(hr);


    if (_ParentType == WINNT_DOMAIN_ID) {

        hr = WinNTGetCachedDCName(
                 _DomainName,
                 szHostServerName,
                 _Credentials.GetFlags()
                 );
    } else {

        hr = MakeUncName(
                 _ServerName,
                 szHostServerName
                 );
    }

    BAIL_ON_FAILURE(hr);


    if (GetObjectState() == ADS_OBJECT_UNBOUND) {

        if (dwGroupType == WINNT_GROUP_GLOBAL) {

            if (_ParentType == WINNT_DOMAIN_ID) {

                hr = WinNTCreateGlobalGroup(
                            szHostServerName + 2,
                            _Name
                            );
                BAIL_ON_FAILURE(hr);


            }else {

                hr = WinNTCreateGlobalGroup(
                            _ServerName,
                            _Name
                            );
                BAIL_ON_FAILURE(hr);

            }

        }
        else {

            //
            // Group type has to be local
            //

            hr = WinNTCreateLocalGroup(
                     szHostServerName + 2,
                     _Name
                     );

            BAIL_ON_FAILURE(hr);

        }

         SetObjectState(ADS_OBJECT_BOUND);

    } // if Object not bound

    if (dwGroupType == WINNT_GROUP_GLOBAL) {

        nasStatus = NetGroupGetInfo(
                        szHostServerName,
                        _Name,
                        1,
                        &lpBuffer
                        );

    } else {

        nasStatus = NetLocalGroupGetInfo(
                        szHostServerName,
                        _Name,
                        1,
                        &lpBuffer
                        );

    }

    hr = HRESULT_FROM_WIN32(nasStatus);
    BAIL_ON_FAILURE(hr);

    hr = Marshall_Set_Level1(
             szHostServerName,
             TRUE,
             lpBuffer
             );
    BAIL_ON_FAILURE(hr);

    if(SUCCEEDED(hr))
        _pPropertyCache->ClearModifiedFlags();

    //
    // objectSid not writable
    //

error:
    if (lpBuffer) {
        NetApiBufferFree(lpBuffer);
    }

    RRETURN_EXP_IF_ERR(hr);
}


STDMETHODIMP
CWinNTGroup::GetInfo(THIS)
{
    HRESULT hr;

    if (GetObjectState() == ADS_OBJECT_UNBOUND) {

    RRETURN_EXP_IF_ERR(E_ADS_OBJECT_UNBOUND);

    }


    _pPropertyCache->flushpropcache();

    //
    // Need to add the group type attribute here.
    //
    hr = SetDWORDPropertyInCache(
             _pPropertyCache,
             TEXT("groupType"),
             _GroupType,
             TRUE           // fExplicit
             );
    //
    // GROUP_INFO
    //

    hr = GetInfo(1, TRUE);

    BAIL_ON_FAILURE(hr);
    //
    // objectSid
    //

    hr = GetInfo(20, TRUE);

error :

    RRETURN(hr);

}

STDMETHODIMP
CWinNTGroup::ImplicitGetInfo(THIS)
{
    HRESULT hr;

    if (GetObjectState() == ADS_OBJECT_UNBOUND) {

    RRETURN_EXP_IF_ERR(E_ADS_OBJECT_UNBOUND);

    }

    //
    // Need to add the group type attribute here.
    //
    hr = SetDWORDPropertyInCache(
             _pPropertyCache,
             TEXT("groupType"),
             _GroupType,
             FALSE           // fExplicit
             );
    //
    // GROUP_INFO
    //

    hr = GetInfo(1, FALSE);

    BAIL_ON_FAILURE(hr);
    //
    // objectSid
    //

    hr = GetInfo(20, FALSE);

error :

    RRETURN(hr);

}

HRESULT
CWinNTGroup::AllocateGroupObject(
    CWinNTGroup ** ppGroup
    )
{
    CWinNTGroup FAR * pGroup = NULL;
    CAggregatorDispMgr FAR * pDispMgr = NULL;
    CPropertyCache FAR * pPropertyCache = NULL;
    HRESULT hr = S_OK;


    pGroup = new CWinNTGroup();
    if (pGroup == NULL) {
        hr = E_OUTOFMEMORY;
    }
    BAIL_ON_FAILURE(hr);

    pDispMgr = new CAggregatorDispMgr;
    if (pDispMgr == NULL) {
        hr = E_OUTOFMEMORY;
    }
    BAIL_ON_FAILURE(hr);

    hr = LoadTypeInfoEntry(
                pDispMgr,
                LIBID_ADs,
                IID_IADsGroup,
                (IADsGroup *)pGroup,
                DISPID_REGULAR
                );
    BAIL_ON_FAILURE(hr);

    hr = LoadTypeInfoEntry(
                pDispMgr,
                LIBID_ADs,
                IID_IADsPropertyList,
                (IADsPropertyList *)pGroup,
                DISPID_VALUE
                );
    BAIL_ON_FAILURE(hr);

    hr = CPropertyCache::createpropertycache(
             GroupClass,
             gdwGroupTableSize,
             (CCoreADsObject *)pGroup,
             &pPropertyCache
             );
    BAIL_ON_FAILURE(hr);

    pDispMgr->RegisterPropertyCache(
                pPropertyCache
                );

    pGroup->_pPropertyCache = pPropertyCache;
    pGroup->_pDispMgr = pDispMgr;
    *ppGroup = pGroup;

    RRETURN(hr);

error:

    delete  pPropertyCache;
    delete  pDispMgr;
    delete  pGroup;

    RRETURN(hr);

}


//
// For current implementation in clocgroup:
// If this function is called as a public function (ie. by another
// modual/class), fExplicit must be FALSE since the cache is NOT
// flushed in this function.
//
// External functions should ONLY call GetInfo(no param) for explicit
// GetInfo. This will flush the cache properly.
//

STDMETHODIMP
CWinNTGroup::GetInfo(
    THIS_ DWORD dwApiLevel,
    BOOL fExplicit
    )
{

    HRESULT hr=E_FAIL;

    switch (dwApiLevel) {

    case 1:

        hr = GetStandardInfo(
                dwApiLevel,
                fExplicit
                );
        RRETURN_EXP_IF_ERR(hr);

    case 20:

        hr = GetSidInfo(
                fExplicit
                );
        RRETURN_EXP_IF_ERR(hr);

    default:

        RRETURN_EXP_IF_ERR(E_ADS_BAD_PARAMETER);
    }
}


HRESULT
CWinNTGroup::GetStandardInfo(
    DWORD dwApiLevel,
    BOOL fExplicit
    )
{

    NET_API_STATUS nasStatus;
    LPBYTE lpBuffer = NULL;
    HRESULT hr;
    WCHAR szHostServerName[MAX_PATH];



    if (GetObjectState() == ADS_OBJECT_UNBOUND) {

        RRETURN_EXP_IF_ERR(E_ADS_OBJECT_UNBOUND);
    }

    if (_ParentType == WINNT_DOMAIN_ID) {

        hr = WinNTGetCachedDCName(
                        _DomainName,
                        szHostServerName,
                        _Credentials.GetFlags()
                        );
        BAIL_ON_FAILURE(hr);
    }else {

        hr = MakeUncName(
                _ServerName,
                szHostServerName
                );
        BAIL_ON_FAILURE(hr);

    }

    //
    // Since the object is bound, the groupType has to be
    // _GroupType and cannot change.
    //

    if (_GroupType == WINNT_GROUP_GLOBAL) {

        nasStatus = NetGroupGetInfo(
                        szHostServerName,
                        _Name,
                        dwApiLevel,
                        &lpBuffer
                        );

    } else {

        nasStatus = NetLocalGroupGetInfo(
                        szHostServerName,
                        _Name,
                        dwApiLevel,
                        &lpBuffer
                        );

    }

    hr = HRESULT_FROM_WIN32(nasStatus);
    BAIL_ON_FAILURE(hr);

    hr = UnMarshall(
            lpBuffer,
            dwApiLevel,
            fExplicit
            );
    BAIL_ON_FAILURE(hr);

error:
    if (lpBuffer) {
        NetApiBufferFree(lpBuffer);
    }

    RRETURN_EXP_IF_ERR(hr);
}

HRESULT
CWinNTGroup::UnMarshall(
    LPBYTE lpBuffer,
    DWORD dwApiLevel,
    BOOL fExplicit
    )
{
    HRESULT hr;
    ADsAssert(lpBuffer);
    switch (dwApiLevel) {
    case 1:
        hr = UnMarshall_Level1(fExplicit, lpBuffer);
        break;

    default:
        hr = E_FAIL;

    }
    RRETURN_EXP_IF_ERR(hr);
}

HRESULT
CWinNTGroup::UnMarshall_Level1(BOOL fExplicit, LPBYTE pBuffer)
{
    BSTR bstrData = NULL;
    LPGROUP_INFO_1 pGroupInfo1 = NULL;
    LPLOCALGROUP_INFO_1 pLocalGroupInfo1 = NULL;
    HRESULT hr = S_OK;

    hr = SetLPTSTRPropertyInCache(
                _pPropertyCache,
                TEXT("Name"),
                _Name,
                fExplicit
                );

    if (_GroupType == WINNT_GROUP_GLOBAL) {

        pGroupInfo1 = (LPGROUP_INFO_1)pBuffer;

        hr = SetLPTSTRPropertyInCache(
                 _pPropertyCache,
                 TEXT("Description"),
                 pGroupInfo1->grpi1_comment,
                 fExplicit
                 );
    }
    else {

        pLocalGroupInfo1 = (LPLOCALGROUP_INFO_1) pBuffer;

        hr = SetLPTSTRPropertyInCache(
                 _pPropertyCache,
                 TEXT("Description"),
                 pLocalGroupInfo1->lgrpi1_comment,
                 fExplicit
                 );
    }


    RRETURN(hr);
}


HRESULT
CWinNTGroup::Prepopulate(
    BOOL fExplicit,
    PSID pSid               // OPTIONAL
    )
{
    HRESULT hr = S_OK;

    DWORD dwErr = 0;
    DWORD dwSidLength = 0;
    
    if (pSid) {

        //
        // On NT4 for some reason GetLengthSID does not set lasterror to 0
        //
        SetLastError(NO_ERROR);

        dwSidLength = GetLengthSid((PSID) pSid);

        //
        // This is an extra check to make sure that we have the
        // correct length.
        //
        dwErr = GetLastError();
        if (dwErr != NO_ERROR) {
            hr = HRESULT_FROM_WIN32(dwErr);
            BAIL_ON_FAILURE(hr);
        }
    
        hr = SetOctetPropertyInCache(
                    _pPropertyCache,
                    TEXT("objectSid"),
                    (PBYTE) pSid,
                    dwSidLength,
                    TRUE
                    );
        BAIL_ON_FAILURE(hr);
    }

error:

    RRETURN(hr);
}


HRESULT
CWinNTGroup::Marshall_Set_Level1(
    LPWSTR szHostServerName,
    BOOL fExplicit,
    LPBYTE pBuffer
    )
{
    LPGROUP_INFO_1 pGroupInfo1 = NULL;
    LPLOCALGROUP_INFO_1 pLocalGroupInfo1 = NULL;
    HRESULT hr = S_OK;
    NET_API_STATUS nasStatus;
    DWORD dwParmErr;
    LPWSTR  pszDescription = NULL;

    hr = GetLPTSTRPropertyFromCache(
                    _pPropertyCache,
                    TEXT("Description"),
                    &pszDescription
                    );

    if(SUCCEEDED(hr)) {

        if (_GroupType == WINNT_GROUP_GLOBAL) {

            // hr = UM_GET_BSTR_PROPERTY(_pGenInfo,Description, bstrData);

            //
            // This should in reality call a virtual function of a derived
            // class,  beta fix!
            //

            pGroupInfo1 = (LPGROUP_INFO_1)pBuffer;
            pGroupInfo1->grpi1_comment = pszDescription;

            //
            // Now perform the Set call.
            //

            nasStatus = NetGroupSetInfo(
                            szHostServerName,
                            _Name,
                            1,
                            (LPBYTE)pGroupInfo1,
                            &dwParmErr
                            );

        }
        else {

            pLocalGroupInfo1 = (LPLOCALGROUP_INFO_1)pBuffer;
            pLocalGroupInfo1->lgrpi1_comment = pszDescription;

            nasStatus = NetLocalGroupSetInfo(
                            szHostServerName,
                            _Name,
                            1,
                            (LPBYTE)pLocalGroupInfo1,
                            &dwParmErr
                            );

        }

        hr = HRESULT_FROM_WIN32(nasStatus);
        BAIL_ON_FAILURE(hr);

    }else {

        //
        // This is because there is no data to marshall
        //

        hr = S_OK;

    }

error:

    if (pszDescription) {
        FreeADsStr(pszDescription);
    }

    RRETURN(hr);
}

HRESULT
CWinNTGroup::Marshall_Create_Level1(
    LPWSTR szHostServerName,
    LPGROUP_INFO_1 pGroupInfo1
    )
{

    //
    // This routine is not called from anywhere ???
    //
    HRESULT hr = S_OK;
    NET_API_STATUS nasStatus = ERROR_INVALID_DATA;
    DWORD dwParmErr;

    if (_GroupType == WINNT_GROUP_GLOBAL) {

        pGroupInfo1->grpi1_name = _Name;
        pGroupInfo1->grpi1_comment = NULL;
        nasStatus = NetGroupAdd(
                        szHostServerName,
                        1,
                        (LPBYTE)pGroupInfo1,
                        &dwParmErr
                        );
    }
    else {

        ADsAssert(!"Group type is bad internally!");

        /*
        pLocalGroupInfo1->lgrp1_name = _Name;
        pLocalGroupInfo1->grp1_comment = NULL;
        nasStatus = NetLocalGroupAdd(
                        szHostServerName,
                        1,
                        (LPBYTE)pLocalGroupInfo1,
                        &dwParmErr
                        );
                        */


    }

    hr = HRESULT_FROM_WIN32(nasStatus);
    BAIL_ON_FAILURE(hr);

error:

    RRETURN(hr);
}


HRESULT
CWinNTGroup::GetSidInfo(
    IN BOOL fExplicit
    )
{
    HRESULT hr = E_FAIL;
    WCHAR szHostServerName[MAX_PATH];

    //
    // Get Server Name
    //

    if (_ParentType == WINNT_DOMAIN_ID) {

        hr = WinNTGetCachedDCName(
                    _DomainName,
                    szHostServerName,
                    _Credentials.GetFlags()
                    );
        BAIL_ON_FAILURE(hr);

    }else {

       hr = MakeUncName(
                _ServerName,
                szHostServerName
                );
    }

    //
    // Get Sid of this group and store in cache if fExplicit.
    //

    hr = GetSidIntoCache(
            szHostServerName,
            _Name,
            _pPropertyCache,
            fExplicit
            );
    BAIL_ON_FAILURE(hr);


error:

    RRETURN(hr);
}