/*++

Copyright (c) 1994-95  Microsoft Corporation

Module Name:

    appobj.cpp

Abstract:

    OLE-createable application object implementation.

Author:

    Don Ryan (donryan) 27-Dec-1994

Environment:

    User Mode - Win32

Revision History:

    Jeff Parham (jeffparh) 16-Jan-1996
        Added Get/SetLastTargetServer() to help isolate server connection
        problems.  (Bug #2993.)

--*/

#include "stdafx.h"
#include "llsmgr.h"
#include "lmerr.h"
#include "lmcons.h"
#include "lmwksta.h"
#include "lmapibuf.h"
#include "lmserver.h"

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

IMPLEMENT_DYNCREATE(CApplication, CCmdTarget)
// IMPLEMENT_OLECREATE(CApplication, "Llsmgr.Application.1", 0x2c5dffb3, 0x472f, 0x11ce, 0xa0, 0x30, 0x0, 0xaa, 0x0, 0x33, 0x9a, 0x98)

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

BEGIN_DISPATCH_MAP(CApplication, CCmdTarget)
    //{{AFX_DISPATCH_MAP(CApplication)
    DISP_PROPERTY_EX(CApplication, "Application", GetApplication, SetNotSupported, VT_DISPATCH)
    DISP_PROPERTY_EX(CApplication, "FullName", GetFullName, SetNotSupported, VT_BSTR)
    DISP_PROPERTY_EX(CApplication, "Name", GetName, SetNotSupported, VT_BSTR)
    DISP_PROPERTY_EX(CApplication, "Parent", GetParent, SetNotSupported, VT_DISPATCH)
    DISP_PROPERTY_EX(CApplication, "Visible", GetVisible, SetNotSupported, VT_BOOL)
    DISP_PROPERTY_EX(CApplication, "ActiveController", GetActiveController, SetNotSupported, VT_DISPATCH)
    DISP_PROPERTY_EX(CApplication, "ActiveDomain", GetActiveDomain, SetNotSupported, VT_DISPATCH)
    DISP_PROPERTY_EX(CApplication, "LocalDomain", GetLocalDomain, SetNotSupported, VT_DISPATCH)
    DISP_PROPERTY_EX(CApplication, "IsFocusDomain", IsFocusDomain, SetNotSupported, VT_BOOL)
    DISP_PROPERTY_EX(CApplication, "LastErrorString", GetLastErrorString, SetNotSupported, VT_BSTR)
    DISP_FUNCTION(CApplication, "Quit", Quit, VT_EMPTY, VTS_NONE)
    DISP_FUNCTION(CApplication, "SelectDomain", SelectDomain, VT_BOOL, VTS_VARIANT)
    DISP_FUNCTION(CApplication, "SelectEnterprise", SelectEnterprise, VT_BOOL, VTS_NONE)
    DISP_PROPERTY_PARAM(CApplication, "Domains", GetDomains, SetNotSupported, VT_DISPATCH, VTS_VARIANT)
    DISP_DEFVALUE(CApplication, "Name")
    //}}AFX_DISPATCH_MAP
END_DISPATCH_MAP()


CApplication::CApplication()

/*++

Routine Description:

    Constructor for OLE-createable application object.

    To keep the application running as long as an OLE automation
    object is active, we must call AfxOleLockApp.

Arguments:

    None.

Return Values:

    None.

--*/

{
    EnableAutomation();

    ASSERT(theApp.m_pApplication == NULL);

    if (theApp.m_pApplication == NULL)
        theApp.m_pApplication = this;

    if (theApp.m_bIsAutomated)
        AfxOleLockApp();

    m_pDomains          = NULL;
    m_pLocalDomain      = NULL;
    m_pActiveDomain     = NULL;
    m_bIsFocusDomain    = FALSE;
    m_bDomainsRefreshed = FALSE;

    m_strLastTargetServer = TEXT("");

    m_domainArray.RemoveAll();

    m_pActiveController = new CController;
    m_idStatus = m_pActiveController ? STATUS_SUCCESS : STATUS_NO_MEMORY;
}


CApplication::~CApplication()

/*++

Routine Description:

    Destructor for OLE-createable application object.

Arguments:

    None.

Return Values:

    None.

--*/

