/*
 * Regedit ACL Editor for Registry Keys
 *
 * Copyright (C) 2004-2006 Thomas Weidenmueller <w3seek@reactos.com>
 * LICENSE: LGPL-2.1-or-later (https://spdx.org/licenses/LGPL-2.1-or-later)
 */

#include "regedit.h"

#define INITGUID
#include <guiddef.h>

/* FIXME - shouldn't be defined here... */
DEFINE_GUID(IID_IRegKeySecurity, 0x965fc360, 0x16ff, 0x11d0, 0x0091, 0xcb,0x00,0xaa,0x00,0xbb,0xb7,0x23);
#if REGEDIT_IMPLEMENT_ISECURITYINFORMATION2
DEFINE_GUID(IID_IRegKeySecurity2, 0xc3ccfdb4, 0x6f88, 0x11d2, 0x00a3, 0xce,0x00,0xc0,0x4f,0xb1,0x78,0x2a);
#endif

/* FIXME: already defined in aclui.h - causing problems when compiling with MSVC/PSDK*/
#ifdef _MSC_VER
#pragma message ("INVESTIGATE ME")
#endif

#if 1 //#ifndef _MSC_VER
DEFINE_GUID(IID_IEffectivePermission, 0x3853dc76, 0x9f35, 0x407c, 0x0088, 0xa1,0xd1,0x93,0x44,0x36,0x5f,0xbc);
DEFINE_GUID(IID_ISecurityObjectTypeInfo, 0xfc3066eb, 0x79ef, 0x444b, 0x0091, 0x11,0xd1,0x8a,0x75,0xeb,0xf2,0xfa);
#endif

/******************************************************************************
   Implementation of the IUnknown methods of CRegKeySecurity
 ******************************************************************************/

static __inline PCRegKeySecurity
impl_from_ISecurityInformation(struct ISecurityInformation *iface)
{
    return (PCRegKeySecurity)((ULONG_PTR)iface - FIELD_OFFSET(CRegKeySecurity,
                                                              lpISecurityInformationVtbl));
}

#if REGEDIT_IMPLEMENT_ISECURITYINFORMATION2
static __inline PCRegKeySecurity
impl_from_ISecurityInformation2(struct ISecurityInformation2 *iface)
{
    return (PCRegKeySecurity)((ULONG_PTR)iface - FIELD_OFFSET(CRegKeySecurity,
                                                              lpISecurityInformation2Vtbl));
}
#endif

static __inline PCRegKeySecurity
impl_from_ISecurityObjectTypeInfo(struct ISecurityObjectTypeInfo *iface)
{
    return (PCRegKeySecurity)((ULONG_PTR)iface - FIELD_OFFSET(CRegKeySecurity,
                                                              lpISecurityObjectTypeInfoVtbl));
}

static __inline PCRegKeySecurity
impl_from_IEffectivePermission(struct IEffectivePermission *iface)
{
    return (PCRegKeySecurity)((ULONG_PTR)iface - FIELD_OFFSET(CRegKeySecurity,
                                                              lpIEffectivePermissionVtbl));
}

#define impl_to_interface(impl,iface) (struct iface *)(&(impl)->lp##iface##Vtbl)

static __inline ULONG
CRegKeySecurity_fnAddRef(PCRegKeySecurity obj)
{
    return (ULONG)InterlockedIncrement((LONG*)&obj->ref);
}

static __inline ULONG
CRegKeySecurity_fnRelease(PCRegKeySecurity obj)
{
    ULONG Ret;

    Ret = (ULONG)InterlockedDecrement((LONG*)&obj->ref);
    if (Ret == 0)
    {
        HeapFree(GetProcessHeap(), 0, obj);
    }

    return Ret;
}

static __inline HRESULT
CRegKeySecurity_fnQueryInterface(PCRegKeySecurity obj,
                                 REFIID iid,
                                 PVOID *pvObject)
{
    PVOID pvObj = NULL;

    if (IsEqualGUID(iid, &IID_IRegKeySecurity))
    {
        pvObj = (PVOID)impl_to_interface(obj, ISecurityInformation);
    }
#if REGEDIT_IMPLEMENT_ISECURITYINFORMATION2
    else if (IsEqualGUID(iid, &IID_IRegKeySecurity2))
    {
        pvObj = (PVOID)impl_to_interface(obj, ISecurityInformation2);
    }
#endif
    else if (IsEqualGUID(iid, &IID_IEffectivePermission))
    {
        pvObj = (PVOID)impl_to_interface(obj, IEffectivePermission);
    }
    else if (IsEqualGUID(iid, &IID_ISecurityObjectTypeInfo))
    {
        pvObj = (PVOID)impl_to_interface(obj, ISecurityObjectTypeInfo);
    }

    if (pvObj == NULL)
    {
        return E_NOINTERFACE;
    }

    *pvObject = pvObj;
    CRegKeySecurity_fnAddRef(obj);

    return S_OK;
}


