//---------------------------------------------------------------------------
//
//  Microsoft Windows
//  Copyright (C) Microsoft Corporation, 1992 - 1996
//
//  File:  cuser.cxx
//
//  Contents:  Host user object code
//
//  History:   Feb-14-96     t-ptam    Created.
//
//----------------------------------------------------------------------------

#include "NWCOMPAT.hxx"
#pragma hdrstop

//
//  Macro-ized implementation.
//

DEFINE_IDispatch_Implementation(CNWCOMPATUser)

DEFINE_IADs_TempImplementation(CNWCOMPATUser)

DEFINE_IADs_PutGetImplementation(CNWCOMPATUser, UserClass, gdwUserTableSize)

DEFINE_IADsPropertyList_Implementation(CNWCOMPATUser, UserClass, gdwUserTableSize)

//----------------------------------------------------------------------------
//
//  Function: CNWCOMPATUser::CNWCOMPATUser()
//
//  Synopsis:
//
//----------------------------------------------------------------------------
CNWCOMPATUser::CNWCOMPATUser():
        _pDispMgr(NULL),
        _pPropertyCache(NULL),
        _ParentType(0),
        _ServerName(NULL),
        _szHostServerName(NULL),
        _hConn(NULL)
{
    ENLIST_TRACKING(CNWCOMPATUser);
}

//----------------------------------------------------------------------------
//
//  Function: CNWCOMPATUser::CreateUser
//
//  Synopsis:
//
//----------------------------------------------------------------------------
HRESULT
CNWCOMPATUser::CreateUser(
    BSTR Parent,
    ULONG ParentType,
    BSTR ServerName,
    BSTR UserName,
    CCredentials &Credentials,
    DWORD dwObjectState,
    REFIID riid,
    void **ppvObj
    )
{
    CNWCOMPATUser FAR * pUser = NULL;
    HRESULT hr = S_OK;

    hr = AllocateUserObject(&pUser);
    BAIL_ON_FAILURE(hr);

    hr = pUser->InitializeCoreObject(
                    Parent,
                    UserName,
                    USER_CLASS_NAME,
                    USER_SCHEMA_NAME,
                    CLSID_NWCOMPATUser,
                    dwObjectState
                    );
    BAIL_ON_FAILURE(hr);

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

    hr = ADsAllocString( ServerName,  &pUser->_szHostServerName);
    BAIL_ON_FAILURE(hr);

    pUser->_Credentials = Credentials;

    //
    // Get a handle to the bindery this object resides on.
    //

    hr = NWApiGetBinderyHandle(
             &pUser->_hConn,
             pUser->_ServerName,
             pUser->_Credentials
             );
    BAIL_ON_FAILURE(hr);


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

    pUser->Release();
    RRETURN(hr);

error:
    delete pUser;

    NW_RRETURN_EXP_IF_ERR(hr);
}

//----------------------------------------------------------------------------
//
//  Function: CNWCOMPATUser::~CNWCOMPATUser
//
//  Synopsis:
//
//----------------------------------------------------------------------------
CNWCOMPATUser::~CNWCOMPATUser( )
{
    ADsFreeString(_ServerName);
    ADsFreeString(_szHostServerName);

    delete _pDispMgr;

    delete _pPropertyCache;

    if (_hConn)
        NWApiReleaseBinderyHandle(_hConn);
}

//----------------------------------------------------------------------------
//
//  Function: CNWCOMPATUser::QueryInterface
//
//  Synopsis:
//
//----------------------------------------------------------------------------
STDMETHODIMP
CNWCOMPATUser::QueryInterface(
    REFIID iid,
    LPVOID FAR* ppv
    )
{
    if (ppv == NULL) {
        RRETURN(E_POINTER);
    }

    if (IsEqualIID(iid, IID_IUnknown))
    {
        *ppv = (IADsUser FAR *) this;
    }
    else if (IsEqualIID(iid, IID_IADsUser))
    {
        *ppv = (IADsUser FAR *) this;
    }
    else if (IsEqualIID(iid, IID_IADs))
    {
        *ppv = (IADsUser FAR *) this;
    }
    else if (IsEqualIID(iid, IID_IDispatch))
    {
        *ppv = (IADsUser FAR *) this;
    }
    else if (IsEqualIID(iid, IID_ISupportErrorInfo))
    {
        *ppv = (ISupportErrorInfo FAR *) this;
    }
    else if (IsEqualIID(iid, IID_IADsPropertyList))
    {
        *ppv = (IADsPropertyList FAR *) this;
    }
    else
    {
        *ppv = NULL;
        return E_NOINTERFACE;
    }
    AddRef();
    return NOERROR;
}

/* ISupportErrorInfo method */
//----------------------------------------------------------------------------
//
//  Function: CNWCOMPATUser::InterfaceSupportsErrorInfo
//
//  Synopsis:
//
//----------------------------------------------------------------------------
STDMETHODIMP
CNWCOMPATUser::InterfaceSupportsErrorInfo(
    THIS_ REFIID riid
    )
{
    if (IsEqualIID(riid, IID_IADs) ||
        IsEqualIID(riid, IID_IADsUser) ||
        IsEqualIID(riid, IID_IADsPropertyList)) {
        RRETURN(S_OK);
    } else {
        RRETURN(S_FALSE);
    }
}

/* IADs methods */

//----------------------------------------------------------------------------
//
//  Function: CNWCOMPATUser::SetInfo
//
//  Synopsis:
//
//----------------------------------------------------------------------------
STDMETHODIMP
CNWCOMPATUser::SetInfo(THIS)
{
    HRESULT hr = S_OK;
    POBJECTINFO pObjectInfo = NULL;
    NW_USER_INFO NwUserInfo = {NULL, NULL, NULL, NULL};

    //
    // Bind an object to a real tangible resource if it is not bounded already.
    //

    if (GetObjectState() == ADS_OBJECT_UNBOUND) {

        hr = BuildObjectInfo(
                _Parent,
                _Name,
                &pObjectInfo
                );
        BAIL_ON_FAILURE(hr);

        hr = NWApiMakeUserInfo(
                 pObjectInfo->ComponentArray[0],
                 pObjectInfo->ComponentArray[1],
                 L"",                              // empty password initially,
                 _Credentials,
                 &NwUserInfo
                 );
        BAIL_ON_FAILURE(hr);

        hr = NWApiCreateUser(
                 &NwUserInfo
                 );
        BAIL_ON_FAILURE(hr);

        SetObjectState(ADS_OBJECT_BOUND);
    }

    //
    // Persist changes.
    //

    hr = SetInfo(USER_WILD_CARD_ID);
    BAIL_ON_FAILURE(hr);

error:

    if (pObjectInfo) {
        FreeObjectInfo(pObjectInfo);
    }

    (void) NWApiFreeUserInfo(&NwUserInfo) ;

    NW_RRETURN_EXP_IF_ERR(hr);
}

