/*++

Copyright (c) 1994-95  Microsoft Corporation

Module Name:

    srvobj.cpp

Abstract:

    Server object implementation.

Author:

    Don Ryan (donryan) 04-Jan-1995

Environment:

    User Mode - Win32

Revision History:

    Jeff Parham (jeffparh) 30-Jan-1996
        o  Modified to use LlsProductLicensesGet() to avoid race conditions in
           getting the correct number of concurrent licenses with secure products.
        o  Ported to LlsLocalService API to remove dependencies on configuration
           information being in the registry.

--*/

#include "stdafx.h"
#include "llsmgr.h"
#include <lm.h>
#include <lmwksta.h>

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

IMPLEMENT_DYNCREATE(CServer, CCmdTarget)

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

BEGIN_DISPATCH_MAP(CServer, CCmdTarget)
    //{{AFX_DISPATCH_MAP(CServer)
    DISP_PROPERTY_EX(CServer, "Application", GetApplication, SetNotSupported, VT_DISPATCH)
    DISP_PROPERTY_EX(CServer, "Name", GetName, SetNotSupported, VT_BSTR)
    DISP_PROPERTY_EX(CServer, "Parent", GetParent, SetNotSupported, VT_DISPATCH)
    DISP_PROPERTY_EX(CServer, "Controller", GetController, SetNotSupported, VT_BSTR)
    DISP_PROPERTY_EX(CServer, "IsLogging", IsLogging, SetNotSupported, VT_BOOL)
    DISP_PROPERTY_EX(CServer, "IsReplicatingToDC", IsReplicatingToDC, SetNotSupported, VT_BOOL)
    DISP_PROPERTY_EX(CServer, "IsReplicatingDaily", IsReplicatingDaily, SetNotSupported, VT_BOOL)
    DISP_PROPERTY_EX(CServer, "ReplicationTime", GetReplicationTime, SetNotSupported, VT_I4)
    DISP_PROPERTY_PARAM(CServer, "Services", GetServices, SetNotSupported, VT_DISPATCH, VTS_VARIANT)
    DISP_DEFVALUE(CServer, "Name")
    //}}AFX_DISPATCH_MAP
END_DISPATCH_MAP()


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

/*++

Routine Description:

    Constructor for server object.

Arguments:

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

Return Values:

    None.

--*/

{
    EnableAutomation();

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

    m_pParent = pParent;

    ASSERT(pName && *pName);

    m_strName = pName;
    m_strController.Empty();

    m_pServices = NULL;
    m_serviceArray.RemoveAll();
    m_bServicesRefreshed = FALSE;

    m_hkeyRoot        = (HKEY)0L;
    m_hkeyLicense     = (HKEY)0L;
    m_hkeyReplication = (HKEY)0L;
   
    m_IsWin2000 = uninitialized;

    m_hLls = NULL;
}


CServer::~CServer()

/*++

Routine Description:

    Destructor for server object.

Arguments:

    None.

Return Values:

    None.

--*/

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

#ifdef CONFIG_THROUGH_REGISTRY
    if (m_hkeyReplication)
        RegCloseKey(m_hkeyReplication);

    if (m_hkeyLicense)
        RegCloseKey(m_hkeyLicense);

    if (m_hkeyRoot)
        RegCloseKey(m_hkeyRoot);
#endif

    DisconnectLls();
}


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

--*/

{
    ResetServices();
    delete this;
}


LPDISPATCH CServer::GetApplication()

/*++

Routine Description:

    Returns the application object.

Arguments:

    None.

Return Values:

    VT_DISPATCH.

--*/

{
    return theApp.GetAppIDispatch();
}


BSTR CServer::GetController()

/*++

Routine Description:

    Returns license controller for server.

Arguments:

    None.

Return Values:

    VT_BSTR.

--*/