/******************************************************************************
   Definition of the ISecurityInformation interface
 ******************************************************************************/

/* IUnknown */
static HRESULT STDMETHODCALLTYPE
ISecurityInformation_fnQueryInterface(struct ISecurityInformation *this,
                                      REFIID iid,
                                      PVOID *pvObject);

static ULONG STDMETHODCALLTYPE
ISecurityInformation_fnAddRef(struct ISecurityInformation *this);

static ULONG STDMETHODCALLTYPE
ISecurityInformation_fnRelease(struct ISecurityInformation *this);

/* ISecurityInformation */
static HRESULT STDMETHODCALLTYPE
ISecurityInformation_fnGetObjectInformation(struct ISecurityInformation *this,
                                            PSI_OBJECT_INFO pObjectInfo);

static HRESULT STDMETHODCALLTYPE
ISecurityInformation_fnGetSecurity(struct ISecurityInformation *this,
                                   SECURITY_INFORMATION RequestedInformation,
                                   PSECURITY_DESCRIPTOR* ppSecurityDescriptor,
                                   BOOL fDefault);

static HRESULT STDMETHODCALLTYPE
ISecurityInformation_fnSetSecurity(struct ISecurityInformation *this,
                                   SECURITY_INFORMATION RequestedInformation,
                                   PSECURITY_DESCRIPTOR pSecurityDescriptor);

static HRESULT STDMETHODCALLTYPE
ISecurityInformation_fnGetAccessRights(struct ISecurityInformation *this,
                                       const GUID* pguidObjectType,
                                      DWORD dwFlags,
                                      PSI_ACCESS* ppAccess,
                                      ULONG* pcAccesses,
                                      ULONG* piDefaultAccess);

static HRESULT STDMETHODCALLTYPE
ISecurityInformation_fnMapGeneric(struct ISecurityInformation *this,
                                 const GUID* pguidObjectType,
                                 UCHAR* pAceFlags,
                                 ACCESS_MASK* pMask);

static HRESULT STDMETHODCALLTYPE
ISecurityInformation_fnGetInheritTypes(struct ISecurityInformation *this,
                                      PSI_INHERIT_TYPE* ppInheritTypes,
                                      ULONG* pcInheritTypes);
static HRESULT STDMETHODCALLTYPE
ISecurityInformation_fnPropertySheetPageCallback(struct ISecurityInformation *this,
                                                HWND hwnd,
                                                UINT uMsg,
                                                SI_PAGE_TYPE uPage);

static const struct ifaceISecurityInformationVbtl vtblISecurityInformation =
{
    /* IUnknown methods */
    ISecurityInformation_fnQueryInterface,
    ISecurityInformation_fnAddRef,
    ISecurityInformation_fnRelease,

    /* ISecurityInformation methods */
    ISecurityInformation_fnGetObjectInformation,
    ISecurityInformation_fnGetSecurity,
    ISecurityInformation_fnSetSecurity,
    ISecurityInformation_fnGetAccessRights,
    ISecurityInformation_fnMapGeneric,
    ISecurityInformation_fnGetInheritTypes,
    ISecurityInformation_fnPropertySheetPageCallback,
};

#if REGEDIT_IMPLEMENT_ISECURITYINFORMATION2
/******************************************************************************
   Definition of the ISecurityInformation2 interface
 ******************************************************************************/

/* IUnknown */
static HRESULT STDMETHODCALLTYPE
ISecurityInformation2_fnQueryInterface(struct ISecurityInformation2 *this,
                                       REFIID iid,
                                       PVOID *pvObject);

static ULONG STDMETHODCALLTYPE
ISecurityInformation2_fnAddRef(struct ISecurityInformation2 *this);

static ULONG STDMETHODCALLTYPE
ISecurityInformation2_fnRelease(struct ISecurityInformation2 *this);