//----------------------------------------------------------------------------
//
//  Function: CNWCOMPATUser::GetInfo
//
//  Synopsis:
//
//----------------------------------------------------------------------------
STDMETHODIMP
CNWCOMPATUser::GetInfo(THIS)
{

   _pPropertyCache->flushpropcache();

   RRETURN(GetInfo(
               TRUE,
               USER_WILD_CARD_ID
               ));
}

//----------------------------------------------------------------------------
//
//  Function: CNWCOMPATUser::AllocateUserObject
//
//  Synopsis:
//
//----------------------------------------------------------------------------
HRESULT
CNWCOMPATUser::AllocateUserObject(
    CNWCOMPATUser ** ppUser
    )
{
    CNWCOMPATUser FAR * pUser = NULL;
    CDispatchMgr FAR * pDispMgr = NULL;
    CPropertyCache FAR * pPropertyCache = NULL;
    HRESULT hr = S_OK;

    //
    // Allocate memory for a  User object.
    //

    pUser = new CNWCOMPATUser();
    if (pUser == NULL) {
        hr = E_OUTOFMEMORY;
    }
    BAIL_ON_FAILURE(hr);

    //
    // Create dispatch manager.
    //

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

    //
    // Load type info.
    //

    hr = LoadTypeInfoEntry(
             pDispMgr,
             LIBID_ADs,
             IID_IADsUser,
             (IADsUser *)pUser,
             DISPID_REGULAR
             );
    BAIL_ON_FAILURE(hr);

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

    hr = CPropertyCache::createpropertycache(
             UserClass,
             gdwUserTableSize,
             (CCoreADsObject *)pUser,
             &pPropertyCache
             );
    BAIL_ON_FAILURE(hr);

    //
    // Return.
    //
    pUser->_pPropertyCache = pPropertyCache;

    pUser->_pDispMgr = pDispMgr;
    *ppUser = pUser;

    RRETURN(hr);

error:
    delete pDispMgr;
    delete pPropertyCache;
    delete pUser;

    RRETURN(hr);
}

//----------------------------------------------------------------------------
//
//  Function: CNWCOMPATUser::SetInfo
//
//  Synopsis:
//
//----------------------------------------------------------------------------
STDMETHODIMP
CNWCOMPATUser::SetInfo(THIS_ DWORD dwPropertyID)
{
    HRESULT       hr = S_OK;


    //
    // Persist changes in cache.
    //

    hr = SetBusinessInfo(_hConn);
    if (hr == E_ADS_PROPERTY_NOT_FOUND) {
        // not a real failure, but a missing attrib
        // BUGBUG: should create if missing
        hr = S_OK;
    }

    BAIL_ON_FAILURE(hr);


    hr = SetAccountRestrictions(_hConn);
    if (hr == E_ADS_PROPERTY_NOT_FOUND) {
        // not a real failure, but a missing attrib
        // BUGBUG: should create if missing
        hr = S_OK;
    }
    BAIL_ON_FAILURE(hr);

error:

    NW_RRETURN_EXP_IF_ERR(hr);
}

//----------------------------------------------------------------------------
//
//  Function: CNWCOMPATUser::SetBusinessInfo
//
//  Synopsis:
//
//----------------------------------------------------------------------------
HRESULT
CNWCOMPATUser::SetBusinessInfo(
    NWCONN_HANDLE hConn
    )
{
    LPWSTR  lpszRightSize = NULL;
    LPWSTR  pszFullName = NULL;
    CHAR    szData[MAX_FULLNAME_LEN + 1];
    HRESULT hr = S_OK;

    //
    // Set FullName.
    //

    hr = GetLPTSTRPropertyFromCache(
                    _pPropertyCache,
                    TEXT("FullName"),
                    &pszFullName
                    );

    if (SUCCEEDED(hr)) {

        //
        // Cut the FullName down to no more than MAX_FULLNAME_LEN of characters.
        //

        lpszRightSize = (LPWSTR) AllocADsMem(
                                     sizeof(WCHAR) * (MAX_FULLNAME_LEN + 1)
                                     );
        if (!lpszRightSize) {
            NW_RRETURN_EXP_IF_ERR(E_OUTOFMEMORY);
        }

        lpszRightSize[MAX_FULLNAME_LEN] = 0;

        wcsncpy(
            lpszRightSize,
            pszFullName,
            MAX_FULLNAME_LEN
            );

        //
        // Convert bstr in ANSI string.
        //

        UnicodeToAnsiString(
            lpszRightSize,
            szData,
            0
            );

        //
        // Commit change.
        //

        hr = NWApiWriteProperty(
                 hConn,
                 _Name,
                 OT_USER,
                 NW_PROP_IDENTIFICATION,
                 (LPBYTE) szData
                 );
        BAIL_ON_FAILURE(hr);

        FreeADsMem(lpszRightSize);
    }

error:

    if (pszFullName) {
        FreeADsStr(pszFullName);
    }

    RRETURN(S_OK);
}