{
    LONG Status;
    CString strValue = _T("");

    if (InitializeIfNecessary())
    {
#ifdef CONFIG_THROUGH_REGISTRY
        TCHAR szValue[256];

        DWORD dwType = REG_SZ;
        DWORD dwSize = sizeof(szValue);

        Status = RegQueryValueEx(
                    m_hkeyReplication,
                    REG_VALUE_ENTERPRISE_SERVER,
                    0,
                    &dwType,
                    (LPBYTE)szValue,
                    &dwSize
                    );

        LlsSetLastStatus(Status); // called api

        if (Status == ERROR_SUCCESS)
            strValue = szValue;
#else
        PLLS_SERVICE_INFO_0     pConfig = NULL;

        Status = ::LlsServiceInfoGet( m_hLls, 0, (LPBYTE *) &pConfig );

        if ( NT_SUCCESS( Status ) )
        {
            strValue = pConfig->EnterpriseServer;

            ::LlsFreeMemory( pConfig->ReplicateTo );
            ::LlsFreeMemory( pConfig->EnterpriseServer );
            ::LlsFreeMemory( pConfig );
        }
        else if ( IsConnectionDropped( Status ) )
        {
            DisconnectLls();
        }
#endif
    }

    return strValue.AllocSysString();
}


BSTR CServer::GetName()

/*++

Routine Description:

    Returns the name of the server.

Arguments:

    None.

Return Values:

    VT_BSTR.

--*/

{
    return m_strName.AllocSysString();
}


LPDISPATCH CServer::GetParent()

/*++

Routine Description:

    Returns the parent of the object.

Arguments:

    None.

Return Values:

    VT_DISPATCH.

--*/

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


LPDISPATCH CServer::GetServices(const VARIANT FAR& index)

/*++

Routine Description:

    Returns a collection object containing all of the
    services registered in the server's registry or returns
    an individual service described by an index into the
    collection.

Arguments:

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

Return Values:

    VT_DISPATCH or VT_EMPTY.

--*/

{
    LPDISPATCH lpdispatch = NULL;

    if (!m_pServices)
    {
        m_pServices = new CServices(this, &m_serviceArray);
    }

    if (m_pServices)
    {
        if (V_ISVOID((VARIANT FAR*)&index))
        {
            if (RefreshServices())
            {
                lpdispatch = m_pServices->GetIDispatch(TRUE);
            }
        }
        else
        {
            if (m_bServicesRefreshed)
            {
                lpdispatch = m_pServices->GetItem(index);
            }
            else if (RefreshServices())
            {
                lpdispatch = m_pServices->GetItem(index);
            }
        }
    }
    else
    {
        LlsSetLastStatus(STATUS_NO_MEMORY);
    }

    return lpdispatch;
}


BOOL CServer::IsLogging()

/*++

Routine Description:

    Returns true if server replicating license information.

Arguments:

    None.

Return Values:

    VT_BOOL.

--*/

{
    return TRUE;    // CODEWORK...
}


BOOL CServer::RefreshServices()

/*++

Routine Description:

    Refreshs service object list.

Arguments:

    None.

Return Values:

    VT_BOOL.

--*/