/* ISecurityInformation2 */
static BOOL STDMETHODCALLTYPE
ISecurityInformation2_fnIsDaclCanonical(struct ISecurityInformation2 *this,
                                        PACL pDacl);

static HRESULT STDMETHODCALLTYPE
ISecurityInformation2_fnLookupSids(struct ISecurityInformation2 *this,
                                   ULONG cSids,
                                   PSID* rgpSids,
                                   LPDATAOBJECT* ppdo);

static const struct ifaceISecurityInformation2Vbtl vtblISecurityInformation2 =
{
    /* IUnknown methods */
    ISecurityInformation2_fnQueryInterface,
    ISecurityInformation2_fnAddRef,
    ISecurityInformation2_fnRelease,

    /* ISecurityInformation2 methods */
    ISecurityInformation2_fnIsDaclCanonical,
    ISecurityInformation2_fnLookupSids
};
#endif

/******************************************************************************
   Definition of the IEffectivePermission interface
 ******************************************************************************/

/* IUnknown */
static HRESULT STDMETHODCALLTYPE
IEffectivePermission_fnQueryInterface(struct IEffectivePermission *this,
                                      REFIID iid,
                                      PVOID *pvObject);

static ULONG STDMETHODCALLTYPE
IEffectivePermission_fnAddRef(struct IEffectivePermission *this);

static ULONG STDMETHODCALLTYPE
IEffectivePermission_fnRelease(struct IEffectivePermission *this);

/* IEffectivePermission */
static HRESULT STDMETHODCALLTYPE
IEffectivePermission_fnGetEffectivePermission(struct IEffectivePermission *this,
                                              const GUID* pguidObjectType,
                                              PSID pUserSid,
                                              LPCWSTR pszServerName,
                                              PSECURITY_DESCRIPTOR pSD,
                                              POBJECT_TYPE_LIST* ppObjectTypeList,
                                              ULONG* pcObjectTypeListLength,
                                              PACCESS_MASK* ppGrantedAccessList,
                                              ULONG* pcGrantedAccessListLength);

static const struct ifaceIEffectivePermissionVbtl vtblIEffectivePermission =
{
    /* IUnknown methods */
    IEffectivePermission_fnQueryInterface,
    IEffectivePermission_fnAddRef,
    IEffectivePermission_fnRelease,

    /* IEffectivePermissions methods */
    IEffectivePermission_fnGetEffectivePermission
};

/******************************************************************************
   Definition of the ISecurityObjectTypeInfo interface
 ******************************************************************************/

/* IUnknown */
static HRESULT STDMETHODCALLTYPE
ISecurityObjectTypeInfo_fnQueryInterface(struct ISecurityObjectTypeInfo *this,
                                         REFIID iid,
                                         PVOID *pvObject);

static ULONG STDMETHODCALLTYPE
ISecurityObjectTypeInfo_fnAddRef(struct ISecurityObjectTypeInfo *this);

static ULONG STDMETHODCALLTYPE
ISecurityObjectTypeInfo_fnRelease(struct ISecurityObjectTypeInfo *this);

/* ISecurityObjectTypeInfo */
static HRESULT STDMETHODCALLTYPE
ISecurityObjectTypeInfo_fnGetInheritSource(struct ISecurityObjectTypeInfo *this,
                                           SECURITY_INFORMATION si,
                                           PACL pACL,
                                           PINHERITED_FROM* ppInheritArray);

static const struct ifaceISecurityObjectTypeInfoVbtl vtblISecurityObjectTypeInfo =
{
    /* IUnknown methods */
    ISecurityObjectTypeInfo_fnQueryInterface,
    ISecurityObjectTypeInfo_fnAddRef,
    ISecurityObjectTypeInfo_fnRelease,

    /* ISecurityObjectTypeInfo methods */
    ISecurityObjectTypeInfo_fnGetInheritSource
};


/******************************************************************************
   Implementation of the ISecurityInformation interface
 ******************************************************************************/

