//#--------------------------------------------------------------
//
//  File:		infohelper.cpp
//
//  Synopsis:   Implementation of helper methods
//              which are used by the sdoserverinfo COM object
//
//
//  History:     06/08/98  MKarki Created
//
//    Copyright (C) 1997-98 Microsoft Corporation
//    All rights reserved.
//
//----------------------------------------------------------------
#include "stdafx.h"
#include "infohelper.h"
#include "sdoias.h"
#include "dsconnection.h"
#include <lmcons.h>
#include <lmwksta.h>
#include <lmserver.h>
#include <lmerr.h>
#include <winldap.h>
#include <explicitlink.h>
#include <lmaccess.h>
#include <lmapibuf.h>
#include <activeds.h>
#include <winsock2.h>

//
// reg key to be queried
//
const WCHAR PRODUCT_OPTIONS_REGKEY [] =
            L"SYSTEM\\CurrentControlSet\\Control\\ProductOptions";

//
// maximum Domain name
//
const DWORD MAX_DOMAINNAME_LENGTH = 1024;

//
// this holds the matrix to get SYSTEMTYPE from the
// NTTYPE and VERSION type
//
const IASOSTYPE g_OsInfoTable [2][2] = {
                             SYSTEM_TYPE_NT4_WORKSTATION,
                             SYSTEM_TYPE_NT5_WORKSTATION,
                             SYSTEM_TYPE_NT4_SERVER,
                             SYSTEM_TYPE_NT5_SERVER
                             };

//++--------------------------------------------------------------
//
//  Function:   SdoGetOSInfo
//
//  Synopsis:   This is method used to get OS System information
//              Currently it returns the following info:
//              1) Os Version: 4 or 5
//              2) NtType:     Wks or Svr
//
//  Arguments:
//              LPCWSTR - machine name
//              PSYSTEMTYPE - info to be returned
//
//  Returns:    HRESULT
//
//  History:    MKarki      Created     06/08/98
//
//----------------------------------------------------------------
HRESULT
SdoGetOSInfo (
        /*[in]*/    LPCWSTR         lpServerName,
        /*[out]*/   PIASOSTYPE      pSystemType
        )
{
    HRESULT     hr = S_OK;
    NTVERSION   eNtVersion;
    NTTYPE      eNtType;

    _ASSERT ((NULL != lpServerName) && (NULL != pSystemType));

    do
    {
        //
        //  get the OS Version now
        //
        hr = ::GetNTVersion (lpServerName, &eNtVersion);
        if (FAILED (hr))
        {
		    IASTracePrintf(
                "Error in SDO - SdoGetOSInfo() - GetNTVersion() failed..."
                );
            break;
        }

        //
        //  get the OS type - NT Server or Workstation
        //
        hr = ::IsWorkstationOrServer (lpServerName, &eNtType);
        if (FAILED (hr))
        {
		    IASTracePrintf(
                "Error in SDO - SdoGetOSInfo()"
                "- IsWorkstationOrServer() failed..."
                );
            break;
        }

        //
        //  now decide which machine type this is
        //
        *pSystemType = g_OsInfoTable [eNtType][eNtVersion];

    } while (FALSE);

    return (hr);

}   //  end of ::SdoServerInfo method

