/*++

Copyright (c) 1994-95  Microsoft Corporation

Module Name:

    domobj.cpp

Abstract:

    Domain object implementation.

Author:

    Don Ryan (donryan) 04-Jan-1995

Environment:

    User Mode - Win32

Revision History:

--*/

#include "stdafx.h"
#include "llsmgr.h"
#include "lmcons.h"
#include "lmapibuf.h"
#include "lmserver.h"
#include "lmaccess.h"

#ifdef _DEBUG
#undef THIS_FILE
static char BASED_CODE THIS_FILE[] = __FILE__;
#endif

IMPLEMENT_DYNCREATE(CDomain, CCmdTarget)

BEGIN_MESSAGE_MAP(CDomain, CCmdTarget)
    //{{AFX_MSG_MAP(CDomain)
        // NOTE - the ClassWizard will add and remove mapping macros here.
    //}}AFX_MSG_MAP
END_MESSAGE_MAP()

BEGIN_DISPATCH_MAP(CDomain, CCmdTarget)
    //{{AFX_DISPATCH_MAP(CDomain)
    DISP_PROPERTY_EX(CDomain, "Name", GetName, SetNotSupported, VT_BSTR)
    DISP_PROPERTY_EX(CDomain, "Parent", GetParent, SetNotSupported, VT_DISPATCH)
    DISP_PROPERTY_EX(CDomain, "Primary", GetPrimary, SetNotSupported, VT_BSTR)
    DISP_PROPERTY_EX(CDomain, "Application", GetApplication, SetNotSupported, VT_DISPATCH)
    DISP_PROPERTY_EX(CDomain, "Controller", GetController, SetNotSupported, VT_BSTR)
    DISP_PROPERTY_EX(CDomain, "IsLogging", IsLogging, SetNotSupported, VT_BOOL)
    DISP_PROPERTY_PARAM(CDomain, "Servers", GetServers, SetNotSupported, VT_DISPATCH, VTS_VARIANT)
    DISP_PROPERTY_PARAM(CDomain, "Users", GetUsers, SetNotSupported, VT_DISPATCH, VTS_VARIANT)
    DISP_PROPERTY_PARAM(CDomain, "TrustedDomains", GetTrustedDomains, SetNotSupported, VT_DISPATCH, VTS_VARIANT)
    DISP_DEFVALUE(CDomain, "Name")
    //}}AFX_DISPATCH_MAP
END_DISPATCH_MAP()


CDomain::CDomain(CCmdTarget* pParent, LPCTSTR pName)

/*++

Routine Description:

    Constructor for domain object.

Arguments:

    pParent - creator of object.
    pName - name of domain.

Return Values:

    None.

--*/

{
    EnableAutomation();

#ifdef ENABLE_PARENT_CHECK
    ASSERT(pParent && pParent->IsKindOf(RUNTIME_CLASS(CApplication)));
#endif // ENABLE_PARENT_CHECK

    m_pParent = pParent;

    ASSERT(pName && *pName);

    m_strName = pName;

    m_strPrimary.Empty();
    m_strController.Empty();

    m_pServers = NULL;
    m_pDomains = NULL;
    m_pUsers   = NULL;

    m_serverArray.RemoveAll();
    m_domainArray.RemoveAll();
    m_userArray.RemoveAll();

    m_bServersRefreshed = FALSE;
    m_bDomainsRefreshed = FALSE;
    m_bUsersRefreshed   = FALSE;
}


CDomain::~CDomain()

/*++

Routine Description:

    Destructor for domain object.

Arguments:

    None.

Return Values:

    None.

--*/

{
    if (m_pUsers)
        m_pUsers->InternalRelease();

    if (m_pServers)
        m_pServers->InternalRelease();

    if (m_pDomains)
        m_pDomains->InternalRelease();
}

void CDomain::OnFinalRelease()

/*++

Routine Description:

    When the last reference for an automation object is released
    OnFinalRelease is called.  This implementation deletes object.

Arguments:

    None.

Return Values:

    None.

--*/

{
    ResetUsers();
    ResetServers();
    ResetDomains();
    delete this;
}


LPDISPATCH CDomain::GetApplication() 

/*++

Routine Description:

    Returns the application object.

Arguments:

    None.

Return Values:

    VT_DISPATCH.

--*/

{
    return theApp.GetAppIDispatch();
}


BSTR CDomain::GetController() 

/*++

Routine Description:

    Returns license controller for domain.

Arguments:

    None.

Return Values:

    VT_BSTR.

--*/

{
    return NULL;    // CODEWORK... 
}


BSTR CDomain::GetName() 