static SI_ACCESS RegAccess[] = {
    {&GUID_NULL, KEY_ALL_ACCESS,         MAKEINTRESOURCEW(IDS_ACCESS_FULLCONTROL),      SI_ACCESS_GENERAL | SI_ACCESS_SPECIFIC},
    {&GUID_NULL, KEY_READ,               MAKEINTRESOURCEW(IDS_ACCESS_READ),             SI_ACCESS_GENERAL},
    {&GUID_NULL, KEY_QUERY_VALUE,        MAKEINTRESOURCEW(IDS_ACCESS_QUERYVALUE),       SI_ACCESS_SPECIFIC},
    {&GUID_NULL, KEY_SET_VALUE,          MAKEINTRESOURCEW(IDS_ACCESS_SETVALUE),         SI_ACCESS_SPECIFIC},
    {&GUID_NULL, KEY_CREATE_SUB_KEY,     MAKEINTRESOURCEW(IDS_ACCESS_CREATESUBKEY),     SI_ACCESS_SPECIFIC},
    {&GUID_NULL, KEY_ENUMERATE_SUB_KEYS, MAKEINTRESOURCEW(IDS_ACCESS_ENUMERATESUBKEYS), SI_ACCESS_SPECIFIC},
    {&GUID_NULL, KEY_NOTIFY,             MAKEINTRESOURCEW(IDS_ACCESS_NOTIFY),           SI_ACCESS_SPECIFIC},
    {&GUID_NULL, KEY_CREATE_LINK,        MAKEINTRESOURCEW(IDS_ACCESS_CREATELINK),       SI_ACCESS_SPECIFIC},
    {&GUID_NULL, DELETE,                 MAKEINTRESOURCEW(IDS_ACCESS_DELETE),           SI_ACCESS_SPECIFIC},
    {&GUID_NULL, WRITE_DAC,              MAKEINTRESOURCEW(IDS_ACCESS_WRITEDAC),         SI_ACCESS_SPECIFIC},
    {&GUID_NULL, WRITE_OWNER,            MAKEINTRESOURCEW(IDS_ACCESS_WRITEOWNER),       SI_ACCESS_SPECIFIC},
    {&GUID_NULL, READ_CONTROL,           MAKEINTRESOURCEW(IDS_ACCESS_READCONTROL),      SI_ACCESS_SPECIFIC},
};

static const DWORD RegDefaultAccess = 1; /* KEY_READ */

static GENERIC_MAPPING RegAccessMasks = {
    KEY_READ,
    KEY_WRITE,
    KEY_EXECUTE,
    KEY_ALL_ACCESS
};

static SI_INHERIT_TYPE RegInheritTypes[] = {
    {&GUID_NULL, 0,                                        (LPWSTR)MAKEINTRESOURCEW(IDS_INHERIT_THISKEYONLY)},
    {&GUID_NULL, CONTAINER_INHERIT_ACE,                    (LPWSTR)MAKEINTRESOURCEW(IDS_INHERIT_THISKEYANDSUBKEYS)},
    {&GUID_NULL, INHERIT_ONLY_ACE | CONTAINER_INHERIT_ACE, (LPWSTR)MAKEINTRESOURCEW(IDS_INHERIT_SUBKEYSONLY)},
};

static HRESULT STDMETHODCALLTYPE
ISecurityInformation_fnQueryInterface(struct ISecurityInformation *this,
                                      REFIID iid,
                                      PVOID *pvObject)
{
    if (IsEqualGUID(iid, &IID_IUnknown))
    {
        *pvObject = (PVOID)this;
        ISecurityInformation_fnAddRef(this);
        return S_OK;
    }

    return CRegKeySecurity_fnQueryInterface(impl_from_ISecurityInformation(this),
                                            iid,
                                            pvObject);
}

static ULONG STDMETHODCALLTYPE
ISecurityInformation_fnAddRef(struct ISecurityInformation *this)
{
    return CRegKeySecurity_fnAddRef(impl_from_ISecurityInformation(this));
}

static ULONG STDMETHODCALLTYPE
ISecurityInformation_fnRelease(struct ISecurityInformation *this)
{
    return CRegKeySecurity_fnRelease(impl_from_ISecurityInformation(this));
}

static HRESULT STDMETHODCALLTYPE
ISecurityInformation_fnGetObjectInformation(struct ISecurityInformation *this,
                                            PSI_OBJECT_INFO pObjectInfo)
{
    PCRegKeySecurity obj = impl_from_ISecurityInformation(this);

    *pObjectInfo = obj->ObjectInfo;
    return S_OK;
}

static HRESULT STDMETHODCALLTYPE
ISecurityInformation_fnGetSecurity(struct ISecurityInformation *this,
                                   SECURITY_INFORMATION RequestedInformation,
                                   PSECURITY_DESCRIPTOR* ppSecurityDescriptor,
                                   BOOL fDefault)
{
    PCRegKeySecurity obj = impl_from_ISecurityInformation(this);
    LONG ErrorCode;

    ErrorCode = GetNamedSecurityInfoW(obj->szRegKey,
                                      SE_REGISTRY_KEY,
                                      RequestedInformation,
                                      NULL,
                                      NULL,
                                      NULL,
                                      NULL,
                                      ppSecurityDescriptor);

    return HRESULT_FROM_WIN32(ErrorCode);
}