//++--------------------------------------------------------------
//
//  Function:   SdoGetDomainInfo
//
//  Synopsis:   This is method used to get the domain type
//                information
//
//  Arguments:
//              LPCWSTR     - machine name
//              LPCWSTR     - Domain name
//              PDOMAINTYPE - Domain Info
//
//  Returns:    HRESULT
//
//  History:    MKarki      Created     06/08/98
//
//----------------------------------------------------------------
HRESULT
SdoGetDomainInfo (
        /*[in]*/   LPCWSTR          pszServerName,
        /*[in]*/   LPCWSTR          pszDomainName,
        /*[out]*/  PIASDOMAINTYPE   pDomainType
        )
{
    HRESULT hr = S_OK;
    BOOL    bHasDC = FALSE;
    BOOL    bHasDS = FALSE;
    BOOL    bMixed = FALSE;
    LPBYTE  pNetBuffer = NULL;
    WCHAR   szGeneratedDomainName [MAX_DOMAINNAME_LENGTH + 3];
    PDOMAIN_CONTROLLER_INFO pDCInfo = NULL;

    _ASSERT (pDomainType);

    //
    //  get the domain information by calling DsGetDcName
    //  where this API is supported
    //
    DWORD dwErr =  ::DsGetDcName (
                        pszServerName,
                        pszDomainName,
                        NULL,
                        NULL,
                        DS_FORCE_REDISCOVERY |
                        DS_DIRECTORY_SERVICE_PREFERRED,
                        &pDCInfo
                        );
    if (NO_ERROR == dwErr)
    {
        //
        //  we sure have a domain controller
        //
        bHasDC = TRUE;

        //
        //  check if DS is available
        //
        bHasDS = ((pDCInfo->Flags & DS_DS_FLAG) != 0);

        if (NULL == pszDomainName)
        {
            pszDomainName = pDCInfo->DomainName;
        }
    }
    else if (ERROR_NO_SUCH_DOMAIN == dwErr)
    {
        IASTracePrintf(
            "Error in SDO - SdoGetDomainInfo()"
            " - domain could not be located..."
            );
    }
    else
    {
	    IASTracePrintf(
            "Error in SDO - SdoGetDomainInfo()"
            " - DsGetDcName(DS_PREFERRED) failed with error:%d",
            dwErr
            );
        hr = HRESULT_FROM_WIN32 (dwErr);
        goto Cleanup;
    }

#if 0
    //
    // case of NT4 - which we don't support as of now
    //
    else
    {
        WCHAR szShortDomainName[MAX_DOMAINNAME_LENGTH +2];
        if (NULL != pszDomainName)
        {
            lstrcpy (szShortDomainName, pszDomainName);
            PWCHAR pTemp = wcschr (szShortDomainName, L'.');
            if (NULL != pTemp)
            {
                *pTemp = L'\0';
            }
        }

        //
        //  DsGetDcName not availabe so call NetGetDCName
        //  could be Nt 4 machine
        //
        LPBYTE pNetBuffer = NULL;
        NET_API_STATUS status = ::NetGetAnyDCName (
                                        pszServerName,
                                        (NULL == pszDomainName) ?
                                        NULL:szShortDomainName,
                                        &pNetBuffer
                                        );
        if (NERR_Success != status)
        {
			IASTracePrintf(
                    "Error in SDO - SdoGetDomainInfo()"
                    " -NetGetAnyDCName (ANY_DOMAIN) failed with error:%d",
                    status
                    );
        }
        else
        {
            //
            //  we sure have a domain controller
            //
            bHasDC = TRUE;

            //
            //  get domain name if we don't have one already
            //
            if (NULL == pszDomainName)
            {
                hr = ::SdoGetDomainName (pszServerName, szGeneratedDomainName);
                if (FAILED (hr))
                {
					IASTracePrintf(
                        "Error in SDO - SdoGetDomainInfo()"
                        " - SdoGetDomainName() failed with error:%x",
                        hr
                        );
                    goto Cleanup;
                }
            }

            //
            //  skip the leading "\\"
            //
            PWCHAR pDomainServerName =
                        2 + reinterpret_cast <PWCHAR>(pNetBuffer);
            //
            //  try connecting to LDAP port on this server
            //
            LDAP *ld = ldap_openW (
                            const_cast <PWCHAR> (pDomainServerName),
                            LDAP_PORT
                            );
            bHasDS = ld ? ldap_unbind (ld), TRUE:FALSE;
        }
    }
#endif

    //
    //  if we have NT5 DC, check if its mixed or native domain
    //
    if (TRUE == bHasDS)
    {
        hr = ::IsMixedDomain (pszDomainName, &bMixed);
        if (FAILED (hr))
        {
			IASTracePrintf(
                "Error in SDO - SdoGetOSInfo()"
                " - IsMixedDomain() failed with errror:%x",
                hr
                );
        }
    }

    //
    //   now set the info in the pDomainInfo struct
    //

    if (SUCCEEDED (hr))
    {
        if (bMixed)
            *pDomainType = DOMAIN_TYPE_MIXED;
        else if (bHasDS)
            *pDomainType = DOMAIN_TYPE_NT5;
        else if (bHasDC)
            *pDomainType = DOMAIN_TYPE_NT4;
        else
            *pDomainType = DOMAIN_TYPE_NONE;
    }

    //
    //  cleanup here
    //
Cleanup:

    if (NULL !=  pDCInfo)
        ::NetApiBufferFree (pDCInfo);

    if (NULL !=  pNetBuffer)
        ::NetApiBufferFree (pNetBuffer);

    return (hr);

}   //  end of SdoGetDomainInfo method