/*++

Routine Description:

    Returns the name of the domain.

Arguments:

    None.

Return Values:

    None.

--*/

{
    return m_strName.AllocSysString();
}


LPDISPATCH CDomain::GetParent() 

/*++

Routine Description:

    Returns the parent of the object.

Arguments:

    None.

Return Values:

    VT_DISPATCH.

--*/

{
    return m_pParent ? m_pParent->GetIDispatch(TRUE) : NULL;
}


BSTR CDomain::GetPrimary() 

/*++

Routine Description:

    Returns the name of the primary controller.

Arguments:

    None.

Return Values:

    VT_BSTR.

--*/

{
    if (m_strPrimary.IsEmpty())
    {
        NET_API_STATUS NetStatus;
        LPTSTR pPrimary = NULL;

        NetStatus = ::NetGetDCName(
                        NULL, 
                        MKSTR(m_strName),
                        (LPBYTE*)&pPrimary
                        );

        if (NetStatus == ERROR_SUCCESS)
        {
            m_strPrimary = pPrimary;
            ::NetApiBufferFree((LPBYTE)pPrimary);
        }

        LlsSetLastStatus(NetStatus);  // called api
    }

    return m_strPrimary.AllocSysString();
}


LPDISPATCH CDomain::GetServers(const VARIANT FAR& index) 

/*++

Routine Description:

    Returns a collection object containing all of the 
    servers in the domain or returns an individual server
    described by an index into the collection.

Arguments:

    index - optional argument that may be a string (VT_BSTR) 
    indicating the server name or a number (VT_I4) indicating 
    the position within collection.

Return Values:

    VT_DISPATCH or VT_EMPTY.

--*/

{
    LPDISPATCH lpdispatch = NULL;

    if (!m_pServers)
    {
        m_pServers = new CServers(this, &m_serverArray);
    }

    if (m_pServers)
    {
        if (V_ISVOID((VARIANT FAR*)&index))                         
        {                                                           
            if (RefreshServers())
            {
                lpdispatch = m_pServers->GetIDispatch(TRUE);
            }
        }                                                           
        else                                                        
        {                                                           
            if (m_bServersRefreshed)
            {
                lpdispatch = m_pServers->GetItem(index);    
            }
            else if (RefreshServers())
            {
                lpdispatch = m_pServers->GetItem(index);    
            }        
        }                                                           
    }
    else
    {
        LlsSetLastStatus(ERROR_OUTOFMEMORY);
    }

    return lpdispatch;
}


LPDISPATCH CDomain::GetTrustedDomains(const VARIANT FAR& index) 

/*++

Routine Description:

    Returns a collection object containing all of the 
    domains trusted bythe domain or returns an individual 
    trusted domain described by an index into the collection.

Arguments:

    index - optional argument that may be a string (VT_BSTR) 
    indicating the domain name or a number (VT_I4) indicating 
    the position within collection.

Return Values:

    VT_DISPATCH or VT_EMPTY.

--*/

{
    LPDISPATCH lpdispatch = NULL;

    if (!m_pDomains)
    {
        m_pDomains = new CDomains(this, &m_domainArray);
    }

    if (m_pDomains)
    {
        if (V_ISVOID((VARIANT FAR*)&index))                     
        {                                                       
            if (RefreshDomains())
            {
                lpdispatch = m_pDomains->GetIDispatch(TRUE);
            }
        }                                                       
        else                                                    
        {                                                       
            if (m_bDomainsRefreshed)
            {
                lpdispatch = m_pDomains->GetItem(index);    
            }
            else if (RefreshDomains())
            {
                lpdispatch = m_pDomains->GetItem(index);    
            }        
        }                                                       
    }
    else
    {
        LlsSetLastStatus(ERROR_OUTOFMEMORY);
    }

    return lpdispatch;
}


LPDISPATCH CDomain::GetUsers(const VARIANT FAR& index) 

/*++

Routine Description:

    Returns a collection object containing all of the 
    users in the domain or returns an individual user
    described by an index into the collection.

Arguments:

    index - optional argument that may be a string (VT_BSTR) 
    indicating the user name or a number (VT_I4) indicating 
    the position within collection.

Return Values:

    VT_DISPATCH or VT_EMPTY.

--*/