static HRESULT STDMETHODCALLTYPE
ISecurityInformation_fnSetSecurity(struct ISecurityInformation *this,
                                   SECURITY_INFORMATION RequestedInformation,
                                   PSECURITY_DESCRIPTOR pSecurityDescriptor)
{
    PCRegKeySecurity obj = impl_from_ISecurityInformation(this);

    /* FIXME */
    *obj->Btn = TRUE;
    return S_OK;
}

static HRESULT STDMETHODCALLTYPE
ISecurityInformation_fnGetAccessRights(struct ISecurityInformation *this,
                                       const GUID* pguidObjectType,
                                       DWORD dwFlags,
                                       PSI_ACCESS* ppAccess,
                                       ULONG* pcAccesses,
                                       ULONG* piDefaultAccess)
{
    *ppAccess = RegAccess;
    *pcAccesses = ARRAY_SIZE(RegAccess);
    *piDefaultAccess = RegDefaultAccess;
    return S_OK;
}

static HRESULT STDMETHODCALLTYPE
ISecurityInformation_fnMapGeneric(struct ISecurityInformation *this,
                                  const GUID* pguidObjectType,
                                  UCHAR* pAceFlags,
                                  ACCESS_MASK* pMask)
{
    MapGenericMask(pMask, &RegAccessMasks);
    *pMask &= ~SYNCHRONIZE;
    return S_OK;
}

static HRESULT STDMETHODCALLTYPE
ISecurityInformation_fnGetInheritTypes(struct ISecurityInformation *this,
                                       PSI_INHERIT_TYPE* ppInheritTypes,
                                       ULONG* pcInheritTypes)
{
    PCRegKeySecurity obj = impl_from_ISecurityInformation(this);

    /* FIXME */
    if (obj->ObjectInfo.dwFlags & SI_CONTAINER)
    {
        *ppInheritTypes = RegInheritTypes;
        *pcInheritTypes = ARRAY_SIZE(RegInheritTypes);
        return S_OK;
    }

    return E_NOTIMPL;
}

static HRESULT STDMETHODCALLTYPE
ISecurityInformation_fnPropertySheetPageCallback(struct ISecurityInformation *this,
    HWND hwnd,
    UINT uMsg,
    SI_PAGE_TYPE uPage)
{
    return S_OK;
}

#if REGEDIT_IMPLEMENT_ISECURITYINFORMATION2
/******************************************************************************
   Implementation of the ISecurityInformation2 interface
 ******************************************************************************/

static HRESULT STDMETHODCALLTYPE
ISecurityInformation2_fnQueryInterface(struct ISecurityInformation2 *this,
                                       REFIID iid,
                                       PVOID *pvObject)
{
    if (IsEqualGUID(iid, &IID_IUnknown))
    {
        *pvObject = (PVOID)this;
        ISecurityInformation2_fnAddRef(this);
        return S_OK;
    }

    return CRegKeySecurity_fnQueryInterface(impl_from_ISecurityInformation2(this),
                                            iid,
                                            pvObject);
}

static ULONG STDMETHODCALLTYPE
ISecurityInformation2_fnAddRef(struct ISecurityInformation2 *this)
{
    return CRegKeySecurity_fnAddRef(impl_from_ISecurityInformation2(this));
}

static ULONG STDMETHODCALLTYPE
ISecurityInformation2_fnRelease(struct ISecurityInformation2 *this)
{
    return CRegKeySecurity_fnRelease(impl_from_ISecurityInformation2(this));
}

static BOOL STDMETHODCALLTYPE
ISecurityInformation2_fnIsDaclCanonical(struct ISecurityInformation2 *this,
                                        PACL pDacl)
{
    /* FIXME */
    return TRUE;
}

static HRESULT STDMETHODCALLTYPE
ISecurityInformation2_fnLookupSids(struct ISecurityInformation2 *this,
                                   ULONG cSids,
                                   PSID* rgpSids,
                                   LPDATAOBJECT* ppdo)
{
    /* FIXME */
    return E_NOTIMPL;
}
#endif