//----------------------------------------------------------------------------
//
//  Function: CNWCOMPATUser::SetAccountRestrictions
//
//  Synopsis:
//
//----------------------------------------------------------------------------
HRESULT
CNWCOMPATUser::SetAccountRestrictions(
    NWCONN_HANDLE hConn
    )
{
    BOOL             fModified = FALSE;
    DATE             daDate  = 0;
    DWORD            dwNumSegment;
    HRESULT          hr = S_OK;
    LC_STRUCTURE     LoginCtrl;
    LONG             lData = 0;
    LP_RPLY_SGMT_LST lpReplySegment = NULL;
    LP_RPLY_SGMT_LST lpTemp = NULL;
    SYSTEMTIME       SysTime;
    USER_DEFAULT     UserDefault;
    BOOL             fBool;
    WORD             wDay = 0;
    WORD             wMonth = 0;
    WORD             wYear = 0;
    WCHAR            szTemp[MAX_PATH];
    BYTE             byDateTime[6];
    BOOL             fAccntLckModified;

    hr = NWApiGetLOGIN_CONTROL(
             hConn,
             _Name,
             &LoginCtrl
             );
    BAIL_ON_FAILURE(hr);

    //
    // SET AccountDisabled.
    //

    hr = GetBOOLPropertyFromCache(
                _pPropertyCache,
                TEXT("AccountDisabled"),
                &fBool
                );

    if (SUCCEEDED(hr)) {

        LoginCtrl.byAccountDisabled = (BYTE) fBool;

        fModified = TRUE;
    }

    //
    // SET AccountExpirationDate.
    //

    memset(byDateTime, 0, 6);
    hr = GetNw312DATEPropertyFromCache(
                _pPropertyCache,
                TEXT("AccountExpirationDate"),
                byDateTime
                );

    if (SUCCEEDED(hr)) {

        LoginCtrl.byAccountExpires[0] = (BYTE) byDateTime[0];
        LoginCtrl.byAccountExpires[1] = (BYTE) byDateTime[1];
        LoginCtrl.byAccountExpires[2] = (BYTE) byDateTime[2];

        fModified = TRUE;
    }

    //
    // SET AccountCanExpire.
    //

    hr = GetBOOLPropertyFromCache(
                _pPropertyCache,
                TEXT("AccountCanExpire"),
                &fBool
                );

    if (SUCCEEDED(hr)) {

        if (fBool == FALSE) {

            LoginCtrl.byAccountExpires[0] = 0;
            LoginCtrl.byAccountExpires[1] = 0;
            LoginCtrl.byAccountExpires[2] = 0;

            fModified = TRUE;

        }
    }

    //
    // SET GraceLoginsAllowed.
    //

    hr = GetDWORDPropertyFromCache(
                _pPropertyCache,
                TEXT("GraceLoginsAllowed"),
                (PDWORD)&lData
                );

    if (SUCCEEDED(hr)) {

        LoginCtrl.byGraceLoginReset = (BYTE) lData;

        fModified = TRUE;
    }

    //
    // SET GraceLoginsRemaining.
    //

    hr = GetDWORDPropertyFromCache(
                _pPropertyCache,
                TEXT("GraceLoginsRemaining"),
                (PDWORD)&lData
                );

    if (SUCCEEDED(hr)) {

        LoginCtrl.byGraceLogins = (BYTE) lData;

        fModified = TRUE;
    }

    //
    // SET IsAccountLocked.
    //

    //
    // if this property not modified in cache, no need to set on svr
    //
    hr =  _pPropertyCache->propertyismodified(
                TEXT("IsAccountLocked"),
                &fAccntLckModified
                );

    if ( SUCCEEDED(hr) && fAccntLckModified==TRUE ) {

        hr = GetBOOLPropertyFromCache(
                _pPropertyCache,
                TEXT("IsAccountLocked"),
                &fBool
                );

        if (SUCCEEDED(hr)) {

            //
            // If fBool is changed from TRUE to FALSE, set wBadLogins 
            // back to 0  -> this will unlock account on nw svr
            //

            if (fBool == FALSE) {

                LoginCtrl.wBadLogins = 0;
                fModified = TRUE;

            }else {

                //
                // Reset it to FALSE if it is changed to TRUE.
                // -> cannot lock an account on nwsvr thru' adsi
                //

                fBool = FALSE;

                hr = SetBOOLPropertyInCache(
                        _pPropertyCache,
                        TEXT("IsAccountLocked"),
                        fBool,
                        TRUE
                        );
                BAIL_ON_FAILURE(hr);
            }
        }
    }

    //
    // SET IsAdmin.
    //

    hr = GetBOOLPropertyFromCache(
                _pPropertyCache,
                TEXT("IsAdmin"),
                &fBool
                );

    if (SUCCEEDED(hr)) {

        hr = NWApiUserAsSupervisor(
                 hConn,
                 _Name,
                 fBool
                 );

        //
        // For beta, disabling the bail. It does not work in the user not
        // supervisor mode.
        //

        // BAIL_ON_FAILURE(hr);
    }

    //
    // SET MaxLogins.
    //

    hr = GetDWORDPropertyFromCache(
                _pPropertyCache,
                TEXT("MaxLogins"),
                (PDWORD)&lData
                );

    if (SUCCEEDED(hr)) {

        LoginCtrl.wMaxConnections = NWApiReverseWORD(
                                        (WORD) lData
                                        );

        fModified = TRUE;
    }

    //
    // SET PasswordExpirationDate.
    //

    memset(byDateTime, 0, 6);
    hr = GetNw312DATEPropertyFromCache(
                    _pPropertyCache,
                    TEXT("PasswordExpirationDate"),
                    byDateTime
                    );

    if (SUCCEEDED(hr)) {


        LoginCtrl.byPasswordExpires[0] = (BYTE) byDateTime[0];
        LoginCtrl.byPasswordExpires[1] = (BYTE) byDateTime[1];
        LoginCtrl.byPasswordExpires[2] = (BYTE) byDateTime[2];

        fModified = TRUE;
    }

    //
    // SET PasswordCanExpire.
    //

    hr = GetBOOLPropertyFromCache(
                _pPropertyCache,
                TEXT("PasswordCanExpire"),
                &fBool
                );

    if (SUCCEEDED(hr)) {

        if (fBool == FALSE) {

            //
            // If passowrd cannot expire, set password expiration date to zero.
            // This is what SysCon does.
            //

            LoginCtrl.byPasswordExpires[0] = 0;
            LoginCtrl.byPasswordExpires[1] = 0;
            LoginCtrl.byPasswordExpires[2] = 0;

            fModified = TRUE;

        }
    }

    //
    // SET PasswordMinimumLength.
    //

    hr = GetDWORDPropertyFromCache(
                    _pPropertyCache,
                    TEXT("PasswordMinimumLength"),
                    (PDWORD)&lData
                    );

    if (SUCCEEDED(hr)) {

        LoginCtrl.byMinPasswordLength = (BYTE) lData;

        fModified = TRUE;
    }

    //
    // SET PasswordRequired.  The section below must goes before "Set
    // PasswordMinimumLength" for it to make sense.
    //

    hr = GetBOOLPropertyFromCache(
                    _pPropertyCache,
                    TEXT("PasswordRequired"),
                    &fBool
                    );


    if (SUCCEEDED(hr)) {

        //
        // If Password is required, set PasswordMinimumLength to default value.
        //

        //
        // If Password is not required, set PasswordMinimumLength to 0.  Again,
        // this is what SysCon does.
        //

        if (fBool) {
            if (!LoginCtrl.byMinPasswordLength) {
                LoginCtrl.byMinPasswordLength = DEFAULT_MIN_PSWD_LEN;
            }
        }else{
            LoginCtrl.byMinPasswordLength = 0;
        }

        fModified = TRUE;
    }

    //
    // Set LoginHours
    //
	OctetString octString;

    hr = GetOctetPropertyFromCache(
                _pPropertyCache,
                TEXT("LoginHours"),
                &octString
                );

    if (SUCCEEDED(hr)) {
        memcpy(LoginCtrl.byLoginTimes, octString.pByte, octString.dwSize);
		FreeADsMem(octString.pByte);
        fModified = TRUE;
    }

    //
    // Set RequireUniquePassword.
    //

    hr = GetBOOLPropertyFromCache(
                _pPropertyCache,
                TEXT("RequireUniquePassword"),
                &fBool
                );

    if (SUCCEEDED(hr)) {

        LoginCtrl.byRestrictions =  fBool ? REQUIRE_UNIQUE_PSWD : 0;

        fModified = TRUE;

    }

    //
    // Commit changes of the properties associated with LOGIN_CONTROL.
    //

    if (fModified == TRUE) {

        hr = NWApiWriteProperty(
                 hConn,
                 _Name,
                 OT_USER,
                 NW_PROP_LOGIN_CONTROL,
                 (LPBYTE) &LoginCtrl
                 );
    }
    else {

        hr = S_OK;
    }

error:

    if (lpReplySegment) {
        DELETE_LIST(lpReplySegment);
    }

    NW_RRETURN_EXP_IF_ERR(hr);
}