{
    ResetServices();

    LONG Status;

    if (InitializeIfNecessary())
    {
#ifdef CONFIG_THROUGH_REGISTRY
        TCHAR szValue[260];
        DWORD cchValue = sizeof(szValue)/sizeof(TCHAR);

        DWORD dwValue;
        DWORD dwValueType;
        DWORD dwValueSize;

        FILETIME ftLastWritten;

        DWORD index = 0L;
        int iService = 0;
        HKEY hkeyService = NULL;

        CString strServiceName;
        CString strServiceDisplayName;

        BOOL bIsPerServer;
        BOOL bIsReadOnly;

        long lPerServerLimit;

        while ((Status = RegEnumKeyEx(
                            m_hkeyLicense,
                            index,
                            szValue,
                            &cchValue,
                            NULL,               // lpdwReserved
                            NULL,               // lpszClass
                            NULL,               // lpcchClass
                            &ftLastWritten
                            )) == ERROR_SUCCESS)
        {
            strServiceName = szValue; // store for ctor...

            Status = RegOpenKeyEx(
                        m_hkeyLicense,
                        MKSTR(strServiceName),
                        0, // dwReserved
                        KEY_ALL_ACCESS,
                        &hkeyService
                        );

            if (Status != ERROR_SUCCESS)
                break; // abort...

            dwValueType = REG_SZ;
            dwValueSize = sizeof(szValue);

            Status = RegQueryValueEx(
                        hkeyService,
                        REG_VALUE_NAME,
                        0, // dwReserved
                        &dwValueType,
                        (LPBYTE)&szValue[0],
                        &dwValueSize
                        );

            if (Status != ERROR_SUCCESS)
                break; // abort...

            strServiceDisplayName = szValue;

            dwValueType = REG_DWORD;
            dwValueSize = sizeof(DWORD);

            Status = RegQueryValueEx(
                        hkeyService,
                        REG_VALUE_MODE,
                        0, // dwReserved
                        &dwValueType,
                        (LPBYTE)&dwValue,
                        &dwValueSize
                        );

            if (Status != ERROR_SUCCESS)
                break; // abort...

            //
            // 0x0 = per seat mode
            // 0x1 = per server mode
            //

            bIsPerServer = (dwValue == 0x1);

            dwValueType = REG_DWORD;
            dwValueSize = sizeof(DWORD);

            Status = RegQueryValueEx(
                        hkeyService,
                        REG_VALUE_FLIP,
                        0, // dwReserved
                        &dwValueType,
                        (LPBYTE)&dwValue,
                        &dwValueSize
                        );

            if (Status != ERROR_SUCCESS)
                break; // abort...

            //
            // 0x0 = can change mode
            // 0x1 = can't change mode
            //

            bIsReadOnly = (dwValue == 0x1);

            BOOL bGetLimitFromRegistry = TRUE;

            if ( ConnectLls() )
            {
                Status = LlsProductLicensesGet( m_hLls, MKSTR(strServiceDisplayName), LLS_LICENSE_MODE_PER_SERVER, &dwValue );

                if ( STATUS_SUCCESS == Status )
                {
                    bGetLimitFromRegistry = FALSE;
                }
            }

            if ( bGetLimitFromRegistry )
            {
                dwValueType = REG_DWORD;
                dwValueSize = sizeof(DWORD);

                Status = RegQueryValueEx(
                            hkeyService,
                            REG_VALUE_LIMIT,
                            0, // dwReserved
                            &dwValueType,
                            (LPBYTE)&dwValue,
                            &dwValueSize
                            );
            }

            if (Status != ERROR_SUCCESS)
                break; // abort...

            lPerServerLimit = (long)dwValue;

            CService* pService = new CService(this,
                                        strServiceName,
                                        strServiceDisplayName,
                                        bIsPerServer,
                                        bIsReadOnly,
                                        lPerServerLimit
                                        );

            m_serviceArray.SetAtGrow(iService++, pService);
            index++;

            cchValue = sizeof(szValue)/sizeof(TCHAR);

            RegCloseKey(hkeyService); // no longer needed...
            hkeyService = NULL;
        }

        if (hkeyService)
            RegCloseKey(hkeyService);

        if (Status == ERROR_NO_MORE_ITEMS)
            Status = ERROR_SUCCESS;

        LlsSetLastStatus(Status);     // called api

        if (Status == ERROR_SUCCESS)
        {
            m_bServicesRefreshed = TRUE;
        }
        else
        {
            ResetServices();
        }
#else
        DWORD   dwResumeHandle = 0;
        int     iService       = 0;

        do
        {
            PLLS_LOCAL_SERVICE_INFO_0   pServiceList = NULL;
            DWORD                       dwEntriesRead  = 0;
            DWORD                       dwTotalEntries = 0;

            Status = ::LlsLocalServiceEnum( m_hLls,
                                            0,
                                            (LPBYTE *) &pServiceList,
                                            LLS_PREFERRED_LENGTH,
                                            &dwEntriesRead,
                                            &dwTotalEntries,
                                            &dwResumeHandle );

            if ( NT_SUCCESS( Status ) )
            {
                DWORD   i;

                for ( i=0; i < dwEntriesRead; i++ )
                {
                    BOOL  bIsPerServer = ( LLS_LICENSE_MODE_PER_SERVER == pServiceList[ i ].Mode );
                    BOOL  bIsReadOnly  = ( 0 == pServiceList[ i ].FlipAllow );
                    DWORD dwConcurrentLimit;

                    // get number of per server license in case where product
                    //     is secure, and
                    //     ( is currently in per seat mode, or
                    //       new secure per server licenses have just been added and registry
                    //          has not been updated yet )
                    if ( STATUS_SUCCESS != LlsProductLicensesGet( m_hLls, pServiceList[ i ].DisplayName, LLS_LICENSE_MODE_PER_SERVER, &dwConcurrentLimit ) )
                    {
                        dwConcurrentLimit = pServiceList[ i ].ConcurrentLimit;
                    }

                    CService* pService = new CService( this,
                                                       pServiceList[ i ].KeyName,
                                                       pServiceList[ i ].DisplayName,
                                                       bIsPerServer,
                                                       bIsReadOnly,
                                                       dwConcurrentLimit );

                    m_serviceArray.SetAtGrow(iService++, pService);

                    ::LlsFreeMemory( pServiceList[ i ].KeyName           );
                    ::LlsFreeMemory( pServiceList[ i ].DisplayName       );
                    ::LlsFreeMemory( pServiceList[ i ].FamilyDisplayName );
                }

                ::LlsFreeMemory( pServiceList );
            }
        } while ( STATUS_MORE_ENTRIES == Status );

        LlsSetLastStatus( Status );     // called api

        if ( NT_SUCCESS( Status ) )
        {
            m_bServicesRefreshed = TRUE;
        }
        else
        {
            ResetServices();

            if ( IsConnectionDropped( Status ) )
            {
                DisconnectLls();
            }
        }
#endif
    }

    return m_bServicesRefreshed;
}