/******************************************************************************
   Implementation of the IEffectivePermission interface
 ******************************************************************************/

static HRESULT STDMETHODCALLTYPE
IEffectivePermission_fnQueryInterface(struct IEffectivePermission *this,
                                      REFIID iid,
                                      PVOID *pvObject)
{
    if (IsEqualGUID(iid, &IID_IUnknown))
    {
        *pvObject = (PVOID)this;
        IEffectivePermission_fnAddRef(this);
        return S_OK;
    }

    return CRegKeySecurity_fnQueryInterface(impl_from_IEffectivePermission(this),
                                            iid,
                                            pvObject);
}

static ULONG STDMETHODCALLTYPE
IEffectivePermission_fnAddRef(struct IEffectivePermission *this)
{
    return CRegKeySecurity_fnAddRef(impl_from_IEffectivePermission(this));
}

static ULONG STDMETHODCALLTYPE
IEffectivePermission_fnRelease(struct IEffectivePermission *this)
{
    return CRegKeySecurity_fnRelease(impl_from_IEffectivePermission(this));
}

static HRESULT STDMETHODCALLTYPE
IEffectivePermission_fnGetEffectivePermission(struct IEffectivePermission *this,
                                              const GUID* pguidObjectType,
                                              PSID pUserSid,
                                              LPCWSTR pszServerName,
                                              PSECURITY_DESCRIPTOR pSD,
                                              POBJECT_TYPE_LIST* ppObjectTypeList,
                                              ULONG* pcObjectTypeListLength,
                                              PACCESS_MASK* ppGrantedAccessList,
                                              ULONG* pcGrantedAccessListLength)
{
    PACL Dacl = NULL;
    BOOL DaclPresent, DaclDefaulted;
    PACCESS_MASK GrantedAccessList;
    DWORD ErrorCode = ERROR_SUCCESS;
    TRUSTEE Trustee = {0};
    static OBJECT_TYPE_LIST DefObjTypeList = {0};

    *ppObjectTypeList = &DefObjTypeList;
    *pcObjectTypeListLength = 1;

    BuildTrusteeWithSid(&Trustee, pUserSid);

    if (GetSecurityDescriptorDacl(pSD,
                                  &DaclPresent,
                                  &Dacl,
                                  &DaclDefaulted) && DaclPresent)
    {
        GrantedAccessList = (PACCESS_MASK)LocalAlloc(LMEM_FIXED,
                                                     sizeof(ACCESS_MASK));
        if (GrantedAccessList == NULL)
        {
            goto Fail;
        }

        ErrorCode = GetEffectiveRightsFromAcl(Dacl,
                                              &Trustee,
                                              GrantedAccessList);
        if (ErrorCode == ERROR_SUCCESS)
        {
            *ppGrantedAccessList = GrantedAccessList;
            *pcGrantedAccessListLength = 1;
        }
        else
            LocalFree((HLOCAL)GrantedAccessList);
    }
    else
Fail:
        ErrorCode = GetLastError();

    return HRESULT_FROM_WIN32(ErrorCode);
}

/******************************************************************************
   Implementation of the ISecurityObjectTypeInfo interface
 ******************************************************************************/

static HRESULT STDMETHODCALLTYPE
ISecurityObjectTypeInfo_fnQueryInterface(struct ISecurityObjectTypeInfo *this,
                                         REFIID iid,
                                         PVOID *pvObject)
{
    if (IsEqualGUID(iid, &IID_IUnknown))
    {
        *pvObject = (PVOID)this;
        ISecurityObjectTypeInfo_fnAddRef(this);
        return S_OK;
    }

    return CRegKeySecurity_fnQueryInterface(impl_from_ISecurityObjectTypeInfo(this),
                                            iid,
                                            pvObject);
}

static ULONG STDMETHODCALLTYPE
ISecurityObjectTypeInfo_fnAddRef(struct ISecurityObjectTypeInfo *this)
{
    return CRegKeySecurity_fnAddRef(impl_from_ISecurityObjectTypeInfo(this));
}

static ULONG STDMETHODCALLTYPE
ISecurityObjectTypeInfo_fnRelease(struct ISecurityObjectTypeInfo *this)
{
    return CRegKeySecurity_fnRelease(impl_from_ISecurityObjectTypeInfo(this));
}