//----------------------------------------------------------------------------
//
//  Function: CNWCOMPATUser::GetInfo
//
//  Synopsis:
//
//----------------------------------------------------------------------------
STDMETHODIMP
CNWCOMPATUser::GetInfo(
    BOOL fExplicit,
    DWORD dwPropertyID
    )
{
    HRESULT       hr = S_OK;

    if (GetObjectState() == ADS_OBJECT_UNBOUND) {
        NW_RRETURN_EXP_IF_ERR(E_ADS_OBJECT_UNBOUND);
    }

    //
    // Fill in all property caches with values - explicit, or return the
    // indicated property - implicit.
    //

    if (fExplicit) {
       hr = ExplicitGetInfo(_hConn, fExplicit);
       BAIL_ON_FAILURE(hr);
    }
    else {
       hr = ImplicitGetInfo(_hConn, dwPropertyID, fExplicit);
       BAIL_ON_FAILURE(hr);
    }

error:
    //
    // Release handle.
    //

    NW_RRETURN_EXP_IF_ERR(hr);
}

//----------------------------------------------------------------------------
//
//  Function: CNWCOMPATUser::ExplicitGetInfo
//
//  Synopsis:
//
//----------------------------------------------------------------------------
HRESULT
CNWCOMPATUser::ExplicitGetInfo(
    NWCONN_HANDLE hConn,
    BOOL fExplicit
    )
{
    HRESULT hr = S_OK;
    LC_STRUCTURE LoginCtrlStruct;

    //
    // Get BusinessInfo functional set.
    //

    hr = GetProperty_FullName(
             hConn,
             fExplicit
             );

    if (hr == E_ADS_PROPERTY_NOT_FOUND) {
        // not a real failure, we ignore it and treat it as a missing attrib
        hr = S_OK;
    }

    BAIL_ON_FAILURE(hr);


    // Is it an admin?  This is part of the AccountRestriction functional set, but is
    // independent of LOGIN_CONTROL, so it needs to go here.
    hr = GetProperty_IsAdmin(
             hConn,
             fExplicit
             );

    if (hr == E_ADS_PROPERTY_NOT_FOUND) {
        // not a real failure, we ignore it and treat it as a missing attrib
        hr = S_OK;
    }

    BAIL_ON_FAILURE(hr);

    //
    // Get LOGIN_CONTROL, which is used in AccountRestriction functional set &
    // AccountStatistics functional set.
    //

    hr = NWApiGetLOGIN_CONTROL(
             hConn,
             _Name,
             &LoginCtrlStruct
             );

    if (hr == E_ADS_PROPERTY_NOT_FOUND) {
        // not a real failure, we ignore it and treat it as a missing attrib,
        // and skip the LOGIN_CONTROL-dependent code
        hr = S_OK;
    }
    else
    {
        BAIL_ON_FAILURE(hr);

        //
        // Get AccountRestriction functional set.
        //

        hr = GetProperty_LoginHours(
                 hConn,
                 LoginCtrlStruct,
                 fExplicit
                 );
        BAIL_ON_FAILURE(hr);

        hr = GetProperty_AccountDisabled(

                 hConn,
                 LoginCtrlStruct,
                 fExplicit
                 );
        BAIL_ON_FAILURE(hr);

        hr = GetProperty_AccountExpirationDate(
                 hConn,
                 LoginCtrlStruct,
                 fExplicit
                 );
        BAIL_ON_FAILURE(hr);

        hr = GetProperty_CanAccountExpire(
                 hConn,
                 LoginCtrlStruct,
                 fExplicit
                 );
        BAIL_ON_FAILURE(hr);

        hr = GetProperty_GraceLoginsAllowed(
                 hConn,
                 LoginCtrlStruct,
                 fExplicit
                 );
        BAIL_ON_FAILURE(hr);

        hr = GetProperty_GraceLoginsRemaining(
                 hConn,
                 LoginCtrlStruct,
                 fExplicit
                 );
        BAIL_ON_FAILURE(hr);

        hr = GetProperty_IsAccountLocked(
                 hConn,
                 LoginCtrlStruct,
                 fExplicit
                 );
        BAIL_ON_FAILURE(hr);

        hr = GetProperty_MaxLogins(
                 hConn,
                 LoginCtrlStruct,
                 fExplicit
                 );
        BAIL_ON_FAILURE(hr);

        hr = GetProperty_CanPasswordExpire(
                 hConn,
                 LoginCtrlStruct,
                 fExplicit
                 );
        BAIL_ON_FAILURE(hr);

        hr = GetProperty_PasswordExpirationDate(
                  hConn,
                  LoginCtrlStruct,
                  fExplicit
                  );
        BAIL_ON_FAILURE(hr);

        hr = GetProperty_PasswordMinimumLength(
                  hConn,
                  LoginCtrlStruct,
                  fExplicit
                  );
        BAIL_ON_FAILURE(hr);

        hr = GetProperty_PasswordRequired(
                  hConn,
                  LoginCtrlStruct,
                  fExplicit
                  );
        BAIL_ON_FAILURE(hr);

        hr = GetProperty_RequireUniquePassword(
                  hConn,
                  LoginCtrlStruct,
                  fExplicit
                  );
        BAIL_ON_FAILURE(hr);

        //
        // Get AccountStatistics functional set.
        //

        hr = GetProperty_BadLoginAddress(
                  hConn,
                  LoginCtrlStruct,
                  fExplicit
                  );
        BAIL_ON_FAILURE(hr);

        hr = GetProperty_LastLogin(
                  hConn,
                  LoginCtrlStruct,
                  fExplicit
                  );
        BAIL_ON_FAILURE(hr);
    }

error:
    RRETURN(hr);
}