void CServer::ResetServices()

/*++

Routine Description:

    Resets service object list.

Arguments:

    None.

Return Values:

    None.

--*/

{
    CService* pService;
    INT_PTR   iService = m_serviceArray.GetSize();

    while (iService--)
    {
        if (pService = (CService*)m_serviceArray[iService])
        {
            ASSERT(pService->IsKindOf(RUNTIME_CLASS(CService)));
            pService->InternalRelease();
        }
    }

    m_serviceArray.RemoveAll();
    m_bServicesRefreshed = FALSE;
}


BOOL CServer::InitializeIfNecessary()

/*++

Routine Description:

    Initialize registry keys if necessary.

Arguments:

    None.

Return Values:

    VT_BOOL.

--*/

{
#ifdef CONFIG_THROUGH_REGISTRY
    LONG Status = ERROR_SUCCESS;

    if (!m_hkeyRoot)
    {
        Status = RegConnectRegistry(
                      MKSTR(m_strName),
                      HKEY_LOCAL_MACHINE,
                      &m_hkeyRoot
                      );

        LlsSetLastStatus(Status); // called api
    }

    if (!m_hkeyLicense && (Status == ERROR_SUCCESS))
    {
        ASSERT(m_hkeyRoot);

        Status = RegOpenKeyEx(
                    m_hkeyRoot,
                    REG_KEY_LICENSE,
                    0,                      // dwReserved
                    KEY_ALL_ACCESS,
                    &m_hkeyLicense
                    );

        LlsSetLastStatus(Status); // called api
    }

    if (!m_hkeyReplication && (Status == ERROR_SUCCESS))
    {
        ASSERT(m_hkeyRoot);

        Status = RegOpenKeyEx(
                    m_hkeyRoot,
                    REG_KEY_SERVER_PARAMETERS,
                    0,                      // dwReserved
                    KEY_ALL_ACCESS,
                    &m_hkeyReplication
                    );

        LlsSetLastStatus(Status); // called api
    }

    return (Status == ERROR_SUCCESS);
#else
    return ConnectLls();
#endif
}


BOOL CServer::IsReplicatingToDC()

/*++

Routine Description:

    Returns true if server replicating to domain controller.

Arguments:

    None.

Return Values:

    VT_BOOL.

--*/