static HRESULT STDMETHODCALLTYPE
ISecurityObjectTypeInfo_fnGetInheritSource(struct ISecurityObjectTypeInfo *this,
                                           SECURITY_INFORMATION si,
                                           PACL pACL,
                                           PINHERITED_FROM* ppInheritArray)
{
    PCRegKeySecurity obj = impl_from_ISecurityObjectTypeInfo(this);
    PINHERITED_FROM pif, pif2;
    SIZE_T pifSize;
    DWORD ErrorCode, i;
    LPWSTR lpBuf;

    pifSize = pACL->AceCount * sizeof(INHERITED_FROM);
    pif = (PINHERITED_FROM)HeapAlloc(GetProcessHeap(), 0, pifSize);
    if (pif == NULL)
        return E_OUTOFMEMORY;

    ErrorCode = GetInheritanceSourceW(obj->szRegKey,
                                      SE_REGISTRY_KEY,
                                      si,
                                      (obj->ObjectInfo.dwFlags & SI_CONTAINER) != 0,
                                      NULL,
                                      0,
                                      pACL,
                                      NULL,
                                      &RegAccessMasks,
                                      pif);
    if (ErrorCode == ERROR_SUCCESS)
    {
        /* Calculate the size of the buffer to return */
        for (i = 0; i < pACL->AceCount; i++)
        {
            if (pif[i].AncestorName != NULL)
                pifSize += (wcslen(pif[i].AncestorName) + 1) * sizeof(WCHAR);
        }

        /* Allocate enough space for the array and the strings */
        pif2 = (PINHERITED_FROM)LocalAlloc(LMEM_FIXED, pifSize);
        if (pif2 == NULL)
        {
            ErrorCode = GetLastError();
            goto Cleanup;
        }

        /* copy the array and strings to the buffer */
        lpBuf = (LPWSTR)((ULONG_PTR)pif2 + (pACL->AceCount * sizeof(INHERITED_FROM)));
        for (i = 0; i < pACL->AceCount; i++)
        {
            pif2[i].GenerationGap = pif[i].GenerationGap;
            if (pif[i].AncestorName != NULL)
            {
                pif2[i].AncestorName = lpBuf;
                wcscpy(lpBuf,
                        pif[i].AncestorName);
                lpBuf += wcslen(pif[i].AncestorName) + 1;
            }
            else
                pif2[i].AncestorName = NULL;
        }

        /* return the newly allocated array */
        *ppInheritArray = pif2;
    }

Cleanup:
    FreeInheritedFromArray(pif, pACL->AceCount, NULL);
    HeapFree(GetProcessHeap(), 0, pif);

    return HRESULT_FROM_WIN32(ErrorCode);
}

/******************************************************************************
   Implementation of the CRegKeySecurity constructor
 ******************************************************************************/

static PCRegKeySecurity
CRegKeySecurity_fnConstructor(LPWSTR lpRegKey,
                              HKEY hRootKey,
                              SI_OBJECT_INFO *ObjectInfo,
                              BOOL *Btn)
{
    PCRegKeySecurity obj;

    obj = (PCRegKeySecurity)HeapAlloc(GetProcessHeap(),
                                      HEAP_ZERO_MEMORY,
                                      FIELD_OFFSET(CRegKeySecurity,
                                                   szRegKey[wcslen(lpRegKey) + 1]));
    if (obj != NULL)
    {
        obj->ref = 1;
        obj->lpISecurityInformationVtbl = &vtblISecurityInformation;
#if REGEDIT_IMPLEMENT_ISECURITYINFORMATION2
        obj->lpISecurityInformation2Vtbl = &vtblISecurityInformation2;
#endif
        obj->lpIEffectivePermissionVtbl = &vtblIEffectivePermission;
        obj->lpISecurityObjectTypeInfoVtbl =  &vtblISecurityObjectTypeInfo;
        obj->ObjectInfo = *ObjectInfo;
        obj->Btn = Btn;
        obj->hRootKey = hRootKey;
        StringCbCopyW(obj->szRegKey, sizeof(obj->szRegKey), lpRegKey);
    }
    else
        SetLastError(ERROR_NOT_ENOUGH_MEMORY);

    return obj;
}

/******************************************************************************/
/******************************************************************************/
/******************************************************************************/

typedef struct _CHANGE_CONTEXT
{
  HKEY hKey;
  LPWSTR KeyString;
} CHANGE_CONTEXT, *PCHANGE_CONTEXT;

typedef BOOL (WINAPI *PEDITSECURITY)(HWND hwndOwner,
                                     struct ISecurityInformation *psi);