//----------------------------------------------------------------------------
//
//  Function: CNWCOMPATUser::ImplicitGetInfo
//
//  Synopsis:
//
//----------------------------------------------------------------------------
HRESULT
CNWCOMPATUser::ImplicitGetInfo(
    NWCONN_HANDLE hConn,
    DWORD dwPropertyID,
    BOOL fExplicit
    )
{
    HRESULT hr = S_OK;

    if (dwPropertyID < 100) {
        hr = GetBusinessInfo(
                 hConn,
                 dwPropertyID,
                 fExplicit
                 );
    }
    else if (dwPropertyID < 200) {
        hr = GetAccountRestrictions(
                 hConn,
                 dwPropertyID,
                 fExplicit
                 );
    }
    else if (dwPropertyID < 300) {
        hr = GetAccountStatistics(
                 hConn,
                 dwPropertyID,
                 fExplicit
                 );
    }

    RRETURN(hr);
}

//----------------------------------------------------------------------------
//
//  Function: CNWCOMPATUser::GetBusinessInfo
//
//  Synopsis:
//
//----------------------------------------------------------------------------
HRESULT
CNWCOMPATUser::GetBusinessInfo(
    NWCONN_HANDLE hConn,
    DWORD dwPropertyID,
    BOOL fExplicit
    )
{
    HRESULT hr = S_OK;

    switch (dwPropertyID) {

    case USER_FULLNAME_ID:
         hr = GetProperty_FullName(
                  hConn,
                  fExplicit
                  );
         break;
    }

    RRETURN(hr);
}

//----------------------------------------------------------------------------
//
//  Function: CNWCOMPATUser::GetAccountRestrictions
//
//  Synopsis:
//
//----------------------------------------------------------------------------
HRESULT
CNWCOMPATUser::GetAccountRestrictions(
    NWCONN_HANDLE hConn,
    DWORD dwPropertyID,
    BOOL fExplicit
    )
{
    HRESULT hr = S_OK;
    LC_STRUCTURE LoginCtrlStruct;

    //
    // Get LOGIN_CONTROL.
    //

    hr = NWApiGetLOGIN_CONTROL(
             hConn,
             _Name,
             &LoginCtrlStruct
             );
    BAIL_ON_FAILURE(hr);

    //
    // Get property.
    //

    switch (dwPropertyID) {

    case USER_ACCOUNTDISABLED_ID:
         hr = GetProperty_AccountDisabled(
                  hConn,
                  LoginCtrlStruct,
                  fExplicit
                  );
         break;

    case USER_ACCOUNTEXPIRATIONDATE_ID:
         hr = GetProperty_AccountExpirationDate(
                  hConn,
                  LoginCtrlStruct,
                  fExplicit
                  );
         break;

    case USER_CANACCOUNTEXPIRE_ID:
         hr = GetProperty_CanAccountExpire(
                  hConn,
                  LoginCtrlStruct,
                  fExplicit
                  );
         break;

    case USER_GRACELOGINSALLOWED_ID:
         hr = GetProperty_GraceLoginsAllowed(
                  hConn,
                  LoginCtrlStruct,
                  fExplicit
                  );
                  break;

    case USER_GRACELOGINSREMAINING_ID:
         hr = GetProperty_GraceLoginsRemaining(
                  hConn,
                  LoginCtrlStruct,
                  fExplicit
                  );
         break;

    case USER_ISACCOUNTLOCKED_ID:
         hr = GetProperty_IsAccountLocked(
                  hConn,
                  LoginCtrlStruct,
                  fExplicit
                  );
         break;

    case USER_ISADMIN_ID:
         hr = GetProperty_IsAdmin(
                  hConn,
                  fExplicit
                  );
         break;

    case USER_MAXLOGINS_ID:
         hr = GetProperty_MaxLogins(
                  hConn,
                  LoginCtrlStruct,
                  fExplicit
                  );
         break;

    case USER_CANPASSWORDEXPIRE_ID:
         hr = GetProperty_CanPasswordExpire(
                  hConn,
                  LoginCtrlStruct,
                  fExplicit
                  );
         break;

    case USER_PASSWORDEXPIRATIONDATE_ID:
         hr = GetProperty_PasswordExpirationDate(
                  hConn,
                  LoginCtrlStruct,
                  fExplicit
                  );
         break;

    case USER_PASSWORDMINIMUMLENGTH_ID:
         hr = GetProperty_PasswordMinimumLength(
                  hConn,
                  LoginCtrlStruct,
                  fExplicit
                  );
         break;

    case USER_PASSWORDREQUIRED_ID:
         hr = GetProperty_PasswordRequired(
                  hConn,
                  LoginCtrlStruct,
                  fExplicit
                  );
         break;

    case USER_REQUIREUNIQUEPASSWORD_ID:
         hr = GetProperty_RequireUniquePassword(
                  hConn,
                  LoginCtrlStruct,
                  fExplicit
                  );
         break;

     case USER_LOGINHOURS_ID:
         hr = GetProperty_LoginHours(
                  hConn,
                  LoginCtrlStruct,
                  fExplicit
                  );
         break;

    }

error:

    NW_RRETURN_EXP_IF_ERR(hr);
}

//----------------------------------------------------------------------------
//
//  Function: CNWCOMPATUser::GetAccountStatistics
//
//  Synopsis:
//
//----------------------------------------------------------------------------
HRESULT
CNWCOMPATUser::GetAccountStatistics(
    NWCONN_HANDLE hConn,
    DWORD dwPropertyID,
    BOOL fExplicit
    )
{
    HRESULT hr = S_OK;
    LC_STRUCTURE LoginCtrlStruct;

    //
    // Get LOGIN_CONTROL.
    //

    hr = NWApiGetLOGIN_CONTROL(
             hConn,
             _Name,
             &LoginCtrlStruct
             );
    BAIL_ON_FAILURE(hr);

    //
    // Get property.
    //

    switch (dwPropertyID) {

    case USER_BADLOGINADDRESS_ID:
         hr = GetProperty_BadLoginAddress(
                  hConn,
                  LoginCtrlStruct,
                  fExplicit
                  );
         break;

    case USER_LASTLOGIN_ID:
         hr = GetProperty_LastLogin(
                  hConn,
                  LoginCtrlStruct,
                  fExplicit
                  );
         break;
    }

error:

    RRETURN(hr);
}

