#include <windows.h>
#include <wbemcli.h>
#include <wbemprov.h>
#include <stdio.h>
#include <commain.h>
#include <clsfac.h>
#include <wbemcomn.h>
#include <ql.h>
#include <sync.h>
#include <Dsrole.h>
#include "utility.h"
#include "PolicMan.h"
#include "PolicTempl.h"
#include "PolicSOM.h"
#include "PolicType.h"
#include "PolicRange.h"
#include "PolicGPO.h"
#include "PolicStatus.h"

#include <tchar.h>

#define REG_RUN_KEY L"Software\\Microsoft\\Windows\\CurrentVersion\\Run"

class CMyServer : public CComServer
{
public:
    CMyServer(void) { InitGlobalNames(); }
    ~CMyServer(void) { FreeGlobalNames(); }

    HRESULT Initialize()
    {
        AddClassInfo(CLSID_PolicySOM, 
            new CClassFactory<CPolicySOM>(GetLifeControl()), 
            _T("WMI Policy SOM Provider"), TRUE);

        AddClassInfo(CLSID_PolicyStatus,
            new CClassFactory<CPolicyStatus>(GetLifeControl()),
            _T("WMI Policy Status Provider"), TRUE);

        return S_OK;

    }
    HRESULT InitializeCom()
    {
        return CoInitializeEx(NULL, COINIT_MULTITHREADED);
    }

/*
  void Register(void)
  {
    wchar_t 
      swKeyValue[] = L"RUNDLL32.EXE %systemroot%\\system32\\wbem\\policman.dll,CreateADContainers",
      swExpandedValue[512],
      swRunOnceKey[] = REG_RUN_KEY ;

    HKEY
      hkRunOnce;

    LONG
      lReturnCode;

    lReturnCode = ExpandEnvironmentStrings(swKeyValue, swExpandedValue, 512);

    lReturnCode = RegOpenKeyEx(HKEY_LOCAL_MACHINE, swRunOnceKey, 0, KEY_SET_VALUE, &hkRunOnce);

    if(ERROR_SUCCESS != lReturnCode)
    {
      // error
    }

    lReturnCode = RegSetValueEx(hkRunOnce, L"PolicMan", 0, REG_EXPAND_SZ, (BYTE *)swExpandedValue, 
                                (lstrlen(swExpandedValue)+1) * sizeof(wchar_t));

    if(ERROR_SUCCESS != lReturnCode)
    {
      // error
    }

    RegCloseKey(hkRunOnce);
  }
*/
} Server;