{
    LONG Status;
    DWORD dwValue = 0;

    if (InitializeIfNecessary())
    {
#ifdef CONFIG_THROUGH_REGISTRY
        DWORD dwType = REG_DWORD;
        DWORD dwSize = sizeof(DWORD);

        Status = RegQueryValueEx(
                    m_hkeyReplication,
                    REG_VALUE_USE_ENTERPRISE,
                    0,
                    &dwType,
                    (LPBYTE)&dwValue,
                    &dwSize
                    );
#else
        PLLS_SERVICE_INFO_0     pConfig = NULL;

        Status = ::LlsServiceInfoGet( m_hLls, 0, (LPBYTE *) &pConfig );

        if ( NT_SUCCESS( Status ) )
        {
            dwValue = pConfig->UseEnterprise;

            ::LlsFreeMemory( pConfig->ReplicateTo );
            ::LlsFreeMemory( pConfig->EnterpriseServer );
            ::LlsFreeMemory( pConfig );
        }

        if ( IsConnectionDropped( Status ) )
        {
            DisconnectLls();
        }
#endif
        LlsSetLastStatus(Status); // called api
    }

    return !((BOOL)dwValue);
}


BOOL CServer::IsReplicatingDaily()

/*++

Routine Description:

    Returns true if server replicating daily at certain time.

Arguments:

    None.

Return Values:

    VT_BOOL.

--*/

{
    LONG Status;
    DWORD dwValue = 0;

    if (InitializeIfNecessary())
    {
#ifdef CONFIG_THROUGH_REGISTRY
        DWORD dwType = REG_DWORD;
        DWORD dwSize = sizeof(DWORD);

        Status = RegQueryValueEx(
                    m_hkeyReplication,
                    REG_VALUE_REPLICATION_TYPE,
                    0,
                    &dwType,
                    (LPBYTE)&dwValue,
                    &dwSize
                    );
#else
        PLLS_SERVICE_INFO_0     pConfig = NULL;

        Status = ::LlsServiceInfoGet( m_hLls, 0, (LPBYTE *) &pConfig );

        if ( NT_SUCCESS( Status ) )
        {
            dwValue = pConfig->ReplicationType;

            ::LlsFreeMemory( pConfig->ReplicateTo );
            ::LlsFreeMemory( pConfig->EnterpriseServer );
            ::LlsFreeMemory( pConfig );
        }

        if ( IsConnectionDropped( Status ) )
        {
            DisconnectLls();
        }
#endif

        LlsSetLastStatus(Status); // called api
    }

    return (dwValue == LLS_REPLICATION_TYPE_TIME);
}


long CServer::GetReplicationTime()

/*++

Routine Description:

    Returns time in seconds between replication or seconds
    from midnight if replicating daily.

Arguments:

    None.

Return Values:

    VT_I4.

--*/

{
    LONG Status;
    DWORD dwValue = 0;

    if (InitializeIfNecessary())
    {
#ifdef CONFIG_THROUGH_REGISTRY
        DWORD dwType = REG_DWORD;
        DWORD dwSize = sizeof(DWORD);

        Status = RegQueryValueEx(
                    m_hkeyReplication,
                    REG_VALUE_REPLICATION_TIME,
                    0,
                    &dwType,
                    (LPBYTE)&dwValue,
                    &dwSize
                    );
#else
        PLLS_SERVICE_INFO_0     pConfig = NULL;

        Status = ::LlsServiceInfoGet( m_hLls, 0, (LPBYTE *) &pConfig );

        if ( NT_SUCCESS( Status ) )
        {
            dwValue = pConfig->ReplicationTime;

            ::LlsFreeMemory( pConfig->ReplicateTo );
            ::LlsFreeMemory( pConfig->EnterpriseServer );
            ::LlsFreeMemory( pConfig );
        }

        if ( IsConnectionDropped( Status ) )
        {
            DisconnectLls();
        }
#endif

        LlsSetLastStatus(Status); // called api
    }

    return dwValue;
}


