// Copyright (C) 1998 Microsoft Corporation
//
// DNS installation and configuration code 
//
// 6-16-98 sburns



#include "headers.hxx"
#include "resource.h"
#include "ProgressDialog.hpp"



static const DWORD HELP_MAP[] =
{
   0, 0
};
static const int NAP_TIME = 3000; // in ms



int
millisecondsToSeconds(int millis)
{
   static const int MILLIS_PER_SECOND = 1000;

   return millis / MILLIS_PER_SECOND;
}



bool
pollForDNSServiceStart(ProgressDialog& progressDialog)
{
   LOG_FUNCTION(PollForDNSServiceStart);

   for (int waitCount = 0; /* empty */ ; waitCount++)
   {
      progressDialog.UpdateText(
         String::format(
            IDS_WAITING_FOR_SERVICE_START,
            millisecondsToSeconds(NAP_TIME * waitCount)));

      if (progressDialog.WaitForButton(NAP_TIME) == ProgressDialog::PRESSED)
      {
         progressDialog.UpdateButton(String());
         popup.Info(
            progressDialog.GetHWND(),
            String::load(IDS_SKIP_DNS_MESSAGE));
         break;
      }

      if (Dns::IsServiceRunning())
      {
         // success!
         return true;
      }
   }

   return false;
}



bool
pollForDNSServiceInstallAndStart(ProgressDialog& progressDialog)
{
   LOG_FUNCTION(pollForDNSServiceInstallAndStart);

   for (int waitCount = 0; /* empty */ ; waitCount++)   
   {
      progressDialog.UpdateText(
         String::format(
            IDS_WAITING_FOR_SERVICE_INSTALL,
            millisecondsToSeconds(NAP_TIME * waitCount)));

      if (progressDialog.WaitForButton(NAP_TIME) == ProgressDialog::PRESSED)
      {
         progressDialog.UpdateButton(String());
         popup.Info(
            progressDialog.GetHWND(),
            String::load(IDS_SKIP_DNS_MESSAGE));
         break;
      }

      if (Dns::IsServiceInstalled())
      {
         // Service is installed.  Now check to see if it is running.               
         return pollForDNSServiceStart(progressDialog);
      }
   }

   return false;
}



HRESULT
createTempFile(const String& name, int textResID)
{
   LOG_FUNCTION2(createTempFile, name);
   ASSERT(!name.empty());
   ASSERT(textResID);

   HRESULT hr = S_OK;
   HANDLE h = INVALID_HANDLE_VALUE;

   do
   {
      hr =
         FS::CreateFile(
            name,
            h,
            GENERIC_WRITE,
            0, 
            CREATE_ALWAYS,
            FILE_ATTRIBUTE_NORMAL);
      BREAK_ON_FAILED_HRESULT(hr);

      AnsiString ansi;
      String::load(textResID).convert(ansi);
      ASSERT(!ansi.empty());

      // write to file with end of file character.

      hr = FS::Write(h, ansi + "\032");
      BREAK_ON_FAILED_HRESULT(hr);
   }
   while (0);

   Win::CloseHandle(h);

   return hr;
}



HRESULT
spawnDNSInstaller(PROCESS_INFORMATION& info)
{
   LOG_FUNCTION(spawnDNSInstaller);

   HRESULT hr = S_OK;
   // CODEWORK: use GetTempPath?

   String sysFolder    = Win::GetSystemDirectory();
   String infPath      = sysFolder + L"\\dcpinf.000"; 
   String unattendPath = sysFolder + L"\\dcpunat.001";

   // create the inf and unattend files for the oc manager
   do
   {
      hr = createTempFile(infPath, IDS_INSTALL_DNS_INF_TEXT);
      BREAK_ON_FAILED_HRESULT(hr);

      hr = createTempFile(unattendPath, IDS_INSTALL_DNS_UNATTEND_TEXT);
      BREAK_ON_FAILED_HRESULT(hr);

      String commandLine =
         String::format(
            IDS_INSTALL_DNS_COMMAND_LINE,
            sysFolder.c_str(),
            infPath.c_str(),
            unattendPath.c_str());

      STARTUPINFO startup;
      memset(&startup, 0, sizeof(startup));

      LOG(L"Calling CreateProcess");
      LOG(commandLine);

      hr =
         Win::CreateProcess(
            commandLine,
            0,
            0,
            false, 
            0,
            0,
            String(),
            startup,
            info);
      BREAK_ON_FAILED_HRESULT(hr);
   }
   while (0);

   return hr;
}