HRESULT GetOrCreateObj(CComQIPtr<IADsContainer, &IID_IADsContainer> &pIADsContainer_In, 
                       CComBSTR &bstrObjName,
                       CComQIPtr<IADsContainer, &IID_IADsContainer> &pIADsContainer_Out)
{
  HRESULT
    hres = WBEM_E_FAILED;

  CComQIPtr<IDispatch, &IID_IDispatch>
    pDisp;

  CComQIPtr<IDirectoryObject, &IID_IDirectoryObject>
    pDirectoryObj;

  CComQIPtr<IADsObjectOptions, &IID_IADsObjectOptions>
    pADsObjectOptions;

  CComVariant
    vSecurityOptions;

  ADSVALUE
    AdsValue[1];

  ADS_ATTR_INFO
    attrInfo[] = { { L"ntSecurityDescriptor", ADS_ATTR_UPDATE, ADSTYPE_NT_SECURITY_DESCRIPTOR, &AdsValue[0], 1} };

  CNtSecurityDescriptor
    cSD;

  DWORD
    dwModified;

  ADS_OBJECT_INFO 
    *pADsInfo = NULL;

  if(NULL == pIADsContainer_In.p) return WBEM_E_FAILED;

  // **** get/create object

  hres = pIADsContainer_In->GetObject(L"Container", bstrObjName, &pDisp);
  if(FAILED(hres) || (NULL == pDisp.p))
  {
    CComQIPtr<IADs, &IID_IADs>
      pIADs;

    hres = pIADsContainer_In->Create(L"Container", bstrObjName, &pDisp);
    if(FAILED(hres) || (NULL == pDisp.p))
    {
      ERRORTRACE((LOG_ESS, "POLICMAN: (Container Creation) Could create container %S : 0x%x\n", (BSTR)bstrObjName, hres));
      return hres;
    }

    // **** write object to AD

    pIADs = pDisp;
    hres = pIADs->SetInfo();
    if(FAILED(hres))
    {
      ERRORTRACE((LOG_ESS, "POLICMAN: (Container Creation) Could write container %S to DS : 0x%x\n", (BSTR)bstrObjName, hres));
      return hres;
    }
  }

  // **** set object security option

  pADsObjectOptions = pDisp;
  vSecurityOptions = (ADS_SECURITY_INFO_OWNER | ADS_SECURITY_INFO_DACL);
  hres = pADsObjectOptions->SetOption(ADS_OPTION_SECURITY_MASK, vSecurityOptions);
  if(FAILED(hres))
  {
    ERRORTRACE((LOG_ESS, "POLICMAN: (Container Creation) Could set security options on object : 0x%x\n", hres));
    return hres;
  }

  // **** create security descriptor

  hres = CreateDefaultSecurityDescriptor(cSD);
  if(FAILED(hres))
  {
    ERRORTRACE((LOG_ESS, "POLICMAN: (Container Creation) Could create security descriptor : 0x%x\n", hres));
    return hres;
  }

  // **** set object security descriptor

  AdsValue[0].dwType = ADSTYPE_NT_SECURITY_DESCRIPTOR;
  AdsValue[0].SecurityDescriptor.dwLength = cSD.GetSize();
  AdsValue[0].SecurityDescriptor.lpValue = (LPBYTE)cSD.GetPtr();

  pDirectoryObj = pDisp;
  hres = pDirectoryObj->SetObjectAttributes(attrInfo, 1, &dwModified);
  if(FAILED(hres))
  {
    ERRORTRACE((LOG_ESS, "POLICMAN: (Container Creation) Could set security on object : 0x%x\n", hres));
    return hres;
  }

  pIADsContainer_Out = pDirectoryObj;

  return WBEM_S_NO_ERROR;
}

#define SYSTEM_PATH L"LDAP://CN=System,"
#define WMIPOLICY_PATH L"CN=WMIPolicy"
#define TEMPLATE_PATH L"CN=PolicyTemplate"
#define TYPE_PATH L"CN=PolicyType"
#define GPO_PATH L"CN=WMIGPO"
#define SOM_PATH L"CN=SOM"