static PEDITSECURITY pfnEditSecurity;
static HMODULE hAclUiDll;

BOOL
InitializeAclUiDll(VOID)
{
    if (!(hAclUiDll = LoadLibraryW(L"aclui.dll")))
    {
        return FALSE;
    }

    if (!(pfnEditSecurity = (PEDITSECURITY)GetProcAddress(hAclUiDll,
                                                         "EditSecurity")))
    {
        FreeLibrary(hAclUiDll);
        hAclUiDll = NULL;
        return FALSE;
    }

    return TRUE;
}

VOID
UnloadAclUiDll(VOID)
{
    if (hAclUiDll != NULL)
    {
        FreeLibrary(hAclUiDll);
    }
}

BOOL
RegKeyEditPermissions(HWND hWndOwner,
                      HKEY hKey,
                      LPCWSTR lpMachine,
                      LPCWSTR lpKeyName)
{
    BOOL Result = FALSE;
    LPCWSTR lphKey = NULL;
    LPWSTR lpKeyPath = NULL;
    PCRegKeySecurity RegKeySecurity;
    SI_OBJECT_INFO ObjectInfo;
    size_t lnMachine = 0, lnKeyName = 0;

    if (pfnEditSecurity == NULL)
    {
        return FALSE;
    }

    if (lpMachine != NULL)
        lnMachine = wcslen(lpMachine);
    if (lpKeyName != NULL)
        lnKeyName = wcslen(lpKeyName);

    /* build registry path */
    if (lpMachine != NULL &&
        (lpMachine[0] == L'\0' ||
         (lpMachine[0] == L'.' && lpMachine[1] == L'.')))
    {
        lnMachine = 0;
    }

    if (hKey == HKEY_CLASSES_ROOT)
        lphKey = L"CLASSES_ROOT";
    else if (hKey == HKEY_CURRENT_USER)
        lphKey = L"CURRENT_USER";
    else if (hKey == HKEY_LOCAL_MACHINE)
        lphKey = L"MACHINE";
    else if (hKey == HKEY_USERS)
        lphKey = L"USERS";
    else if (hKey == HKEY_CURRENT_CONFIG)
        lphKey = L"CONFIG";
    else
    goto Cleanup;

    lpKeyPath = HeapAlloc(GetProcessHeap(),
                          0,
                          (2 + lnMachine + 1 + wcslen(lphKey) + 1 + lnKeyName) * sizeof(WCHAR));
    if (lpKeyPath == NULL)
    {
        SetLastError(ERROR_NOT_ENOUGH_MEMORY);
        goto Cleanup;
    }
    lpKeyPath[0] = L'\0';

    if (lnMachine != 0)
    {
        wcscat(lpKeyPath, L"\\\\");
        wcscat(lpKeyPath, lpMachine);
        wcscat(lpKeyPath, L"\\");
    }

    wcscat(lpKeyPath, lphKey);
    if (lpKeyName != NULL && lpKeyName[0] != L'\0')
    {
        if (lpKeyName[0] != L'\\')
            wcscat(lpKeyPath, L"\\");

        wcscat(lpKeyPath, lpKeyName);
    }

    ObjectInfo.dwFlags = SI_EDIT_ALL  | SI_ADVANCED | SI_CONTAINER | SI_EDIT_EFFECTIVE | SI_EDIT_PERMS |
                             SI_OWNER_RECURSE | SI_RESET_DACL_TREE | SI_RESET_SACL_TREE;
    ObjectInfo.hInstance = hInst;
    ObjectInfo.pszServerName = (LPWSTR)lpMachine;
    ObjectInfo.pszObjectName = (LPWSTR)lpKeyName; /* FIXME */
    ObjectInfo.pszPageTitle = (LPWSTR)lpKeyName; /* FIXME */

    if (!(RegKeySecurity = CRegKeySecurity_fnConstructor(lpKeyPath,
                                                         hKey,
                                                         &ObjectInfo,
                                                         &Result)))
    {
        goto Cleanup;
    }

    /* display the security editor dialog */
    pfnEditSecurity(hWndOwner, impl_to_interface(RegKeySecurity, ISecurityInformation));

    /* dereference the interface, it should be destroyed here */
    CRegKeySecurity_fnRelease(RegKeySecurity);

Cleanup:
    if (lpKeyPath != NULL)
        HeapFree(GetProcessHeap(), 0, lpKeyPath);

    return Result;
}

/* EOF */