//++--------------------------------------------------------------
//
//  Function:   IsWorkstationOrServer
//
//  Synopsis:   This is method determines if a specific machine
//              is running NT workstation or Server
//
//  Arguments:
//              LPCWSTR - machine name
//              NTTYPE*
//
//  Returns:    HRESULT
//
//  History:    MKarki      Created     06/08/98
//
//----------------------------------------------------------------
HRESULT
IsWorkstationOrServer (
        /*[in]*/    LPCWSTR pszComputerName,
        /*[out]*/   NTTYPE  *pNtType
        )
{
    HRESULT hr = S_OK;
    WCHAR   szCompleteName [IAS_MAX_SERVER_NAME + 3];
    PWCHAR  pszTempName = const_cast <PWCHAR> (pszComputerName);

    _ASSERT ((NULL != pszComputerName) && (NULL !=  pNtType));

    do
    {
        //
        // the computer name should have a "\\" in front
        //
        if ((L'\\' != *pszComputerName) || (L'\\' != *(pszComputerName + 1)))
        {
            if (::wcslen (pszComputerName) > IAS_MAX_SERVER_NAME)
            {
                IASTracePrintf(
                    "Error in Server Info SDO - IsWorkstationOrServer()"
                    " - Computer name is too big..."
                    );
                hr = E_FAIL;
                break;
            }
            ::wcscpy (szCompleteName, L"\\\\");
            ::wcscat (szCompleteName, pszComputerName);
            pszTempName = szCompleteName;
        }

        //
        // Connect to the registry
        //
        HKEY  hResult;
        DWORD dwErr = ::RegConnectRegistry (
                                pszTempName,
                                HKEY_LOCAL_MACHINE,
                                &hResult
                                );
        if (ERROR_SUCCESS != dwErr)
        {
		    IASTracePrintf(
                "Error in SDO - IsWorkstationOrServer()"
                " - RegConnectRegistry() failed with error:%d",
                dwErr
                );
            hr = HRESULT_FROM_WIN32 (dwErr);
            break;
        }

        //
        //  open the registry key now
        //
        HKEY hValueKey;
        dwErr = ::RegOpenKeyEx (
                        hResult,
                        PRODUCT_OPTIONS_REGKEY,
                        0,
                        KEY_QUERY_VALUE,
                        &hValueKey
                        );
        if (ERROR_SUCCESS != dwErr)
        {
		    IASTracePrintf(
                "Error in SDO - IsWorkstationOrServer()"
                " - RegOpenKeyEx() failed with error:%d",
                hr
                );
            RegCloseKey (hResult);
            hr = HRESULT_FROM_WIN32 (dwErr);
            break;
        }

        //
        //  get the value now
        //
        WCHAR szProductType [MAX_PATH];
        DWORD dwBufferLength = MAX_PATH;
        dwErr = RegQueryValueEx (
                        hValueKey,
                        L"ProductType",
                        NULL,
                        NULL,
                        (LPBYTE)szProductType,
                        &dwBufferLength
                        );
        if (ERROR_SUCCESS != dwErr)
        {
		    IASTracePrintf(
                "Error in SDO - IsWorkstationOrServer()"
                " - RegQueryValueEx() failed with error:%d",
                hr
                );
            RegCloseKey (hValueKey);
            RegCloseKey (hResult);
            hr = HRESULT_FROM_WIN32 (dwErr);
        }

        //
        //  determine which NT Type we have on this machine
        //
        if (_wcsicmp (L"WINNT", szProductType) == 0)
        {
            *pNtType = NT_WKSTA;
        }
        else if (!_wcsicmp (L"SERVERNT", szProductType) ||
                 !_wcsicmp (L"LanmanNT", szProductType))
        {
            *pNtType = NT_SVR;
        }
        else
        {
		    IASTracePrintf(
                "Error in SDO - IsWorkstationOrServer()"
                " - Could not determine machine type..."
                );
            RegCloseKey (hValueKey);
            RegCloseKey (hResult);
            hr = E_FAIL;
        }

        //
        //  cleanup
        //
        RegCloseKey (hValueKey);
        RegCloseKey (hResult);

    } while (FALSE);

    return (hr);

}   //  end of  ::IsWorkstationOrServer method