{
    LPDISPATCH lpdispatch = NULL;

    if (!m_pUsers)
    {
        m_pUsers = new CUsers(this, &m_userArray);
    }

    if (m_pUsers)
    {
        if (V_ISVOID((VARIANT FAR*)&index))                     
        {                                                       
            if (RefreshUsers())
            {
                lpdispatch = m_pUsers->GetIDispatch(TRUE);
            }
        }                                                       
        else                                                    
        {                                                       
            if (m_bUsersRefreshed)
            {
                lpdispatch = m_pUsers->GetItem(index);    
            }
            else if (RefreshUsers())
            {
                lpdispatch = m_pUsers->GetItem(index);    
            }        
        }                                                       
    }
    else
    {
        LlsSetLastStatus(ERROR_OUTOFMEMORY);
    }

    return lpdispatch;
}


BOOL CDomain::IsLogging() 

/*++

Routine Description:

    Returns true if primary replicating license information.

Arguments:

    None.

Return Values:

    VT_BOOL.

--*/

{
    return TRUE;    // CODEWORK... LlsEnterpriseServerFind??
}


BOOL CDomain::RefreshDomains()

/*++

Routine Description:

    Refreshs trusted domain array.

Arguments:

    None.

Return Values:

    VT_BOOL.

--*/

{
    ResetDomains();

    NTSTATUS NtStatus;
    LSA_HANDLE LsaPolicyHandle  = NULL;

    ACCESS_MASK           LsaDesiredAccess;
    LSA_OBJECT_ATTRIBUTES LsaObjectAttributes;

    BSTR bstrPrimary = GetPrimary();

    LSA_UNICODE_STRING PrimaryNameU;
    RtlInitUnicodeString(&PrimaryNameU, bstrPrimary);  

    LsaDesiredAccess = LLS_DESIRED_ACCESS;

    InitializeObjectAttributes( 
        &LsaObjectAttributes,
        NULL,                       // Name        
        0,                          // Attributes
        NULL,                       // Root
        NULL                        // Security Descriptor  
        );           

    NtStatus = LsaOpenPolicy(
                &PrimaryNameU,
                &LsaObjectAttributes,
                LsaDesiredAccess,
                &LsaPolicyHandle 
                );

    if (NT_SUCCESS(NtStatus))
    {
        LSA_ENUMERATION_HANDLE LsaEnumContext = 0L;

        int iDomain = 0;

        do
        {
            DWORD  EntriesRead;                                               
            LPBYTE ReturnBuffer = NULL;

            NtStatus = LsaEnumerateTrustedDomains(
                        LsaPolicyHandle,
                        &LsaEnumContext,
                        (LPVOID*)&ReturnBuffer,
                        LLS_PREFERRED_LENGTH,
                        &EntriesRead
                        );

            if (NtStatus == STATUS_SUCCESS ||                                
                NtStatus == STATUS_MORE_ENTRIES)                             
            {
                CDomain*               pDomain;
                PLSA_TRUST_INFORMATION pLsaTrustInfo;

                pLsaTrustInfo = (PLSA_TRUST_INFORMATION)ReturnBuffer;

                ASSERT(iDomain == m_domainArray.GetSize());
                m_domainArray.SetSize(m_domainArray.GetSize() + EntriesRead);

                while (EntriesRead--)
                {
                    pDomain = new CDomain(this, pLsaTrustInfo->Name.Buffer);

                    m_domainArray.SetAt(iDomain++, pDomain); // validate later   
                    pLsaTrustInfo++;                   
                }                                        

                LsaFreeMemory(ReturnBuffer);
            }

        } while (NtStatus == STATUS_MORE_ENTRIES);

        LsaClose(LsaPolicyHandle);
    }

    if (NtStatus == STATUS_NO_MORE_ENTRIES)
        NtStatus = STATUS_SUCCESS;

    LlsSetLastStatus(NtStatus);   // called api

    SysFreeString(bstrPrimary);     // allocated above...

    if (NT_SUCCESS(NtStatus))
    {
        m_bDomainsRefreshed = TRUE;
    }
    else
    {
        ResetDomains();
    }

    return m_bDomainsRefreshed;
}


BOOL CDomain::RefreshServers()

/*++

Routine Description:

    Refreshs server object array.

Arguments:

    None.

Return Values:

    VT_BOOL.

--*/