bool
installDNS(ProgressDialog& progressDialog)
{
   LOG_FUNCTION(installDNS);

   if (Dns::IsServiceInstalled())
   {
      LOG(L"DNS service is already installed");

      if (Dns::IsServiceRunning())
      {
         LOG(L"DNS service is already running");
         return true;
      }

      // @@ start the DNS service Dns::StartService?
   }

   progressDialog.UpdateText(String::load(IDS_INSTALLING_DNS));

   PROCESS_INFORMATION info;
   HRESULT hr = spawnDNSInstaller(info);
         
   if (FAILED(hr))
   {
      progressDialog.UpdateText(
         String::load(IDS_PROGRESS_ERROR_INSTALLING_DNS));
      popup.Error(
         progressDialog.GetHWND(),
         hr,
         IDS_ERROR_LAUNCHING_INSTALLER);
      return false;   
   }

   progressDialog.UpdateButton(IDS_PROGRESS_BUTTON_SKIP_DNS);

   // monitor the state of the installer process.
   for (int waitCount = 0; /* empty */ ; waitCount++)   
   {
      progressDialog.UpdateText(
         String::format(
            IDS_WAITING_FOR_INSTALLER,
            millisecondsToSeconds(NAP_TIME * waitCount)));

      if (progressDialog.WaitForButton(NAP_TIME) == ProgressDialog::PRESSED)
      {
         progressDialog.UpdateButton(String());
         popup.Info(
            progressDialog.GetHWND(),
            String::load(IDS_SKIP_DNS_MESSAGE));
         break;
      }

      DWORD exitCode = 0;         
      hr = Win::GetExitCodeProcess(info.hProcess, exitCode);
      if (FAILED(hr))
      {
         LOG(L"GetExitCodeProcess failed");
         LOG_HRESULT(hr);

         progressDialog.UpdateText(
            String::load(IDS_PROGRESS_ERROR_INSTALLING_DNS));
         popup.Error(
            progressDialog.GetHWND(),
            hr,
            IDS_ERROR_QUERYING_INSTALLER);
         return false;
      }

      if (exitCode != STILL_ACTIVE)
      {
         // installer has terminated.  Now check the status of the DNS
         // service
         return pollForDNSServiceInstallAndStart(progressDialog);
      }
   }

   // user bailed out
   return false;
}



bool
InstallAndConfigureDns(
   ProgressDialog&      progressDialog,
   const String&        domainDNSName)
{
   LOG_FUNCTION2(DNSSetup, domainDNSName);
   ASSERT(!domainDNSName.empty());

   if (!installDNS(progressDialog))
   {
      return false;
   }

   progressDialog.UpdateText(String::load(IDS_CONFIGURING_DNS));

   HINSTANCE dnsmgr = 0;
   HRESULT hr = Win::LoadLibrary(String::load(IDS_DNSMGR_DLL_NAME), dnsmgr);

   if (SUCCEEDED(hr))
   {
      FARPROC proc = 0;
      hr = Win::GetProcAddress(dnsmgr, L"DnsSetup", proc);

      if (SUCCEEDED(hr))
      {
         String p1 = domainDNSName;
         if (*(p1.rbegin()) != L'.')
         {
            // add trailing dot
            p1 += L'.';
         }

         String p2 = p1 + L"dns";

         LOG(L"Calling DnsSetup");
         LOG(String::format(L"lpszFwdZoneName     : %1", p1.c_str()));
         LOG(String::format(L"lpszFwdZoneFileName : %1", p2.c_str()));
         LOG(               L"lpszRevZoneName     : (null)");
         LOG(               L"lpszRevZoneFileName : (null)");
         LOG(               L"dwFlags             : 0");

         typedef HRESULT (*DNSSetup)(PCWSTR, PCWSTR, PCWSTR, PCWSTR, DWORD);      
         DNSSetup dnsproc = reinterpret_cast<DNSSetup>(proc);

         hr = dnsproc(p1.c_str(), p2.c_str(), 0, 0, 0);
      }
      else
      {
         LOG(L"unable to locate DnsSetup proc address");
      }

      HRESULT unused = Win::FreeLibrary(dnsmgr);

      ASSERT(SUCCEEDED(unused));
   }
   else
   {
      LOG(L"unable to load DNSMGR");
   }

   LOG_HRESULT(hr);

   if (FAILED(hr))
   {
      // unable to configure DNS, but it was installed.
      progressDialog.UpdateText(
         String::load(IDS_PROGRESS_ERROR_CONFIGURING_DNS));
      popup.Error(
         progressDialog.GetHWND(),
         hr,
         String::format(IDS_ERROR_CONFIGURING_DNS, domainDNSName.c_str()));

      return false;
   }

   return true;
}