//++--------------------------------------------------------------
//
//  Function:   GetNTVersion
//
//  Synopsis:   This is method determines which version of NT
//              is running on this machine
//
//  Arguments:
//              LPCWSTR - machine name
//              NTVERSION*
//
//  Returns:    HRESULT
//
//  History:    MKarki      Created     06/08/98
//
//----------------------------------------------------------------
HRESULT
GetNTVersion (
        /*[in]*/    LPCWSTR     lpComputerName,
        /*[out]*/   NTVERSION   *pNtVersion
        )
{
    HRESULT hr = S_OK;

    _ASSERT ((NULL  != lpComputerName) && (NULL != pNtVersion));

    do
    {
        //
        //  get level 100 workstation information
        //
        PWKSTA_INFO_100 pInfo = NULL;
        DWORD dwErr = ::NetWkstaGetInfo (
                                (LPWSTR)lpComputerName,
                                100,
                                (LPBYTE*)&pInfo
                                );
        if (NERR_Success != dwErr)
        {
		    IASTracePrintf(
                "Error in SDO - GetNTVersion()"
                "- NTWkstaGetInfo failed with error:%d",
                dwErr
                );
            hr = HRESULT_FROM_WIN32 (dwErr);
            break;
        }

        //
        //  get the version info
        //
        if (4 == pInfo->wki100_ver_major)
        {
            *pNtVersion = NTVERSION_4;
        }
        else if ( 5 == pInfo->wki100_ver_major)
        {
            *pNtVersion = NTVERSION_5;
        }
        else
        {
		    IASTracePrintf(
                    "Error in SDO - GetNTVersion()"
                    " - Unsupported OS version..."
                    );
            hr = E_FAIL;
        }

    } while (FALSE);

    return (hr);

}   //  end of ::GetNTVersion method