BOOL CServer::ConnectLls()

/*++

Routine Description:

    Connects to license service on this server.

Arguments:

    None.

Return Values:

    VT_BOOL.

--*/

{
    NTSTATUS Status;

    if ( NULL == m_hLls )
    {
       CString strNetServerName = m_strName;

       if ( strNetServerName.Left(2).Compare( TEXT( "\\\\" ) ) )
       {
           strNetServerName = TEXT( "\\\\" ) + strNetServerName;
       }

       Status = LlsConnect( MKSTR(strNetServerName), &m_hLls );

       if ( STATUS_SUCCESS != Status )
       {
           m_hLls = NULL;
       }
       else if ( !HaveAdminAuthority() )
       {
           m_hLls = NULL;
           Status = ERROR_ACCESS_DENIED;
       }

       LlsSetLastStatus( Status );
    }

    return ( NULL != m_hLls );
}


void CServer::DisconnectLls()

/*++

Routine Description:

    Disconnects from license service on this server.

Arguments:

    None.

Return Values:

    None.

--*/

{
    if ( NULL != m_hLls )
    {
       LlsClose( m_hLls );

       m_hLls = NULL;
    }
}


BOOL CServer::HaveAdminAuthority()

/*++

Routine Description:

    Checks whether the current user has admin authority on the server.

Arguments:

    None.

Return Values:

    BOOL.

--*/

{
    BOOL           bIsAdmin;
    CString        strNetShareName;

    strNetShareName = m_strName + TEXT( "\\ADMIN$" );

    if ( strNetShareName.Left(2).Compare( TEXT( "\\\\" ) ) )
    {
        strNetShareName = TEXT( "\\\\" ) + strNetShareName;
    }

#ifdef USE_WNET_API
    DWORD          dwError;
    NETRESOURCE    NetResource;

    ZeroMemory( &NetResource, sizeof( NetResource ) );

    NetResource.dwType       = RESOURCETYPE_DISK;
    NetResource.lpLocalName  = NULL;
    NetResource.lpRemoteName = MKSTR(strNetShareName);
    NetResource.lpProvider   = NULL;

    dwError = WNetAddConnection2( &NetResource, NULL, NULL, 0 );

    bIsAdmin = ( NO_ERROR == dwError );

    if ( NO_ERROR != dwError )
    {
        bIsAdmin = FALSE;
    }
    else
    {
        bIsAdmin = TRUE;

        WNetCancelConnection2( MKSTR(strNetShareName), 0, FALSE );
    }
#else
    NET_API_STATUS  NetStatus;
    USE_INFO_1      UseInfo;
    DWORD           dwErrorParm;

    ZeroMemory( &UseInfo, sizeof( UseInfo ) );

    UseInfo.ui1_remote = MKSTR( strNetShareName );

    NetStatus = NetUseAdd( NULL, 1, (LPBYTE) &UseInfo, &dwErrorParm );

    if ( NERR_Success != NetStatus )
    {
        bIsAdmin = FALSE;
    }
    else
    {
        bIsAdmin = TRUE;

        NetStatus = NetUseDel( NULL, MKSTR(strNetShareName), 0 );
        // ignore status
    }
#endif

   return bIsAdmin;
}


BOOL CServer::IsWin2000()

/*++

Routine Description:

    Checks whether the current server is Windows 2000 or greater.

Arguments:

    None.

Return Values:

    BOOL.

--*/

{
    if ( m_IsWin2000 == uninitialized )
    {
        if ( GetName() != NULL )
        {
            NET_API_STATUS NetStatus;
            PWKSTA_INFO_100 pWkstaInfo100 = NULL;

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

            if (NetStatus == ERROR_SUCCESS)
            {
                if (pWkstaInfo100->wki100_ver_major < 5)
                {
                    m_IsWin2000 = notwin2000;
                }
                else
                {
                    m_IsWin2000 = win2000;
                }

                NetApiBufferFree(pWkstaInfo100);
            }
        }
    }

    // if still unitialized, assume win2000.

    return ( !(m_IsWin2000 == notwin2000) );
}