HRESULT InScopeOfCOM_CreateADContainers(void)
{
  HRESULT
    hres = WBEM_E_FAILED;

  PDSROLE_PRIMARY_DOMAIN_INFO_BASIC 
    pBasic;

  CComPtr<IADs>
    pRootDSE;

  CComQIPtr<IADs, &IID_IADs>
    pObj;

  CComQIPtr<IADsContainer, &IID_IADsContainer>
    pWMIPolicyObj,
    pSystemObj,
    pADsContainer;

  CComVariant
    vDomainName;

  CComBSTR
    bstrSystemPath(SYSTEM_PATH),
    bstrWMIPolicy(WMIPOLICY_PATH),
    bstrTemplate(TEMPLATE_PATH),
    bstrType(TYPE_PATH),
    bstrSom(SOM_PATH),
    bstrGPO(GPO_PATH);

  // **** delay until AD is up and running

  DWORD
    dwResult = DsRoleGetPrimaryDomainInformation(NULL, DsRolePrimaryDomainInfoBasic,
                                                 (PBYTE *)&pBasic);

  if(dwResult == ERROR_SUCCESS) 
  {
    // **** Check if this is a DC

    if((pBasic->MachineRole == DsRole_RoleBackupDomainController) || 
       (pBasic->MachineRole == DsRole_RolePrimaryDomainController)) 
    {
      HANDLE 
        hEvent;

      hEvent = OpenEvent(EVENT_ALL_ACCESS, FALSE, TEXT("NtdsDelayedStartupCompletedEvent") );

      if(hEvent) {
          WaitForSingleObject(hEvent, 50000);
          CloseHandle (hEvent);
      }
    }
  }

  // **** get LDAP name of domain controller

  hres = ADsGetObject(L"LDAP://rootDSE", IID_IADs, (void**)&pRootDSE);
  if(FAILED(hres))
  {
    ERRORTRACE((LOG_ESS, "POLICMAN: (Container Creation) Could not get pointer to LDAP://rootDSE : 0x%x\n", hres));
    return hres;
  }
  else
  {
    hres = pRootDSE->Get(L"defaultNamingContext", &vDomainName);
    if(FAILED(hres) || (V_VT(&vDomainName) != VT_BSTR) || (V_BSTR(&vDomainName) == NULL))
    {
      ERRORTRACE((LOG_ESS, "POLICMAN: (Container Creation) could not get defaultNamingContext : 0x%x\n", hres));
      return hres;
    }

    bstrSystemPath.Append(vDomainName.bstrVal);
  }

  // **** get system path

  hres = ADsGetObject(bstrSystemPath, IID_IADsContainer, (void **)&pSystemObj);
  if (FAILED(hres))
  {
    ERRORTRACE((LOG_ESS, "POLICMAN: (Container Creation) Could not get pointer to %S : 0x%x\n", (BSTR)bstrSystemPath, hres));
    return hres;
  }

  // **** get/create WMIPolicy containers

  hres = GetOrCreateObj(pSystemObj, bstrWMIPolicy, pWMIPolicyObj);
  if(FAILED(hres))
  {
    ERRORTRACE((LOG_ESS, "POLICMAN: (Container Creation) Could not create/get pointer to %S : 0x%x\n", (BSTR)bstrWMIPolicy, hres));
    return hres;
  }
  else
  {
    hres = GetOrCreateObj(pWMIPolicyObj, bstrTemplate, pADsContainer);
    if(FAILED(hres))
    {
      ERRORTRACE((LOG_ESS, "POLICMAN: (Container Creation) Could not create/get pointer to %S : 0x%x\n", (BSTR)bstrTemplate, hres));
      return hres;
    }

    hres = GetOrCreateObj(pWMIPolicyObj, bstrType, pADsContainer);
    if(FAILED(hres))
    {
      ERRORTRACE((LOG_ESS, "POLICMAN: (Container Creation) Could not create/get pointer to %S : 0x%x\n", (BSTR)bstrType, hres));
      return hres;
    }

    hres = GetOrCreateObj(pWMIPolicyObj, bstrSom, pADsContainer);
    if(FAILED(hres))
    {
      ERRORTRACE((LOG_ESS, "POLICMAN: (Container Creation) Could not create/get pointer to %S : 0x%x\n", (BSTR)bstrSom, hres));
      return hres;
    }

    hres = GetOrCreateObj(pWMIPolicyObj, bstrGPO, pADsContainer);
    if(FAILED(hres))
    {
      ERRORTRACE((LOG_ESS, "POLICMAN: (Container Creation) Could not create/get pointer to %S : 0x%x\n", (BSTR)bstrGPO, hres));
      return hres;
    }
  }

  return S_OK;
}

extern "C" STDAPI CreateADContainers(void)
{
  HRESULT
    hres = WBEM_E_FAILED;

  // **** init process context

  CoInitialize(NULL);

  CoInitializeSecurity (NULL, -1, NULL, NULL,
    RPC_C_AUTHN_LEVEL_NONE, RPC_C_IMP_LEVEL_IMPERSONATE, NULL,
    EOAC_NONE, NULL);

  try
  {
    hres = InScopeOfCOM_CreateADContainers();
  }
  catch(...)
  {
    // **** error

    return WBEM_E_FAILED;
  }

  // **** if we returned successfully, then remove the run key

  if(SUCCEEDED(hres))
  {
    wchar_t
      swKeyValue[] = L"RUNDLL32.EXE %systemroot%\\system32\\wbem\\policman.dll,CreateADContainers",
      swExpandedKeyValue[512],
      swRunOnceKey[] = REG_RUN_KEY ;

    HKEY
      hkRunOnce;

    LONG
      lReturnCode;

    lReturnCode = RegOpenKeyEx(HKEY_LOCAL_MACHINE, swRunOnceKey, 0, KEY_SET_VALUE, &hkRunOnce);

    if(ERROR_SUCCESS != lReturnCode)
    {
      // error
    }

    lReturnCode = RegDeleteValue(hkRunOnce, L"PolicMan");

    if(ERROR_SUCCESS != lReturnCode)
    {
      // error
    }
  }
    
  // **** cleanup and shutdown

  CoUninitialize();

  return S_OK;
}