//++--------------------------------------------------------------
//
//  Function:   IsMixedDomain
//
//  Synopsis:   This is method determines which version of NT
//              is running on this machine
//
//  Arguments:
//              [in]    LPCWSTR - machine name
//              [out]   PBOOL   - is mixed
//
//  Returns:    HRESULT
//
//  History:    MKarki      Created     06/08/98
//
//----------------------------------------------------------------
HRESULT
IsMixedDomain (
            /*[in]*/    LPCWSTR pszDomainName,
            /*[out]*/   PBOOL   pbIsMixed
            )
{
    HRESULT hr = S_OK;
    WCHAR szTempName [MAX_DOMAINNAME_LENGTH + 8];

    _ASSERT ((NULL != pszDomainName) && (NULL != pbIsMixed));

    do
    {
        //
        // check the arguments passedin
        //
        if ((NULL == pszDomainName) || (NULL == pbIsMixed))
        {
		    IASTracePrintf(
                "Error in SDO - IsMixedDomain()"
                " - Invalid parameter - NULL"
                );
            hr = E_INVALIDARG;
            break;
        }

        if (::wcslen (pszDomainName) > MAX_DOMAINNAME_LENGTH)
        {
		    IASTracePrintf(
                "Error in SDO - IsMixedDomain()"
                " - Invalid parameter (domain name is to long)..."
                );
            hr = E_FAIL;
            break;
        }

        //
        // form the DN name
        //
        wcscpy (szTempName, L"LDAP://");
        wcscat (szTempName, pszDomainName);

        //
        //  get the domain object
        //
        CComPtr <IADs> pIADs;
        hr = ::ADsGetObject (
                        szTempName,
                        IID_IADs,
                        reinterpret_cast <PVOID*> (&pIADs)
                        );
        if (FAILED (hr))
        {
		    IASTracePrintf(
                "Error in SDO - IsMixedDomain()"
                " - Could not get the domain object from the DS with error:%x",
                hr
                );
            break;
        }

        //
        //  get the Mixed Domain info
        //
        _variant_t varMixedInfo;
        hr = pIADs->Get (L"nTMixedDomain", &varMixedInfo);
        if (FAILED (hr))
        {
            if (E_ADS_PROPERTY_NOT_FOUND == hr)
            {
                //
                //  this is OK
                //
                *pbIsMixed = FALSE;
                hr = S_OK;
            }
            else
            {
			    IASTracePrintf(
                    "Error in SDO - IsMixedDomain()"
                     "- Could not get the 'nTMixedDomain' property"
                    "from the domain object, failed with error:%x",
                    hr
                    );
            }
            break;
        }

        _ASSERT (
             (VT_BOOL == V_VT (&varMixedInfo)) ||
             (VT_I4 == V_VT (&varMixedInfo))
            );

        //
        //  get the values from the variant
        //
        if (VT_I4 == V_VT (&varMixedInfo))
        {
            *pbIsMixed = V_I4 (&varMixedInfo);
        }
        else if (VT_BOOL == V_VT (&varMixedInfo))
        {
            *pbIsMixed =  (VARIANT_TRUE == V_BOOL (&varMixedInfo));
        }
        else
        {
		    IASTracePrintf(
                "Error in SDO - IsMixedDomain()"
                "-'nTMixedDomain property has an invalid value..."
                );
            hr = E_FAIL;
            break;
        }

    }  while (FALSE);

    return (hr);

}   // end of IsMixedDomain

//++--------------------------------------------------------------
//
//  Function:   SdoGetDomainName
//
//  Synopsis:   This is method determines the domain name
//              given the server name
//
//  Arguments:
//              LPCWSTR - machine name
//              LPWSTR  - pDomanName
//
//  Returns:    HRESULT
//
//  History:    MKarki      Created     06/08/98
//
//----------------------------------------------------------------
HRESULT
SdoGetDomainName (
            /*[in]*/    LPCWSTR pszServerName,
            /*[out]*/   LPWSTR  pDomainName
            )
{
    _ASSERT (NULL != pDomainName);

#if 0
    SERVER_INFO_503 ServerInfo;
    ServerInfo.sv503_domain = pDomainName;
    DWORD dwErr = ::NetServerGetInfo (
                        const_cast <LPWSTR> (pszServerName),
                        503,
                        reinterpret_cast <LPBYTE*> (&ServerInfo)
                        );
    if (NERR_Success != dwErr)
    {
		IASTracePrintf("Error in SDO - SdoGetDomainName() - NetServerGetInfo() failed...");
        return (HRESULT_FROM_WIN32 (dwErr));
    }

    return (S_OK);


#endif
    return (E_FAIL);

}   //  end of ::SdoGetDomainName method