{
    ASSERT(theApp.m_pApplication == this);

    if (theApp.m_pApplication == this)
        theApp.m_pApplication = NULL;

    if (theApp.m_bIsAutomated)
        AfxOleUnlockApp();

    if (m_pDomains)
        m_pDomains->InternalRelease();

    if (m_pLocalDomain)
        m_pLocalDomain->InternalRelease();

    if (m_pActiveDomain)
        m_pActiveDomain->InternalRelease();

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


void CApplication::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.

--*/

{
    ResetDomains();
    delete this;
}


LPDISPATCH CApplication::GetActiveController()

/*++

Routine Description:

    Returns the active license controller object.

Arguments:

    None.

Return Values:

    VT_DISPATCH or VT_EMPTY.

--*/

{
    ASSERT_VALID(m_pActiveController);
    return m_pActiveController->GetIDispatch(TRUE);
}


LPDISPATCH CApplication::GetActiveDomain()

/*++

Routine Description:

    Returns the active domain object.

Arguments:

    None.

Return Values:

    VT_DISPATCH or VT_EMPTY.

--*/

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


LPDISPATCH CApplication::GetApplication()

/*++

Routine Description:

    Returns the application object.

Arguments:

    None.

Return Values:

    VT_DISPATCH.

--*/

{
    return GetIDispatch(TRUE);
}


LPDISPATCH CApplication::GetDomains(const VARIANT FAR& index)

/*++

Routine Description:

    Returns a collection object containing all of the domains
    visible to the local machine or returns an individual domain
    described by an index into the collection.

Arguments:

    index - optional argument that may be a string (VT_BSTR)
    indicating a 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
    {
        SetLastStatus(STATUS_NO_MEMORY);
    }

    return lpdispatch;
}


BSTR CApplication::GetFullName()

/*++

Routine Description:

    Returns the file specification for the application,
    including path.

Arguments:

    None.

Return Values:

    VT_BSTR.

--*/

{
    TCHAR szModuleFileName[260];

    GetModuleFileName(AfxGetApp()->m_hInstance, szModuleFileName, 260);
    return SysAllocStringLen(szModuleFileName, lstrlen(szModuleFileName));
}


BSTR CApplication::GetLastErrorString()

/*++

Routine Description:

    Retrieves string for last error.

    (Routine stolen from winsadmn...).

Arguments:

    None.

Return Values:

    VT_BSTR.

--*/

{
    CString strLastError;
    DWORD nId = m_idStatus;

    if (((long)nId == RPC_S_CALL_FAILED) ||
        ((long)nId == RPC_NT_SS_CONTEXT_MISMATCH))
    {
        strLastError.LoadString(IDP_ERROR_DROPPED_LINK);
    }
    else if (((long)nId == RPC_S_SERVER_UNAVAILABLE) ||
             ((long)nId == RPC_NT_SERVER_UNAVAILABLE))
    {
        strLastError.LoadString(IDP_ERROR_NO_RPC_SERVER);
    }
    else if ((long)nId == STATUS_MEMBER_IN_GROUP)
    {
        strLastError.LoadString(IDP_ERROR_MEMBER_IN_GROUP);
    }
    else
    {
        HINSTANCE hinstDll = NULL;

        if ((nId >= NERR_BASE) && (nId <= MAX_NERR))
        {
            hinstDll = ::LoadLibrary(_T("netmsg.dll"));
        }
        else if (nId >= 0x4000000)
        {
            hinstDll = ::LoadLibrary(_T("ntdll.dll"));
        }

        TCHAR szLastError[1024];
        DWORD cchLastError = sizeof(szLastError) / sizeof(TCHAR);

        DWORD dwFlags = FORMAT_MESSAGE_IGNORE_INSERTS|
                        FORMAT_MESSAGE_MAX_WIDTH_MASK|
                        (hinstDll ? FORMAT_MESSAGE_FROM_HMODULE
                                  : FORMAT_MESSAGE_FROM_SYSTEM);

        cchLastError = ::FormatMessage(
                          dwFlags,
                          hinstDll,
                          nId,
                          0,
                          szLastError,
                          cchLastError,
                          NULL
                          );

        if (hinstDll)
        {
            ::FreeLibrary(hinstDll);
        }

        if (cchLastError)
        {
            strLastError = szLastError;
        }
        else
        {
            strLastError.LoadString(IDP_ERROR_UNSUCCESSFUL);
        }
    }

    return strLastError.AllocSysString();
}


LPDISPATCH CApplication::GetLocalDomain()

/*++

Routine Description:

    Returns the local domain object.

Arguments:

    None.

Return Values:

    VT_DISPATCH or VT_EMPTY.

--*/

{
    if (!m_pLocalDomain)
    {
        NET_API_STATUS NetStatus;
        PWKSTA_INFO_100 pWkstaInfo100 = NULL;

        NetStatus = NetWkstaGetInfo(
                        NULL,
                        100,
                        (LPBYTE*)&pWkstaInfo100
                        );

        if (NetStatus == ERROR_SUCCESS)
        {
            m_pLocalDomain = new CDomain(this, pWkstaInfo100->wki100_langroup);

            if (!m_pLocalDomain)
            {
                NetStatus = ERROR_NOT_ENOUGH_MEMORY;
            }

            NetApiBufferFree(pWkstaInfo100);
        }

        SetLastStatus(NetStatus);   // called api
    }

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


BSTR CApplication::GetName()

/*++

Routine Description:

    Returns the name of the application.

Arguments:

    None.

Return Values:

    VT_BSTR.

--*/

{
    CString AppName = AfxGetAppName();
    return AppName.AllocSysString();
}


LPDISPATCH CApplication::GetParent()

/*++

Routine Description:

    Returns the parent of the object.

Arguments:

    None.

Return Values:

    VT_DISPATCH.

--*/

{
    return GetApplication();
}


BOOL CApplication::GetVisible()

/*++

Routine Description:

    Returns whether or not the application is visible to the user.

Arguments:

    None.

Return Values:

    VT_BOOL.

--*/

{
    return FALSE;
}


BOOL CApplication::IsFocusDomain()

/*++

Routine Description:

    Returns true if application focused on domain.

Arguments:

    None.

Return Values:

    VT_BOOL.

--*/

{
    return m_bIsFocusDomain;
}


BOOL CApplication::RefreshDomains()

/*++

Routine Description:

    Refreshs domain object list.

Arguments:

    None.

Return Values:

    None.

--*/

{
    ResetDomains();

    NET_API_STATUS NetStatus;
    DWORD ResumeHandle = 0L;

    int iDomain = 0;

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

        NetStatus = NetServerEnum(
                        NULL,                   // servername
                        100,                    // level
                        &ReturnBuffer,
                        LLS_PREFERRED_LENGTH,
                        &EntriesRead,
                        &TotalEntries,
                        SV_TYPE_DOMAIN_ENUM,
                        NULL,                   // domain
                        &ResumeHandle
                        );

        if (NetStatus == ERROR_SUCCESS ||
            NetStatus == ERROR_MORE_DATA)
        {
            CDomain*         pDomain;
            PSERVER_INFO_100 pServerInfo100;

            pServerInfo100 = (PSERVER_INFO_100)ReturnBuffer;

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

            while (EntriesRead--)
            {
                pDomain = new CDomain(this, pServerInfo100->sv100_name);

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

            NetApiBufferFree(ReturnBuffer);
        }

    } while (NetStatus == ERROR_MORE_DATA);

    SetLastStatus(NetStatus);   // called api

    if (NetStatus == ERROR_SUCCESS)
    {
        m_bDomainsRefreshed = TRUE;
    }
    else
    {
        ResetDomains();
    }

    return m_bDomainsRefreshed;
}


void CApplication::ResetDomains()

/*++

Routine Description:

    Resets domain object list.

Arguments:

    None.

Return Values:

    None.

--*/

{
    CDomain* pDomain;
    INT_PTR  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;
}


BOOL CApplication::SelectDomain(const VARIANT FAR& domain)

/*++

Routine Description:

    Connects to license controller of specified domain.

Arguments:

    domain - name of domain.

Return Values:

    VT_BOOL.

--*/

{
    BOOL bIsSelected = FALSE;

    ASSERT_VALID(m_pActiveController);

    if (bIsSelected = m_pActiveController->Connect(domain))
    {
        LPTSTR pszActiveDomain = MKSTR(m_pActiveController->m_strActiveDomainName);

        if (m_pActiveDomain)
        {
            m_pActiveDomain->InternalRelease();
            m_pActiveDomain = NULL;
        }

        if (pszActiveDomain && *pszActiveDomain)
        {
            m_pActiveDomain = new CDomain(this, pszActiveDomain);

            if (m_pActiveDomain)
            {
                m_bIsFocusDomain = TRUE;
            }
            else
            {
                m_bIsFocusDomain = FALSE; // invalidate m_pActiveDomain
                SetLastStatus(STATUS_NO_MEMORY);
            }
        }
        else
        {
            m_bIsFocusDomain = FALSE; // invalidate m_pActiveDomain
        }
    }

    return bIsSelected;
}


BOOL CApplication::SelectEnterprise()

/*++

Routine Description:

    Connects to license controller of enterprise.

Arguments:

    None.

Return Values:

    VT_BOOL.

--*/

{
    BOOL bIsSelected = FALSE;

    VARIANT va;
    VariantInit(&va);   // connect to default controller

    if (bIsSelected = m_pActiveController->Connect(va))
    {
        if (m_pActiveDomain)
        {
            m_pActiveDomain->InternalRelease();
            m_pActiveDomain = NULL;
        }

        m_bIsFocusDomain = FALSE;
    }

    return bIsSelected;
}


void CApplication::Quit()

/*++

Routine Description:

    Closes all documents and exits the application.

Arguments:

    None.

Return Values:

    None.

--*/

{
    AfxPostQuitMessage(0); // no main window...
}


BSTR CApplication::GetLastTargetServer()

/*++

Routine Description:

    Retrieves string for last server to which we tried to connect.

Arguments:

    None.

Return Values:

    VT_BSTR.

--*/

{
    if ( m_strLastTargetServer.IsEmpty() )
        return NULL;
    else
        return m_strLastTargetServer.AllocSysString();
}


void CApplication::SetLastTargetServer( LPCTSTR pszServerName )

/*++

Routine Description:

    Sets string for last server to which we tried to connect.

Arguments:

    pszServerName - last server name to which we tried to connect.

Return Values:

    None.

--*/

{
    m_strLastTargetServer = pszServerName;
}