//----------------------------------------------------------------------------
//
//  Function: CNWCOMPATUser::GetProperty_FullName
//
//  Synopsis:
//
//----------------------------------------------------------------------------
HRESULT
CNWCOMPATUser::GetProperty_FullName(
    NWCONN_HANDLE hConn,
    BOOL fExplicit
    )
{
    LPWSTR           lpszFullName = NULL;
    CHAR             szFullName[MAX_FULLNAME_LEN + 1];
    DWORD            dwNumSegment = 0;
    HRESULT          hr = S_OK;
    LP_RPLY_SGMT_LST lpReplySegment = NULL;
    LP_RPLY_SGMT_LST lpTemp = NULL; // Used by DELETE_LIST macro below

    //
    // Get IDENTIFICATIOIN.  This property contains the full name of an object.
    //

    hr = NWApiGetProperty(
             _Name,
             NW_PROP_IDENTIFICATION,
             OT_USER,
             hConn,
             &lpReplySegment,
             &dwNumSegment
             );

    //
    // There was a bug marked on this code because NWApiGetProperty would fail with
    // an error if the property didn't exist (raid #34833), and the temp patch was
    // simply to hide all errors and always return S_OK when GetProperty_FullName
    // returned.  Now NWApiGetProperty will return E_ADS_PROPERTY_NOT_FOUND.
    //


    BAIL_ON_FAILURE(hr);

    //
    // Convert result into a UNICODE string.
    //

    strcpy(szFullName, lpReplySegment->Segment);

    lpszFullName = (LPWSTR) AllocADsMem(
                                (strlen(szFullName)+1) * sizeof(WCHAR)
                                );

    AnsiToUnicodeString(
        szFullName,
        lpszFullName,
        0
        );

    //
    // Unmarshall.
    //

    hr = SetLPTSTRPropertyInCache(
                _pPropertyCache,
                TEXT("FullName"),
                (LPWSTR)lpszFullName,
                fExplicit
                );
    BAIL_ON_FAILURE(hr);

error:

    if (lpszFullName) {

        FreeADsMem(lpszFullName);
    }
    if (lpReplySegment) {
        DELETE_LIST(lpReplySegment);
    }



    RRETURN(hr);
}

//----------------------------------------------------------------------------
//
//  Function: CNWCOMPATUser::GetProperty_AccountDisabled
//
//  Synopsis:
//
//----------------------------------------------------------------------------
HRESULT
CNWCOMPATUser::GetProperty_AccountDisabled(
    NWCONN_HANDLE hConn,
    LC_STRUCTURE LoginCtrlStruct,
    BOOL fExplicit
    )
{
    BOOL         dwBool = TRUE;
    HRESULT      hr = S_OK;

    //
    // Put value into a variant.
    //

    dwBool = (BOOL) LoginCtrlStruct.byAccountDisabled;

    //
    // Unmarshall.
    //

    hr = SetBOOLPropertyInCache(
             _pPropertyCache,
             TEXT("AccountDisabled"),
             dwBool,
             fExplicit
             );


    RRETURN(hr);
}

//----------------------------------------------------------------------------
//
//  Function: CNWCOMPATUser::GetProperty_AccountExpirationDate
//
//  Synopsis:
//
//----------------------------------------------------------------------------
HRESULT
CNWCOMPATUser::GetProperty_AccountExpirationDate(
    NWCONN_HANDLE hConn,
    LC_STRUCTURE LoginCtrlStruct,
    BOOL fExplicit
    )
{
    HRESULT      hr = S_OK;
    BYTE byDateTime[6];
    BYTE byNoDateTime[6];

    memset(byNoDateTime, 0, 6);
    memset(byDateTime, 0, 6);
    memcpy(byDateTime, LoginCtrlStruct.byAccountExpires, 3);

    //
    // LoginCtrlSturct.byAccountExpires == 000 indicates no expired date
    //
    if (memcmp(byDateTime, byNoDateTime, 3)!=0) {

        hr = SetNw312DATEPropertyInCache(
                    _pPropertyCache,
                    TEXT("AccountExpirationDate"),
                    byDateTime,
                    fExplicit
                    );

        BAIL_ON_FAILURE(hr);
    }

error:

    RRETURN(hr);
}

//----------------------------------------------------------------------------
//
//  Function: CNWCOMPATUser::GetProperty_CanAccountExpire
//
//  Synopsis:
//
//----------------------------------------------------------------------------
HRESULT
CNWCOMPATUser::GetProperty_CanAccountExpire(
    NWCONN_HANDLE hConn,
    LC_STRUCTURE LoginCtrlStruct,
    BOOL fExplicit
    )
{
    BOOL         dwBool = TRUE;
    HRESULT      hr = S_OK;

    //
    // Account cannot expire if there is no expiration date.
    //

    if ((LoginCtrlStruct.byAccountExpires[0] == 0) &&
        (LoginCtrlStruct.byAccountExpires[1] == 0) &&
        (LoginCtrlStruct.byAccountExpires[2] == 0)) {

        dwBool = FALSE;
    }

    //
    // Unmarshall.
    //

    hr = SetBOOLPropertyInCache(
            _pPropertyCache,
            TEXT("AccountCanExpire"),
            dwBool,
            fExplicit
            );

    RRETURN(hr);
}

//----------------------------------------------------------------------------
//
//  Function: CNWCOMPATUser::GetProperty_GraceLoginsAllowed
//
//  Synopsis:
//
//----------------------------------------------------------------------------
HRESULT
CNWCOMPATUser::GetProperty_GraceLoginsAllowed(
    NWCONN_HANDLE hConn,
    LC_STRUCTURE LoginCtrlStruct,
    BOOL fExplicit
    )
{
    HRESULT      hr = S_OK;
    LONG         lGraceLoginsAllowed = 0;

    //
    // Get "byGraceLoginReset".  The property is not meaningful when it equals
    // to 0xff.
    //

    if (LoginCtrlStruct.byGraceLoginReset != 0xff) {

        lGraceLoginsAllowed = (LONG) LoginCtrlStruct.byGraceLoginReset;
    }

    //
    // Unmarshall.
    //

    hr = SetDWORDPropertyInCache(
             _pPropertyCache,
             TEXT("GraceLoginsAllowed"),
             (DWORD)lGraceLoginsAllowed,
             fExplicit
             );

    RRETURN(hr);
}

