#include <aclapi.h>

void CRegistry::Read(LPCSTR szKeyType, HKEY hRootKey, LPCTSTR szSubKey, LPCTSTR szKeyName)
{
  HKEY hKey;
  LONG lReturnCode;
  lReturnCode = RegOpenKeyEx(hRootKey, szSubKey, 0,	KEY_READ,	&hKey);
  if(lReturnCode != ERROR_SUCCESS)
  {
    // error, or key may not exist.
  }
  else
  {
    DWORD dwSize;
    @todo Determine the type (szKeyType) and pass the appropriate value back to NSIS.

    //Read a DWORD
    //dwSize = sizeof(DWORD);
    //RegQueryValueEx(hKey, szKeyName, NULL, NULL, (LPBYTE)&m_dwData, &dwSize);

    // Read a String
    //dwSize = 128; // set to a value big enough to receive the string
    //RegQueryValueEx(hKey, szKeyName, NULL, NULL, (LPBYTE)&m_strName, &dwSize);

    // Read a Binary
    //dwSize = sizeof(double) * 4;
    //RegQueryValueEx(hKey, szKeyName, NULL, NULL,(LPBYTE)&m_lfValues, &dwSize);
  }
  if (hKey)
    RegCloseKey(hKey);
}

void CRegistry::WriteStr(HKEY hRootKey, LPCTSTR szSubKey, LPCTSTR szKeyName, LPCTSTR szValue)
{
  HKEY hKey;
  LONG lReturnCode;
  lReturnCode = RegOpenKeyEx(hRootKey, szSubKey, 0,	KEY_READ | KEY_WRITE,	&hKey);
  if(lReturnCode == ERROR_SUCCESS)
  {
    // key exists. You'll need to obtain its security rights and check them against the current user.
    // Then you'll need to call RegSetValue
  }
  else
  {
    // key doesn't exist
    DWORD dwRes;
    DWORD dwDisposition;
    PSID pEveryoneSID = NULL;
    PSID pAdminSID = NULL;
    PACL pACL = NULL;
    PSECURITY_DESCRIPTOR pSD = NULL;
    EXPLICIT_ACCESS ea[2];
    SID_IDENTIFIER_AUTHORITY SIDAuthWorld = SECURITY_WORLD_SID_AUTHORITY;
    SID_IDENTIFIER_AUTHORITY SIDAuthNT = SECURITY_NT_AUTHORITY;
    SECURITY_ATTRIBUTES sa;
    LONG lResult;

    // Create a well-known SID for the Everyone group.
    if(!AllocateAndInitializeSid(&SIDAuthWorld, 1, SECURITY_WORLD_RID, 0, 0, 0, 0, 0, 0, 0,	&pEveryoneSID))
    {
	    TRACE("AllocateAndInitializeSid (EveryoneGroup) Error: %u\n", GetLastError());
    }
    else
    {
      // Initialize an EXPLICIT_ACCESS structure for an ACE.
      // The ACE will allow Everyone read access to the key.
      ZeroMemory(&ea, 2 * sizeof(EXPLICIT_ACCESS));
      ea[0].grfAccessPermissions = KEY_READ;
      ea[0].grfAccessMode = SET_ACCESS;
      ea[0].grfInheritance= NO_INHERITANCE;
      ea[0].Trustee.TrusteeForm = TRUSTEE_IS_SID;
      ea[0].Trustee.TrusteeType = TRUSTEE_IS_WELL_KNOWN_GROUP;
      ea[0].Trustee.ptstrName  = (LPTSTR) pEveryoneSID;

      // Create a SID for the BUILTIN\Administrators group.
      if(!AllocateAndInitializeSid(&SIDAuthNT, 2, SECURITY_BUILTIN_DOMAIN_RID,
        DOMAIN_ALIAS_RID_ADMINS, 0, 0, 0, 0, 0, 0, &pAdminSID))
      {
        TRACE("AllocateAndInitializeSid (AdminGroup) Error %u\n", GetLastError());
      }
      else
      {
        // Initialize an EXPLICIT_ACCESS structure for an ACE.
        // The ACE will allow the Administrators group full access to the key.
        ea[1].grfAccessPermissions = KEY_ALL_ACCESS;
        ea[1].grfAccessMode = SET_ACCESS;
        ea[1].grfInheritance= NO_INHERITANCE;
        ea[1].Trustee.TrusteeForm = TRUSTEE_IS_SID;
        ea[1].Trustee.TrusteeType = TRUSTEE_IS_GROUP;
        ea[1].Trustee.ptstrName  = (LPTSTR) pAdminSID;

        // Create a new ACL that contains the new ACEs.
        dwRes = SetEntriesInAcl(2, ea, NULL, &pACL);
        if (ERROR_SUCCESS != dwRes)
        {
          TRACE("SetEntriesInAcl Error: %u\n", GetLastError());
        }
        else
        {
          // Initialize a security descriptor.
          pSD = (PSECURITY_DESCRIPTOR) LocalAlloc(LPTR,	SECURITY_DESCRIPTOR_MIN_LENGTH);
          if (NULL == pSD)
          {
            TRACE("LocalAlloc (Security Descriptor) Error: %u\n", GetLastError());
          }
          else
          {
            if (!InitializeSecurityDescriptor(pSD, SECURITY_DESCRIPTOR_REVISION))
            {
              TRACE("InitializeSecurityDescriptor Error: %u\n", GetLastError());
            }
            else
            {
              // Add the ACL to the security descriptor.
              if (!SetSecurityDescriptorDacl(pSD, TRUE, pACL, FALSE))
              {
                TRACE("SetSecurityDescriptorDacl Error %u\n", GetLastError());
              }
              else
              {
                // Initialize a security attributes structure.
                sa.nLength = sizeof (SECURITY_ATTRIBUTES);
                sa.lpSecurityDescriptor = pSD;
                sa.bInheritHandle = FALSE;

                // Use the security attributes to set the security descriptor
                // when you create a key
                lResult = RegCreateKeyEx(hRootKey, hSubKey, 0,	"",	0, KEY_READ | KEY_WRITE, &sa,	&hKey, &dwDisposition);
                if (lResult != ERROR_SUCCESS)
                {
                  TRACE("RegCreateKeyEx (CreateKey) Error: %u\n", lRes );
                }
                else
                {
                  // Write the value into the registry.
                  RegSetValueEx(hKey, szKeyName, 0, REG_SZ, (LPBYTE) &szValue[0], szValue.GetLength());
                }
              }
            }
          }
        }
      }
    }
  }
  if (pEveryoneSID)
  	FreeSid(pEveryoneSID);
  if (pAdminSID)
    FreeSid(pAdminSID);
  if (pACL)
    LocalFree(pACL);
  if (pSD)
    LocalFree(pSD);
  if (hKey)
    RegCloseKey(hKey);
}