{
    ResetServers();

    NET_API_STATUS NetStatus;
    DWORD ResumeHandle = 0L;

    int iServer = 0;

    do 
    {
        DWORD  EntriesRead;
        DWORD  TotalEntries;
        LPBYTE ReturnBuffer = NULL;

        NetStatus = ::NetServerEnum(
                        NULL,                   // servername
                        100,                    // level
                        &ReturnBuffer,
                        LLS_PREFERRED_LENGTH,   
                        &EntriesRead,
                        &TotalEntries,
                        SV_TYPE_NT,    
                        MKSTR(m_strName),                   
                        &ResumeHandle
                        );

        if (NetStatus == ERROR_SUCCESS || 
            NetStatus == ERROR_MORE_DATA)
        {
            CServer*         pServer; 
            PSERVER_INFO_100 pServerInfo100;

            pServerInfo100 = (PSERVER_INFO_100)ReturnBuffer;

            ASSERT(iServer == m_serverArray.GetSize());
            m_serverArray.SetSize(m_serverArray.GetSize() + EntriesRead);

            while (EntriesRead--)
            {
                pServer = new CServer(this, pServerInfo100->sv100_name);

                m_serverArray.SetAt(iServer++, pServer); // validate later
                pServerInfo100++;
            }

            NetApiBufferFree(ReturnBuffer);
        }

    } while (NetStatus == ERROR_MORE_DATA);    

    LlsSetLastStatus(NetStatus);  // called api

    if (NetStatus == ERROR_SUCCESS)
    {
        m_bServersRefreshed = TRUE;
    }
    else
    {
        ResetServers();
    }

    return m_bServersRefreshed;
}


BOOL CDomain::RefreshUsers()

/*++

Routine Description:

    Refreshs user object array.

Arguments:

    None.

Return Values:

    VT_BOOL.

--*/

{
    ResetUsers();

    NET_API_STATUS NetStatus;
    DWORD ResumeHandle = 0L;

    int iUser = 0;

    do 
    {
        DWORD  EntriesRead;
        DWORD  TotalEntries;
        LPBYTE ReturnBuffer = NULL;

        NetStatus = NetUserEnum(
                        GetPrimary(),           // servername
                        0,                      // level
                        FILTER_NORMAL_ACCOUNT,
                        &ReturnBuffer,
                        LLS_PREFERRED_LENGTH,   
                        &EntriesRead,
                        &TotalEntries,
                        &ResumeHandle
                        );

        if (NetStatus == ERROR_SUCCESS || 
            NetStatus == ERROR_MORE_DATA)
        {
            CUser*       pUser;
            PUSER_INFO_0 pUserInfo0;

            pUserInfo0 = (PUSER_INFO_0)ReturnBuffer;

            ASSERT(iUser == m_userArray.GetSize());
            m_userArray.SetSize(m_userArray.GetSize() + EntriesRead);

            while (EntriesRead--)
            {
                pUser = new CUser(this, pUserInfo0->usri0_name);

                m_userArray.SetAt(iUser++, pUser); // validate later
                pUserInfo0++;
            }

            NetApiBufferFree(ReturnBuffer);
        }
    } while (NetStatus == ERROR_MORE_DATA);    

    LlsSetLastStatus(NetStatus);  // called api

    if (NetStatus == ERROR_SUCCESS)
    {
        m_bUsersRefreshed = TRUE;
    }
    else
    {
        ResetUsers();
    }
    
    return m_bUsersRefreshed;
}


void CDomain::ResetDomains()

/*++

Routine Description:

    Resets domain object array.

Arguments:

    None.

Return Values:

    None.

--*/

{
    CDomain* pDomain;
    int      iDomain = m_domainArray.GetSize();

    while (iDomain--)
    {
        if (pDomain = (CDomain*)m_domainArray[iDomain])
        {
            ASSERT(pDomain->IsKindOf(RUNTIME_CLASS(CDomain)));
            pDomain->InternalRelease();
        }
    }

    m_domainArray.RemoveAll();
    m_bDomainsRefreshed = FALSE;
}


void CDomain::ResetServers()

/*++

Routine Description:

    Resets server object array.

Arguments:

    None.

Return Values:

    None.

--*/

{
    CServer* pServer;
    int      iServer = m_serverArray.GetSize();

    while (iServer--)
    {
        if (pServer = (CServer*)m_serverArray[iServer])
        {
            ASSERT(pServer->IsKindOf(RUNTIME_CLASS(CServer)));
            pServer->InternalRelease();
        }
    }

    m_serverArray.RemoveAll();
    m_bServersRefreshed = FALSE;
}


void CDomain::ResetUsers()

/*++

Routine Description:

    Resets user object array.

Arguments:

    None.

Return Values:

    None.

--*/

{
    CUser* pUser;
    int    iUser = m_userArray.GetSize();

    while (iUser--)
    {
        if (pUser = (CUser*)m_userArray[iUser])
        {
            ASSERT(pUser->IsKindOf(RUNTIME_CLASS(CUser)));
            pUser->InternalRelease();
        }
    }

    m_userArray.RemoveAll();
    m_bUsersRefreshed = FALSE;
}