//----------------------------------------------------------------------------
//
//  Function: CNWCOMPATUser::GetProperty_GraceLoginsRemaining
//
//  Synopsis:
//
//----------------------------------------------------------------------------
HRESULT
CNWCOMPATUser::GetProperty_GraceLoginsRemaining(
    NWCONN_HANDLE hConn,
    LC_STRUCTURE LoginCtrlStruct,
    BOOL fExplicit
    )
{
    HRESULT      hr = S_OK;
    LONG         lGraceLoginsRemaining = 0;

    //
    // Get "byGraceLogins".  The property is not meaningful when it equals to
    // 0xff.
    //

    if (LoginCtrlStruct.byGraceLogins != 0xff) {

        lGraceLoginsRemaining = (LONG) LoginCtrlStruct.byGraceLogins;
    }

    //
    // Unmarshall.
    //

    hr = SetDWORDPropertyInCache(
             _pPropertyCache,
             TEXT("GraceLoginsRemaining"),
             (DWORD)lGraceLoginsRemaining,
             fExplicit
             );

    RRETURN(hr);
}

//----------------------------------------------------------------------------
//
//  Function: CNWCOMPATUser::GetProperty_IsAccountLocked
//
//  Synopsis:
//
//----------------------------------------------------------------------------
HRESULT
CNWCOMPATUser::GetProperty_IsAccountLocked(
    NWCONN_HANDLE hConn,
    LC_STRUCTURE LoginCtrlStruct,
    BOOL fExplicit
    )
{
    BOOL         dwBool = FALSE;
    HRESULT      hr = S_OK;

    //
    // Account is locked when wBadLogins = 0xffff.
    //

    if (LoginCtrlStruct.wBadLogins == 0xffff) {

        dwBool = TRUE;
    }

    //
    // Unmarshall.
    //

    hr = SetBOOLPropertyInCache(
             _pPropertyCache,
             TEXT("IsAccountLocked"),
             dwBool,
             fExplicit
             );

    RRETURN(hr);
}

//----------------------------------------------------------------------------
//
//  Function: CNWCOMPATUser::GetProperty_IsAdmin
//
//  Synopsis:
//
//----------------------------------------------------------------------------
HRESULT
CNWCOMPATUser::GetProperty_IsAdmin(
    NWCONN_HANDLE hConn,
    BOOL fExplicit
    )
{
    BOOL    dwBool = TRUE;
    HRESULT hr = S_OK;

    //
    // Check if this user has the same security as the supervisor.  If it does,
    // then it is an admin.
    //

    hr = NWApiIsObjectInSet(
             hConn,
             _Name,
             OT_USER,
             NW_PROP_SECURITY_EQUALS,
             NW_PROP_SUPERVISOR,
             OT_USER
             );

    //
    // BUGBUG - for now, any failure is assumed to be "No such object.".  Fix
    // this after bug #33322 is fixed.
    //

    if (FAILED(hr)) {
        dwBool = FALSE;
    }

    //
    // Unmarshall.
    //

    hr = SetBOOLPropertyInCache(
             _pPropertyCache,
             TEXT("IsAdmin"),
             dwBool,
             fExplicit
             );

    //
    // BUGBUG - replace S_OK with hr (see above).
    //

    RRETURN(S_OK);
}

//----------------------------------------------------------------------------
//
//  Function: CNWCOMPATUser::GetProperty_MaxLogins
//
//  Synopsis:
//
//----------------------------------------------------------------------------
HRESULT
CNWCOMPATUser::GetProperty_MaxLogins(
    NWCONN_HANDLE hConn,
    LC_STRUCTURE LoginCtrlStruct,
    BOOL fExplicit
    )
{
    HRESULT      hr = S_OK;
    LONG         lMaxLogins = 0;

    //
    // Get "wMaxConnections".
    //

    lMaxLogins = (LONG) NWApiReverseWORD(
                            LoginCtrlStruct.wMaxConnections
                            );

    //
    // Unmarshall.
    //

    hr = SetDWORDPropertyInCache(
             _pPropertyCache,
             TEXT("MaxLogins"),
             (DWORD)lMaxLogins,
             fExplicit
             );


    RRETURN(hr);
}

//----------------------------------------------------------------------------
//
//  Function: CNWCOMPATUser::GetProperty_CanPasswordExpire
//
//  Synopsis:
//
//----------------------------------------------------------------------------
HRESULT
CNWCOMPATUser::GetProperty_CanPasswordExpire(
    NWCONN_HANDLE hConn,
    LC_STRUCTURE LoginCtrlStruct,
    BOOL fExplicit
    )
{
    BOOL         dwBool = TRUE;
    HRESULT      hr = S_OK;

    //
    // Password cannot expire if there is no expiration date.
    //

    if ((LoginCtrlStruct.byPasswordExpires[0] == 0) &&
        (LoginCtrlStruct.byPasswordExpires[1] == 0) &&
        (LoginCtrlStruct.byPasswordExpires[2] == 0)) {

        dwBool = FALSE;
    }

    //
    // Unmarshall.
    //
    hr = SetBOOLPropertyInCache(
             _pPropertyCache,
             TEXT("PasswordCanExpire"),
             dwBool,
             fExplicit
             );

    RRETURN(hr);
}

//----------------------------------------------------------------------------
//
//  Function: CNWCOMPATUser::GetProperty_PasswordExpirationDate
//
//  Synopsis:
//
//----------------------------------------------------------------------------
HRESULT
CNWCOMPATUser::GetProperty_PasswordExpirationDate(
    NWCONN_HANDLE hConn,
    LC_STRUCTURE LoginCtrlStruct,
    BOOL fExplicit
    )
{
    HRESULT      hr = S_OK;
    BYTE byDateTime[6];

    memset(byDateTime, 0, 6);
    memcpy(byDateTime, LoginCtrlStruct.byPasswordExpires, 3);

    hr = SetNw312DATEPropertyInCache(
                _pPropertyCache,
                TEXT("PasswordExpirationDate"),
                byDateTime,
                fExplicit
                );

    RRETURN(hr);
}

//----------------------------------------------------------------------------
//
//  Function: CNWCOMPATUser::GetProperty_PasswordMinimumLength
//
//  Synopsis:
//
//----------------------------------------------------------------------------
HRESULT
CNWCOMPATUser::GetProperty_PasswordMinimumLength(
    NWCONN_HANDLE hConn,
    LC_STRUCTURE LoginCtrlStruct,
    BOOL fExplicit
    )
{
    HRESULT      hr = S_OK;
    LONG         lMinimumLength = 0;

    //
    // Get "byMinPasswordLength".
    //

    lMinimumLength = (LONG) LoginCtrlStruct.byMinPasswordLength;

    //
    // Unmarshall.
    //

    hr = SetDWORDPropertyInCache(
             _pPropertyCache,
             TEXT("PasswordMinimumLength"),
             (DWORD)lMinimumLength,
             fExplicit
             );

    RRETURN(hr);
}