void CRegistry::WriteStrEx(HKEY hRootKey, LPCTSTR szSubKey, LPCTSTR szKeyName, LPCTSTR szValue)
{
  // Basically the same as WriteStr; except for the RegSetValue call.
}

void CRegistry::WriteBin(HKEY hRootKey, LPCTSTR szSubKey, LPCTSTR szKeyName, double dValue)
{
  // Basically the same as WriteStr; except for the RegSetValue call.
}

void CRegistry::WriteDWORD(HKEY hRootKey, LPCTSTR szSubKey, LPCTSTR szKeyName, DWORD dwValue)
{
  // Basically the same as WriteStr; except for the RegSetValue call.
}

void CRegistry::Delete(HKEY hRootKey, LPCTSTR szSubKey, LPCTSTR szKeyName)
{
  HKEY hKey;
  LONG lReturnCode;
  lReturnCode = RegOpenKeyEx(hRootKey, szSubKey, 0,	KEY_READ | KEY_WRITE,	&hKey);
  if(lReturnCode != ERROR_SUCCESS)
  {
    // error, or key may not exist.
  }
  else
  {
  }
}

void CRegistry::Enumerate(HKEY hRootKey, LPCTSTR szSubKey)
{
  HKEY hKey;
  LONG lReturnCode;
  lReturnCode = RegOpenKeyEx(hRootKey, szSubKey, 0,	KEY_READ | KEY_WRITE,	&hKey);
  if(lReturnCode != ERROR_SUCCESS)
  {
    // error, or key may not exist.
  }
  else
  {
    // Enumerate the values.
  }
}