//----------------------------------------------------------------------------
//
//  Function: CNWCOMPATUser::GetProperty_PasswordRequired
//
//  Synopsis:
//
//----------------------------------------------------------------------------
HRESULT
CNWCOMPATUser::GetProperty_PasswordRequired(
    NWCONN_HANDLE hConn,
    LC_STRUCTURE LoginCtrlStruct,
    BOOL fExplicit
    )
{
    BOOL         dwBool = TRUE;
    HRESULT      hr = S_OK;

    //
    // Password is not required if "byMinPasswordLength" is 0.
    //

    if (LoginCtrlStruct.byMinPasswordLength == 0) {

        dwBool = FALSE;
    }

    //
    // Unmarshall.
    //

    hr = SetBOOLPropertyInCache(
             _pPropertyCache,
             TEXT("PasswordRequired"),
             dwBool,
             fExplicit
             );

    RRETURN(hr);
}

//----------------------------------------------------------------------------
//
//  Function: CNWCOMPATUser::GetProperty_RequireUniquePassword
//
//  Synopsis:
//
//----------------------------------------------------------------------------
HRESULT
CNWCOMPATUser::GetProperty_RequireUniquePassword(
    NWCONN_HANDLE hConn,
    LC_STRUCTURE LoginCtrlStruct,
    BOOL fExplicit
    )
{
    BOOL         dwBool = TRUE;
    HRESULT      hr = S_OK;

    //
    // If byRestrictions = 0, "RequireUniquePassword" = FALSE.
    //

    if (LoginCtrlStruct.byRestrictions == 0) {

        dwBool = FALSE;
    }

    //
    // Unmarshall.
    //
    hr = SetBOOLPropertyInCache(
             _pPropertyCache,
             TEXT("RequireUniquePassword"),
             dwBool,
             fExplicit
             );

    RRETURN(hr);
}

HRESULT
CNWCOMPATUser::GetProperty_LoginHours(
    NWCONN_HANDLE hConn,
    LC_STRUCTURE LoginCtrlStruct,
    BOOL fExplicit
    )
{
    BOOL         dwBool = TRUE;
    HRESULT      hr = S_OK;

    //
    // Unmarshall.
    //
    hr = SetOctetPropertyInCache(
             _pPropertyCache,
             TEXT("LoginHours"),
             (BYTE*)LoginCtrlStruct.byLoginTimes,
             42,
             fExplicit
             );

    RRETURN(hr);
}


//----------------------------------------------------------------------------
//
//  Function: CNWCOMPATUser::GetProperty_BadLoginAddress
//
//  Synopsis:
//
//----------------------------------------------------------------------------
HRESULT
CNWCOMPATUser::GetProperty_BadLoginAddress(
    NWCONN_HANDLE hConn,
    LC_STRUCTURE LoginCtrlStruct,
    BOOL fExplicit
    )
{
    HRESULT hr = S_OK;
    LPWSTR  lpszTemp = NULL;

    //
    // Put address together in the format described in spec.
    //

    lpszTemp = (LPWSTR) AllocADsMem((NET_ADDRESS_NUM_CHAR+1)*sizeof(WCHAR));
    if (!lpszTemp) {
        RRETURN(E_OUTOFMEMORY);
    }

    wsprintf(
        lpszTemp,
        L"%s:%02X%02X%02X%02X.%02X%02X%02X%02X%02X%02X.%02X%02X",
        bstrAddressTypeString,
        LoginCtrlStruct.byBadLoginAddr[10],
        LoginCtrlStruct.byBadLoginAddr[11],
        LoginCtrlStruct.byBadLoginAddr[0],
        LoginCtrlStruct.byBadLoginAddr[1],
        LoginCtrlStruct.byBadLoginAddr[2],
        LoginCtrlStruct.byBadLoginAddr[3],
        LoginCtrlStruct.byBadLoginAddr[4],
        LoginCtrlStruct.byBadLoginAddr[5],
        LoginCtrlStruct.byBadLoginAddr[6],
        LoginCtrlStruct.byBadLoginAddr[7],
        LoginCtrlStruct.byBadLoginAddr[8],
        LoginCtrlStruct.byBadLoginAddr[9]
        );

    //
    // Unmarshall.
    //

    hr = SetLPTSTRPropertyInCache(
                _pPropertyCache,
                TEXT("BadLoginAddress"),
                (LPWSTR)lpszTemp,
                fExplicit
                );
    BAIL_ON_FAILURE(hr);

error:

    if (lpszTemp) {

        FreeADsMem(lpszTemp);
    }

    RRETURN(hr);
}

//----------------------------------------------------------------------------
//
//  Function: CNWCOMPATUser::GetProperty_LastLogin
//
//  Synopsis:
//
//----------------------------------------------------------------------------
HRESULT
CNWCOMPATUser::GetProperty_LastLogin(
    NWCONN_HANDLE hConn,
    LC_STRUCTURE LoginCtrlStruct,
    BOOL fExplicit
    )
{

    HRESULT hr = S_OK;
    BYTE byNoDateTime[6];
    
    memset(byNoDateTime, 0, 6);
    
    //
    // LastLogin==000000 indicates no or unknown LastLogin
    //
    if (memcmp(LoginCtrlStruct.byLastLogin, byNoDateTime, 6) != 0)  {

        hr =  SetNw312DATEPropertyInCache(
                _pPropertyCache,
                TEXT("LastLogin"),
                LoginCtrlStruct.byLastLogin,
                fExplicit
                );
    }

    RRETURN(hr);
}



HRESULT
ConvertNW312DateToVariant(
    BYTE byDateTime[],
    PDATE pDate
    )
{
    HRESULT hr = S_OK;
    WORD wYear;

    //
    // Subtract 80 from wYear for NWApiMakeVariantTime.
    //

    wYear = (WORD)byDateTime[0];

    if (wYear != 0) {
        wYear -= 80;
    }

    //
    // Convert into Variant Time.
    //

    hr = NWApiMakeVariantTime(
                pDate,
                (WORD)byDateTime[2],
                (WORD)byDateTime[1],
                wYear,
                0,0,0
                );
    RRETURN(hr);
}


HRESULT
ConvertVariantToNW312Date(
    DATE daDate,
    BYTE byDateTime[]
    )
{
    WORD wDay;
    WORD wYear;
    WORD wMonth;
    HRESULT hr = S_OK;

    hr = NWApiBreakVariantTime(
                daDate,
                &wDay,
                &wMonth,
                &wYear
                );
    BAIL_ON_FAILURE(hr);

    byDateTime[0] = (BYTE)wYear;
    byDateTime[1] = (BYTE)wMonth;
    byDateTime[2] = (BYTE)wDay;

    byDateTime[3] = 0;
    byDateTime[4] = 0;
    byDateTime[5] = 0;

error:

    RRETURN(hr);
}
