From cbc09b6cd6d0d0b8bf095a88d4d8333616637f71 Mon Sep 17 00:00:00 2001 From: Sean Hall Date: Mon, 4 Feb 2019 20:05:54 -0600 Subject: Import code from old v4 repo --- src/ca/cpappexec.cpp | 344 +++++++ src/ca/cpappexec.h | 12 + src/ca/cpapproleexec.cpp | 720 +++++++++++++++ src/ca/cpapproleexec.h | 20 + src/ca/cpapprolesched.cpp | 843 +++++++++++++++++ src/ca/cpapprolesched.h | 112 +++ src/ca/cpappsched.cpp | 752 ++++++++++++++++ src/ca/cpappsched.h | 83 ++ src/ca/cpasmexec.cpp | 1888 +++++++++++++++++++++++++++++++++++++++ src/ca/cpasmexec.h | 20 + src/ca/cpasmsched.cpp | 2135 ++++++++++++++++++++++++++++++++++++++++++++ src/ca/cpasmsched.h | 168 ++++ src/ca/cpcost.h | 30 + src/ca/cpexec.cpp | 704 +++++++++++++++ src/ca/cpexec.def | 11 + src/ca/cppartexec.cpp | 690 ++++++++++++++ src/ca/cppartexec.h | 20 + src/ca/cppartroleexec.cpp | 397 ++++++++ src/ca/cppartroleexec.h | 12 + src/ca/cppartrolesched.cpp | 421 +++++++++ src/ca/cppartrolesched.h | 76 ++ src/ca/cppartsched.cpp | 912 +++++++++++++++++++ src/ca/cppartsched.h | 125 +++ src/ca/cpsched.cpp | 590 ++++++++++++ src/ca/cpsubsexec.cpp | 411 +++++++++ src/ca/cpsubsexec.h | 12 + src/ca/cpsubssched.cpp | 606 +++++++++++++ src/ca/cpsubssched.h | 62 ++ src/ca/cputilexec.cpp | 1881 ++++++++++++++++++++++++++++++++++++++ src/ca/cputilexec.h | 193 ++++ src/ca/cputilsched.cpp | 885 ++++++++++++++++++ src/ca/cputilsched.h | 144 +++ 32 files changed, 15279 insertions(+) create mode 100644 src/ca/cpappexec.cpp create mode 100644 src/ca/cpappexec.h create mode 100644 src/ca/cpapproleexec.cpp create mode 100644 src/ca/cpapproleexec.h create mode 100644 src/ca/cpapprolesched.cpp create mode 100644 src/ca/cpapprolesched.h create mode 100644 src/ca/cpappsched.cpp create mode 100644 src/ca/cpappsched.h create mode 100644 src/ca/cpasmexec.cpp create mode 100644 src/ca/cpasmexec.h create mode 100644 src/ca/cpasmsched.cpp create mode 100644 src/ca/cpasmsched.h create mode 100644 src/ca/cpcost.h create mode 100644 src/ca/cpexec.cpp create mode 100644 src/ca/cpexec.def create mode 100644 src/ca/cppartexec.cpp create mode 100644 src/ca/cppartexec.h create mode 100644 src/ca/cppartroleexec.cpp create mode 100644 src/ca/cppartroleexec.h create mode 100644 src/ca/cppartrolesched.cpp create mode 100644 src/ca/cppartrolesched.h create mode 100644 src/ca/cppartsched.cpp create mode 100644 src/ca/cppartsched.h create mode 100644 src/ca/cpsched.cpp create mode 100644 src/ca/cpsubsexec.cpp create mode 100644 src/ca/cpsubsexec.h create mode 100644 src/ca/cpsubssched.cpp create mode 100644 src/ca/cpsubssched.h create mode 100644 src/ca/cputilexec.cpp create mode 100644 src/ca/cputilexec.h create mode 100644 src/ca/cputilsched.cpp create mode 100644 src/ca/cputilsched.h (limited to 'src/ca') diff --git a/src/ca/cpappexec.cpp b/src/ca/cpappexec.cpp new file mode 100644 index 00000000..43d6cd6d --- /dev/null +++ b/src/ca/cpappexec.cpp @@ -0,0 +1,344 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + + +// private structs + +struct CPI_APPLICATION_ATTRIBUTES +{ + int iActionType; + int iActionCost; + LPWSTR pwzKey; + LPWSTR pwzID; + LPWSTR pwzName; + LPWSTR pwzPartID; + CPI_PROPERTY* pPropList; +}; + + +// prototypes for private helper functions + +static HRESULT ReadApplicationAttributes( + LPWSTR* ppwzData, + CPI_APPLICATION_ATTRIBUTES* pAttrs + ); +static void FreeApplicationAttributes( + CPI_APPLICATION_ATTRIBUTES* pAttrs + ); +static HRESULT CreateApplication( + CPI_APPLICATION_ATTRIBUTES* pAttrs + ); +static HRESULT RemoveApplication( + CPI_APPLICATION_ATTRIBUTES* pAttrs + ); + + +// function definitions + +HRESULT CpiConfigureApplications( + LPWSTR* ppwzData, + HANDLE hRollbackFile + ) +{ + HRESULT hr = S_OK; + + CPI_APPLICATION_ATTRIBUTES attrs; + ::ZeroMemory(&attrs, sizeof(attrs)); + + // read action text + hr = CpiActionStartMessage(ppwzData, FALSE); + ExitOnFailure(hr, "Failed to send action start message"); + + // get count + int iCnt = 0; + hr = WcaReadIntegerFromCaData(ppwzData, &iCnt); + ExitOnFailure(hr, "Failed to read count"); + + // write count to rollback file + hr = CpiWriteIntegerToRollbackFile(hRollbackFile, iCnt); + ExitOnFailure(hr, "Failed to write count to rollback file"); + + for (int i = 0; i < iCnt; i++) + { + // read attributes from CustomActionData + hr = ReadApplicationAttributes(ppwzData, &attrs); + ExitOnFailure(hr, "Failed to read attributes"); + + // progress message + hr = CpiActionDataMessage(1, attrs.pwzName); + ExitOnFailure(hr, "Failed to send progress messages"); + + if (S_FALSE == hr) + ExitFunction(); + + // write key to rollback file + hr = CpiWriteKeyToRollbackFile(hRollbackFile, attrs.pwzKey); + ExitOnFailure(hr, "Failed to write key to rollback file"); + + // action + switch (attrs.iActionType) + { + case atCreate: + hr = CreateApplication(&attrs); + ExitOnFailure(hr, "Failed to create application, key: %S", attrs.pwzKey); + break; + case atRemove: + hr = RemoveApplication(&attrs); + ExitOnFailure(hr, "Failed to remove application, key: %S", attrs.pwzKey); + break; + } + + // write completion status to rollback file + hr = CpiWriteIntegerToRollbackFile(hRollbackFile, 1); + ExitOnFailure(hr, "Failed to write completion status to rollback file"); + + // progress + hr = WcaProgressMessage(attrs.iActionCost, FALSE); + ExitOnFailure(hr, "Failed to update progress"); + } + + hr = S_OK; + +LExit: + // clean up + FreeApplicationAttributes(&attrs); + + return hr; +} + +HRESULT CpiRollbackConfigureApplications( + LPWSTR* ppwzData, + CPI_ROLLBACK_DATA* pRollbackDataList + ) +{ + HRESULT hr = S_OK; + + int iRollbackStatus; + + CPI_APPLICATION_ATTRIBUTES attrs; + ::ZeroMemory(&attrs, sizeof(attrs)); + + // read action text + hr = CpiActionStartMessage(ppwzData, NULL == pRollbackDataList); + ExitOnFailure(hr, "Failed to send action start message"); + + // get count + int iCnt = 0; + hr = WcaReadIntegerFromCaData(ppwzData, &iCnt); + ExitOnFailure(hr, "Failed to read count"); + + for (int i = 0; i < iCnt; i++) + { + // read attributes from CustomActionData + hr = ReadApplicationAttributes(ppwzData, &attrs); + ExitOnFailure(hr, "Failed to read attributes"); + + // rollback status + hr = CpiFindRollbackStatus(pRollbackDataList, attrs.pwzKey, &iRollbackStatus); + + if (S_FALSE == hr) + continue; // not found, nothing to rollback + + // progress message + hr = CpiActionDataMessage(1, attrs.pwzName); + ExitOnFailure(hr, "Failed to send progress messages"); + + if (S_FALSE == hr) + ExitFunction(); + + // action + switch (attrs.iActionType) + { + case atCreate: + hr = CreateApplication(&attrs); + if (FAILED(hr)) + WcaLog(LOGMSG_STANDARD, "Failed to create application, hr: 0x%x, key: %S", hr, attrs.pwzKey); + break; + case atRemove: + hr = RemoveApplication(&attrs); + if (FAILED(hr)) + WcaLog(LOGMSG_STANDARD, "Failed to remove application, hr: 0x%x, key: %S", hr, attrs.pwzKey); + break; + } + + // check rollback status + if (0 == iRollbackStatus) + continue; // operation did not complete, skip progress + + // progress + hr = WcaProgressMessage(attrs.iActionCost, FALSE); + ExitOnFailure(hr, "Failed to update progress"); + } + + hr = S_OK; + +LExit: + // clean up + FreeApplicationAttributes(&attrs); + + return hr; +} + + +// helper function definitions + +static HRESULT ReadApplicationAttributes( + LPWSTR* ppwzData, + CPI_APPLICATION_ATTRIBUTES* pAttrs + ) +{ + HRESULT hr = S_OK; + + hr = WcaReadIntegerFromCaData(ppwzData, &pAttrs->iActionType); + ExitOnFailure(hr, "Failed to read action type"); + hr = WcaReadIntegerFromCaData(ppwzData, &pAttrs->iActionCost); + ExitOnFailure(hr, "Failed to read action cost"); + hr = WcaReadStringFromCaData(ppwzData, &pAttrs->pwzKey); + ExitOnFailure(hr, "Failed to read key"); + hr = WcaReadStringFromCaData(ppwzData, &pAttrs->pwzID); + ExitOnFailure(hr, "Failed to read id"); + hr = WcaReadStringFromCaData(ppwzData, &pAttrs->pwzName); + ExitOnFailure(hr, "Failed to read name"); + hr = WcaReadStringFromCaData(ppwzData, &pAttrs->pwzPartID); + ExitOnFailure(hr, "Failed to read partition id"); + hr = CpiReadPropertyList(ppwzData, &pAttrs->pPropList); + ExitOnFailure(hr, "Failed to read properties"); + + hr = S_OK; + +LExit: + return hr; +} + +static void FreeApplicationAttributes( + CPI_APPLICATION_ATTRIBUTES* pAttrs + ) +{ + ReleaseStr(pAttrs->pwzKey); + ReleaseStr(pAttrs->pwzID); + ReleaseStr(pAttrs->pwzName); + ReleaseStr(pAttrs->pwzPartID); + + if (pAttrs->pPropList) + CpiFreePropertyList(pAttrs->pPropList); +} + +static HRESULT CreateApplication( + CPI_APPLICATION_ATTRIBUTES* pAttrs + ) +{ + HRESULT hr = S_OK; + + ICatalogCollection* piAppColl = NULL; + ICatalogObject* piAppObj = NULL; + + long lChanges = 0; + + // log + WcaLog(LOGMSG_VERBOSE, "Creating application, key: %S", pAttrs->pwzKey); + + // get applications collection + hr = CpiGetApplicationsCollection(pAttrs->pwzPartID, &piAppColl); + if (S_FALSE == hr) + hr = HRESULT_FROM_WIN32(ERROR_NOT_FOUND); + ExitOnFailure(hr, "Failed to get applications collection"); + + // check if application exists + hr = CpiFindCollectionObjectByStringKey(piAppColl, pAttrs->pwzID, &piAppObj); + ExitOnFailure(hr, "Failed to find application"); + + if (S_FALSE == hr) + { + // create application + hr = CpiAddCollectionObject(piAppColl, &piAppObj); + ExitOnFailure(hr, "Failed to add application to collection"); + + hr = CpiPutCollectionObjectValue(piAppObj, L"ID", pAttrs->pwzID); + ExitOnFailure(hr, "Failed to set application id property"); + + hr = CpiPutCollectionObjectValue(piAppObj, L"Name", pAttrs->pwzName); + ExitOnFailure(hr, "Failed to set application name property"); + + // save changes + hr = piAppColl->SaveChanges(&lChanges); + if (COMADMIN_E_OBJECTERRORS == hr) + CpiLogCatalogErrorInfo(); + ExitOnFailure(hr, "Failed to add application"); + } + + // properties + hr = CpiPutCollectionObjectValues(piAppObj, pAttrs->pPropList); + ExitOnFailure(hr, "Failed to write properties"); + + // save changes + hr = piAppColl->SaveChanges(&lChanges); + if (COMADMIN_E_OBJECTERRORS == hr) + CpiLogCatalogErrorInfo(); + ExitOnFailure(hr, "Failed to save changes"); + + // log + WcaLog(LOGMSG_VERBOSE, "%d changes saved to catalog, key: %S", lChanges, pAttrs->pwzKey); + + hr = S_OK; + +LExit: + // clean up + ReleaseObject(piAppColl); + ReleaseObject(piAppObj); + + return hr; +} + +static HRESULT RemoveApplication( + CPI_APPLICATION_ATTRIBUTES* pAttrs + ) +{ + HRESULT hr = S_OK; + + ICatalogCollection* piAppColl = NULL; + + long lChanges = 0; + + // log + WcaLog(LOGMSG_VERBOSE, "Removing application, key: %S", pAttrs->pwzKey); + + // get applications collection + hr = CpiGetApplicationsCollection(pAttrs->pwzPartID, &piAppColl); + ExitOnFailure(hr, "Failed to get applications collection"); + + if (S_FALSE == hr) + { + // applications collection not found + WcaLog(LOGMSG_VERBOSE, "Unable to retrieve applications collection, nothing to delete, key: %S", pAttrs->pwzKey); + ExitFunction1(hr = S_OK); + } + + // remove + hr = CpiRemoveCollectionObject(piAppColl, pAttrs->pwzID, NULL, TRUE); + ExitOnFailure(hr, "Failed to remove application"); + + if (S_FALSE == hr) + { + // application not found + WcaLog(LOGMSG_VERBOSE, "Application not found, nothing to delete, key: %S", pAttrs->pwzKey); + ExitFunction1(hr = S_OK); + } + + // save changes + hr = piAppColl->SaveChanges(&lChanges); + if (COMADMIN_E_OBJECTERRORS == hr) + CpiLogCatalogErrorInfo(); + ExitOnFailure(hr, "Failed to save changes"); + + // log + WcaLog(LOGMSG_VERBOSE, "%d changes saved to catalog, key: %S", lChanges, pAttrs->pwzKey); + + hr = S_OK; + +LExit: + // clean up + ReleaseObject(piAppColl); + + return hr; +} diff --git a/src/ca/cpappexec.h b/src/ca/cpappexec.h new file mode 100644 index 00000000..5003b046 --- /dev/null +++ b/src/ca/cpappexec.h @@ -0,0 +1,12 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + + +HRESULT CpiConfigureApplications( + LPWSTR* ppwzData, + HANDLE hRollbackFile + ); +HRESULT CpiRollbackConfigureApplications( + LPWSTR* ppwzData, + CPI_ROLLBACK_DATA* pRollbackDataList + ); diff --git a/src/ca/cpapproleexec.cpp b/src/ca/cpapproleexec.cpp new file mode 100644 index 00000000..e3b71e93 --- /dev/null +++ b/src/ca/cpapproleexec.cpp @@ -0,0 +1,720 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + + +// private structs + +struct CPI_APPLICATION_ROLE_ATTRIBUTES +{ + int iActionType; + int iActionCost; + LPWSTR pwzKey; + LPWSTR pwzName; + LPWSTR pwzAppID; + LPWSTR pwzPartID; + CPI_PROPERTY* pPropList; +}; + +struct CPI_USER_IN_APPLICATION_ROLE_ATTRIBUTES +{ + int iActionType; + int iActionCost; + LPWSTR pwzKey; + LPWSTR pwzRoleName; + LPWSTR pwzAccount; + LPWSTR pwzAppID; + LPWSTR pwzPartID; +}; + + +// prototypes for private helper functions + +static HRESULT ReadApplicationRoleAttributes( + LPWSTR* ppwzData, + CPI_APPLICATION_ROLE_ATTRIBUTES* pAttrs + ); +static void FreeApplicationRoleAttributes( + CPI_APPLICATION_ROLE_ATTRIBUTES* pAttrs + ); +static HRESULT CreateApplicationRole( + CPI_APPLICATION_ROLE_ATTRIBUTES* pAttrs + ); +static HRESULT RemoveApplicationRole( + CPI_APPLICATION_ROLE_ATTRIBUTES* pAttrs + ); +static HRESULT ReadUsersInApplicationRoleAttributes( + LPWSTR* ppwzData, + CPI_USER_IN_APPLICATION_ROLE_ATTRIBUTES* pAttrs + ); +static void FreeUsersInApplicationRoleAttributes( + CPI_USER_IN_APPLICATION_ROLE_ATTRIBUTES* pAttrs + ); +static HRESULT CreateUsersInApplicationRole( + CPI_USER_IN_APPLICATION_ROLE_ATTRIBUTES* pAttrs + ); +static HRESULT RemoveUsersInApplicationRole( + CPI_USER_IN_APPLICATION_ROLE_ATTRIBUTES* pAttrs + ); + + +// function definitions + +HRESULT CpiConfigureApplicationRoles( + LPWSTR* ppwzData, + HANDLE hRollbackFile + ) +{ + HRESULT hr = S_OK; + + CPI_APPLICATION_ROLE_ATTRIBUTES attrs; + ::ZeroMemory(&attrs, sizeof(attrs)); + + // read action text + hr = CpiActionStartMessage(ppwzData, FALSE); + ExitOnFailure(hr, "Failed to send action start message"); + + // ger count + int iCnt = 0; + hr = WcaReadIntegerFromCaData(ppwzData, &iCnt); + ExitOnFailure(hr, "Failed to read count"); + + // write count to rollback file + hr = CpiWriteIntegerToRollbackFile(hRollbackFile, iCnt); + ExitOnFailure(hr, "Failed to write count to rollback file"); + + for (int i = 0; i < iCnt; i++) + { + // read attributes from CustomActionData + hr = ReadApplicationRoleAttributes(ppwzData, &attrs); + ExitOnFailure(hr, "Failed to read attributes"); + + // progress message + hr = CpiActionDataMessage(1, attrs.pwzName); + ExitOnFailure(hr, "Failed to send progress messages"); + + if (S_FALSE == hr) + ExitFunction(); + + // write key to rollback file + hr = CpiWriteKeyToRollbackFile(hRollbackFile, attrs.pwzKey); + ExitOnFailure(hr, "Failed to write key to rollback file"); + + // action + switch (attrs.iActionType) + { + case atCreate: + hr = CreateApplicationRole(&attrs); + ExitOnFailure(hr, "Failed to create application role, key: %S", attrs.pwzKey); + break; + case atRemove: + hr = RemoveApplicationRole(&attrs); + ExitOnFailure(hr, "Failed to remove application role, key: %S", attrs.pwzKey); + break; + } + + // write completion status to rollback file + hr = CpiWriteIntegerToRollbackFile(hRollbackFile, 1); + ExitOnFailure(hr, "Failed to write completion status to rollback file"); + + // progress + hr = WcaProgressMessage(attrs.iActionCost, FALSE); + ExitOnFailure(hr, "Failed to update progress"); + } + + hr = S_OK; + +LExit: + // clean up + FreeApplicationRoleAttributes(&attrs); + + return hr; +} + +HRESULT CpiRollbackConfigureApplicationRoles( + LPWSTR* ppwzData, + CPI_ROLLBACK_DATA* pRollbackDataList + ) +{ + HRESULT hr = S_OK; + + int iRollbackStatus; + + CPI_APPLICATION_ROLE_ATTRIBUTES attrs; + ::ZeroMemory(&attrs, sizeof(attrs)); + + // read action text + hr = CpiActionStartMessage(ppwzData, NULL == pRollbackDataList); + ExitOnFailure(hr, "Failed to send action start message"); + + // get count + int iCnt = 0; + hr = WcaReadIntegerFromCaData(ppwzData, &iCnt); + ExitOnFailure(hr, "Failed to read count"); + + for (int i = 0; i < iCnt; i++) + { + // read attributes from CustomActionData + hr = ReadApplicationRoleAttributes(ppwzData, &attrs); + ExitOnFailure(hr, "Failed to read attributes"); + + // rollback status + hr = CpiFindRollbackStatus(pRollbackDataList, attrs.pwzKey, &iRollbackStatus); + + if (S_FALSE == hr) + continue; // not found, nothing to rollback + + // progress message + hr = CpiActionDataMessage(1, attrs.pwzName); + ExitOnFailure(hr, "Failed to send progress messages"); + + if (S_FALSE == hr) + ExitFunction(); + + // action + switch (attrs.iActionType) + { + case atCreate: + hr = CreateApplicationRole(&attrs); + if (FAILED(hr)) + WcaLog(LOGMSG_STANDARD, "Failed to create application role, hr: 0x%x, key: %S", hr, attrs.pwzKey); + break; + case atRemove: + hr = RemoveApplicationRole(&attrs); + if (FAILED(hr)) + WcaLog(LOGMSG_STANDARD, "Failed to remove application role, hr: 0x%x, key: %S", hr, attrs.pwzKey); + break; + } + + // check rollback status + if (0 == iRollbackStatus) + continue; // operation did not complete, skip progress + + // progress + hr = WcaProgressMessage(attrs.iActionCost, FALSE); + ExitOnFailure(hr, "Failed to update progress"); + } + + hr = S_OK; + +LExit: + // clean up + FreeApplicationRoleAttributes(&attrs); + + return hr; +} + +HRESULT CpiConfigureUsersInApplicationRoles( + LPWSTR* ppwzData, + HANDLE hRollbackFile + ) +{ + HRESULT hr = S_OK; + + CPI_USER_IN_APPLICATION_ROLE_ATTRIBUTES attrs; + ::ZeroMemory(&attrs, sizeof(attrs)); + + // read action text + hr = CpiActionStartMessage(ppwzData, FALSE); + ExitOnFailure(hr, "Failed to send action start message"); + + // ger count + int iCnt = 0; + hr = WcaReadIntegerFromCaData(ppwzData, &iCnt); + ExitOnFailure(hr, "Failed to read count"); + + // write count to rollback file + hr = CpiWriteIntegerToRollbackFile(hRollbackFile, iCnt); + ExitOnFailure(hr, "Failed to write count to rollback file"); + + for (int i = 0; i < iCnt; i++) + { + // read attributes from CustomActionData + hr = ReadUsersInApplicationRoleAttributes(ppwzData, &attrs); + ExitOnFailure(hr, "Failed to read attributes"); + + // progress message + hr = CpiActionDataMessage(1, attrs.pwzRoleName); + ExitOnFailure(hr, "Failed to send progress messages"); + + if (S_FALSE == hr) + ExitFunction(); + + // write key to rollback file + hr = CpiWriteKeyToRollbackFile(hRollbackFile, attrs.pwzKey); + ExitOnFailure(hr, "Failed to write key to rollback file"); + + // action + switch (attrs.iActionType) + { + case atCreate: + hr = CreateUsersInApplicationRole(&attrs); + ExitOnFailure(hr, "Failed to create user in application role, key: %S", attrs.pwzKey); + break; + case atRemove: + hr = RemoveUsersInApplicationRole(&attrs); + ExitOnFailure(hr, "Failed to remove user from application role, key: %S", attrs.pwzKey); + break; + } + + // write completion status to rollback file + hr = CpiWriteIntegerToRollbackFile(hRollbackFile, 1); + ExitOnFailure(hr, "Failed to write completion status to rollback file"); + + // progress + hr = WcaProgressMessage(attrs.iActionCost, FALSE); + ExitOnFailure(hr, "Failed to update progress"); + } + + hr = S_OK; + +LExit: + // clean up + FreeUsersInApplicationRoleAttributes(&attrs); + + return hr; +} + +HRESULT CpiRollbackConfigureUsersInApplicationRoles( + LPWSTR* ppwzData, + CPI_ROLLBACK_DATA* pRollbackDataList + ) +{ + HRESULT hr = S_OK; + + int iRollbackStatus; + + CPI_USER_IN_APPLICATION_ROLE_ATTRIBUTES attrs; + ::ZeroMemory(&attrs, sizeof(attrs)); + + // read action text + hr = CpiActionStartMessage(ppwzData, NULL == pRollbackDataList); + ExitOnFailure(hr, "Failed to send action start message"); + + // get count + int iCnt = 0; + hr = WcaReadIntegerFromCaData(ppwzData, &iCnt); + ExitOnFailure(hr, "Failed to read count"); + + for (int i = 0; i < iCnt; i++) + { + // read attributes from CustomActionData + hr = ReadUsersInApplicationRoleAttributes(ppwzData, &attrs); + ExitOnFailure(hr, "Failed to read attributes"); + + // rollback status + hr = CpiFindRollbackStatus(pRollbackDataList, attrs.pwzKey, &iRollbackStatus); + + if (S_FALSE == hr) + continue; // not found, nothing to rollback + + // progress message + hr = CpiActionDataMessage(1, attrs.pwzRoleName); + ExitOnFailure(hr, "Failed to send progress messages"); + + if (S_FALSE == hr) + ExitFunction(); + + // action + switch (attrs.iActionType) + { + case atCreate: + hr = CreateUsersInApplicationRole(&attrs); + if (FAILED(hr)) + WcaLog(LOGMSG_STANDARD, "Failed to add user to application role, hr: 0x%x, key: %S", hr, attrs.pwzKey); + break; + case atRemove: + hr = RemoveUsersInApplicationRole(&attrs); + if (FAILED(hr)) + WcaLog(LOGMSG_STANDARD, "Failed to remove user from application role, hr: 0x%x, key: %S", hr, attrs.pwzKey); + break; + } + + // check rollback status + if (0 == iRollbackStatus) + continue; // operation did not complete, skip progress + + // progress + hr = WcaProgressMessage(attrs.iActionCost, FALSE); + ExitOnFailure(hr, "Failed to update progress"); + } + + hr = S_OK; + +LExit: + // clean up + FreeUsersInApplicationRoleAttributes(&attrs); + + return hr; +} + + +// helper function definitions + +static HRESULT ReadApplicationRoleAttributes( + LPWSTR* ppwzData, + CPI_APPLICATION_ROLE_ATTRIBUTES* pAttrs + ) +{ + HRESULT hr = S_OK; + + hr = WcaReadIntegerFromCaData(ppwzData, &pAttrs->iActionType); + ExitOnFailure(hr, "Failed to read action type"); + hr = WcaReadIntegerFromCaData(ppwzData, &pAttrs->iActionCost); + ExitOnFailure(hr, "Failed to read action cost"); + hr = WcaReadStringFromCaData(ppwzData, &pAttrs->pwzKey); + ExitOnFailure(hr, "Failed to read key"); + hr = WcaReadStringFromCaData(ppwzData, &pAttrs->pwzName); + ExitOnFailure(hr, "Failed to read name"); + hr = WcaReadStringFromCaData(ppwzData, &pAttrs->pwzAppID); + ExitOnFailure(hr, "Failed to read application id"); + hr = WcaReadStringFromCaData(ppwzData, &pAttrs->pwzPartID); + ExitOnFailure(hr, "Failed to read partition id"); + hr = CpiReadPropertyList(ppwzData, &pAttrs->pPropList); + ExitOnFailure(hr, "Failed to read properties"); + + hr = S_OK; + +LExit: + return hr; +} + +static void FreeApplicationRoleAttributes( + CPI_APPLICATION_ROLE_ATTRIBUTES* pAttrs + ) +{ + ReleaseStr(pAttrs->pwzKey); + ReleaseStr(pAttrs->pwzName); + ReleaseStr(pAttrs->pwzAppID); + ReleaseStr(pAttrs->pwzPartID); + + if (pAttrs->pPropList) + CpiFreePropertyList(pAttrs->pPropList); +} + +static HRESULT CreateApplicationRole( + CPI_APPLICATION_ROLE_ATTRIBUTES* pAttrs + ) +{ + HRESULT hr = S_OK; + + ICatalogCollection* piRolesColl = NULL; + ICatalogObject* piRoleObj = NULL; + + long lChanges = 0; + + // log + WcaLog(LOGMSG_VERBOSE, "Creating application role, key: %S", pAttrs->pwzKey); + + // get roles collection + hr = CpiGetRolesCollection(pAttrs->pwzPartID, pAttrs->pwzAppID, &piRolesColl); + if (S_FALSE == hr) + hr = HRESULT_FROM_WIN32(ERROR_NOT_FOUND); + ExitOnFailure(hr, "Failed to get roles collection"); + + // check if role exists + hr = CpiFindCollectionObjectByName(piRolesColl, pAttrs->pwzName, &piRoleObj); + ExitOnFailure(hr, "Failed to find role"); + + if (S_FALSE == hr) + { + // create role + hr = CpiAddCollectionObject(piRolesColl, &piRoleObj); + ExitOnFailure(hr, "Failed to add role to collection"); + + hr = CpiPutCollectionObjectValue(piRoleObj, L"Name", pAttrs->pwzName); + ExitOnFailure(hr, "Failed to set role name property"); + } + + // properties + hr = CpiPutCollectionObjectValues(piRoleObj, pAttrs->pPropList); + ExitOnFailure(hr, "Failed to write properties"); + + // save changes + hr = piRolesColl->SaveChanges(&lChanges); + if (COMADMIN_E_OBJECTERRORS == hr) + CpiLogCatalogErrorInfo(); + ExitOnFailure(hr, "Failed to save changes"); + + // log + WcaLog(LOGMSG_VERBOSE, "%d changes saved to catalog, key: %S", lChanges, pAttrs->pwzKey); + + hr = S_OK; + +LExit: + // clean up + ReleaseObject(piRolesColl); + ReleaseObject(piRoleObj); + + return hr; +} + +static HRESULT RemoveApplicationRole( + CPI_APPLICATION_ROLE_ATTRIBUTES* pAttrs + ) +{ + HRESULT hr = S_OK; + + ICatalogCollection* piRolesColl = NULL; + + long lChanges = 0; + + // log + WcaLog(LOGMSG_VERBOSE, "Removing application role, key: %S", pAttrs->pwzKey); + + // get roles collection + hr = CpiGetRolesCollection(pAttrs->pwzPartID, pAttrs->pwzAppID, &piRolesColl); + ExitOnFailure(hr, "Failed to get roles collection"); + + if (S_FALSE == hr) + { + // roles collection not found + WcaLog(LOGMSG_VERBOSE, "Unable to retrieve roles collection, nothing to delete, key: %S", pAttrs->pwzKey); + ExitFunction1(hr = S_OK); + } + + // remove + hr = CpiRemoveCollectionObject(piRolesColl, NULL, pAttrs->pwzName, FALSE); + ExitOnFailure(hr, "Failed to remove role"); + + if (S_FALSE == hr) + { + // role not found + WcaLog(LOGMSG_VERBOSE, "Role not found, nothing to delete, key: %S", pAttrs->pwzKey); + ExitFunction1(hr = S_OK); + } + + // save changes + hr = piRolesColl->SaveChanges(&lChanges); + if (COMADMIN_E_OBJECTERRORS == hr) + CpiLogCatalogErrorInfo(); + ExitOnFailure(hr, "Failed to save changes"); + + // log + WcaLog(LOGMSG_VERBOSE, "%d changes saved to catalog, key: %S", lChanges, pAttrs->pwzKey); + + hr = S_OK; + +LExit: + // clean up + ReleaseObject(piRolesColl); + + return hr; +} + +static HRESULT ReadUsersInApplicationRoleAttributes( + LPWSTR* ppwzData, + CPI_USER_IN_APPLICATION_ROLE_ATTRIBUTES* pAttrs + ) +{ + HRESULT hr = S_OK; + + hr = WcaReadIntegerFromCaData(ppwzData, &pAttrs->iActionType); + ExitOnFailure(hr, "Failed to read action type"); + hr = WcaReadIntegerFromCaData(ppwzData, &pAttrs->iActionCost); + ExitOnFailure(hr, "Failed to read action cost"); + hr = WcaReadStringFromCaData(ppwzData, &pAttrs->pwzKey); + ExitOnFailure(hr, "Failed to read key"); + hr = WcaReadStringFromCaData(ppwzData, &pAttrs->pwzRoleName); + ExitOnFailure(hr, "Failed to read role name"); + hr = WcaReadStringFromCaData(ppwzData, &pAttrs->pwzAccount); + ExitOnFailure(hr, "Failed to read account name"); + hr = WcaReadStringFromCaData(ppwzData, &pAttrs->pwzAppID); + ExitOnFailure(hr, "Failed to read application id"); + hr = WcaReadStringFromCaData(ppwzData, &pAttrs->pwzPartID); + ExitOnFailure(hr, "Failed to read partition id"); + + hr = S_OK; + +LExit: + return hr; +} + +static void FreeUsersInApplicationRoleAttributes( + CPI_USER_IN_APPLICATION_ROLE_ATTRIBUTES* pAttrs + ) +{ + ReleaseStr(pAttrs->pwzKey); + ReleaseStr(pAttrs->pwzRoleName); + ReleaseStr(pAttrs->pwzAccount); + ReleaseStr(pAttrs->pwzAppID); + ReleaseStr(pAttrs->pwzPartID); +} + +static HRESULT CreateUsersInApplicationRole( + CPI_USER_IN_APPLICATION_ROLE_ATTRIBUTES* pAttrs + ) +{ + HRESULT hr = S_OK; + UINT er = ERROR_SUCCESS; + + ICatalogCollection* piUsrInRoleColl = NULL; + ICatalogObject* piUsrInRoleObj = NULL; + + PSID pSid = NULL; + long lChanges = 0; + + // log + WcaLog(LOGMSG_VERBOSE, "Adding user to application role, key: %S", pAttrs->pwzKey); + + // get users in role collection + hr = CpiGetUsersInRoleCollection(pAttrs->pwzPartID, pAttrs->pwzAppID, pAttrs->pwzRoleName, &piUsrInRoleColl); + if (S_FALSE == hr) + hr = HRESULT_FROM_WIN32(ERROR_NOT_FOUND); + ExitOnFailure(hr, "Failed to get users in role collection"); + + // get SID for account + do { + er = ERROR_SUCCESS; + hr = CpiAccountNameToSid(pAttrs->pwzAccount, &pSid); + if (HRESULT_FROM_WIN32(ERROR_NONE_MAPPED) == hr && !::MsiGetMode(WcaGetInstallHandle(), MSIRUNMODE_ROLLBACK)) + { + WcaLog(LOGMSG_STANDARD, "Failed to lookup account name, hr: 0x%x, account: '%S'", hr, pAttrs->pwzAccount); + er = WcaErrorMessage(msierrComPlusFailedLookupNames, hr, INSTALLMESSAGE_ERROR | MB_ABORTRETRYIGNORE, 0); + switch (er) + { + case IDABORT: + ExitFunction(); // exit with error code from CpiAccountNameToSid() + case IDRETRY: + break; + case IDIGNORE: + default: + ExitFunction1(hr = S_OK); + } + } + else + ExitOnFailure(hr, "Failed to get SID for account"); + } while (IDRETRY == er); + + // find any existing entry + hr = CpiFindUserCollectionObject(piUsrInRoleColl, pSid, NULL); + if (HRESULT_FROM_WIN32(ERROR_NONE_MAPPED) == hr || HRESULT_FROM_WIN32(ERROR_SOME_NOT_MAPPED) == hr) + WcaLog(LOGMSG_STANDARD, "Failed to lookup account names, hr: 0x%x", hr); + else + ExitOnFailure(hr, "Failed to find user in application role"); + + if (S_OK == hr) + { + WcaLog(LOGMSG_VERBOSE, "User already assigned to application role, key: %S", pAttrs->pwzKey); + ExitFunction(); // exit with hr = S_OK + } + + // convert SID back to account name + hr = CpiSidToAccountName(pSid, &pAttrs->pwzAccount); + ExitOnFailure(hr, "Failed to convert SID to account name"); + + // add user + hr = CpiAddCollectionObject(piUsrInRoleColl, &piUsrInRoleObj); + ExitOnFailure(hr, "Failed to add user in role to collection"); + + hr = CpiPutCollectionObjectValue(piUsrInRoleObj, L"User", pAttrs->pwzAccount); + ExitOnFailure(hr, "Failed to set role name property"); + + // save changes + hr = piUsrInRoleColl->SaveChanges(&lChanges); + if (COMADMIN_E_OBJECTERRORS == hr) + CpiLogCatalogErrorInfo(); + ExitOnFailure(hr, "Failed to save changes"); + + // log + WcaLog(LOGMSG_VERBOSE, "%d changes saved to catalog, key: %S", lChanges, pAttrs->pwzKey); + + hr = S_OK; + +LExit: + // clean up + ReleaseObject(piUsrInRoleColl); + ReleaseObject(piUsrInRoleObj); + + if (pSid) + ::HeapFree(::GetProcessHeap(), 0, pSid); + + return hr; +} + +static HRESULT RemoveUsersInApplicationRole( + CPI_USER_IN_APPLICATION_ROLE_ATTRIBUTES* pAttrs + ) +{ + HRESULT hr = S_OK; + UINT er = ERROR_SUCCESS; + + ICatalogCollection* piUsrInRoleColl = NULL; + + PSID pSid = NULL; + long lChanges = 0; + + // log + WcaLog(LOGMSG_VERBOSE, "Removing user from application role, key: %S", pAttrs->pwzKey); + + // get users in role collection + hr = CpiGetUsersInRoleCollection(pAttrs->pwzPartID, pAttrs->pwzAppID, pAttrs->pwzRoleName, &piUsrInRoleColl); + ExitOnFailure(hr, "Failed to get users in role collection"); + + if (S_FALSE == hr) + { + // users in role collection not found + WcaLog(LOGMSG_VERBOSE, "Unable to retrieve users in role collection, nothing to delete, key: %S", pAttrs->pwzKey); + ExitFunction1(hr = S_OK); + } + + // get SID for account + do { + er = ERROR_SUCCESS; + hr = CpiAccountNameToSid(pAttrs->pwzAccount, &pSid); + if (HRESULT_FROM_WIN32(ERROR_NONE_MAPPED) == hr && !::MsiGetMode(WcaGetInstallHandle(), MSIRUNMODE_ROLLBACK)) + { + WcaLog(LOGMSG_STANDARD, "Failed to lookup account name, hr: 0x%x, account: '%S'", hr, pAttrs->pwzAccount); + er = WcaErrorMessage(msierrComPlusFailedLookupNames, hr, INSTALLMESSAGE_ERROR | MB_ABORTRETRYIGNORE, 0); + switch (er) + { + case IDABORT: + ExitFunction(); // exit with error code from CpiAccountNameToSid() + case IDRETRY: + break; + case IDIGNORE: + default: + ExitFunction1(hr = S_OK); + } + } + else + ExitOnFailure(hr, "Failed to get SID for account"); + } while (IDRETRY == er); + + // remove + hr = CpiRemoveUserCollectionObject(piUsrInRoleColl, pSid); + if (HRESULT_FROM_WIN32(ERROR_NONE_MAPPED) == hr || HRESULT_FROM_WIN32(ERROR_SOME_NOT_MAPPED) == hr) + { + WcaLog(LOGMSG_STANDARD, "Failed to lookup account names, hr: 0x%x", hr); + hr = S_FALSE; + } + else + ExitOnFailure(hr, "Failed to remove user"); + + if (S_FALSE == hr) + { + // role not found + WcaLog(LOGMSG_VERBOSE, "User not found for application role, nothing to delete, key: %S", pAttrs->pwzKey); + ExitFunction1(hr = S_OK); + } + + // save changes + hr = piUsrInRoleColl->SaveChanges(&lChanges); + if (COMADMIN_E_OBJECTERRORS == hr) + CpiLogCatalogErrorInfo(); + ExitOnFailure(hr, "Failed to save changes"); + + // log + WcaLog(LOGMSG_VERBOSE, "%d changes saved to catalog, key: %S", lChanges, pAttrs->pwzKey); + + hr = S_OK; + +LExit: + // clean up + ReleaseObject(piUsrInRoleColl); + + if (pSid) + ::HeapFree(::GetProcessHeap(), 0, pSid); + + return hr; +} diff --git a/src/ca/cpapproleexec.h b/src/ca/cpapproleexec.h new file mode 100644 index 00000000..1251cbdb --- /dev/null +++ b/src/ca/cpapproleexec.h @@ -0,0 +1,20 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + + +HRESULT CpiConfigureApplicationRoles( + LPWSTR* ppwzData, + HANDLE hRollbackFile + ); +HRESULT CpiRollbackConfigureApplicationRoles( + LPWSTR* ppwzData, + CPI_ROLLBACK_DATA* pRollbackDataList + ); +HRESULT CpiConfigureUsersInApplicationRoles( + LPWSTR* ppwzData, + HANDLE hRollbackFile + ); +HRESULT CpiRollbackConfigureUsersInApplicationRoles( + LPWSTR* ppwzData, + CPI_ROLLBACK_DATA* pRollbackDataList + ); diff --git a/src/ca/cpapprolesched.cpp b/src/ca/cpapprolesched.cpp new file mode 100644 index 00000000..a268d156 --- /dev/null +++ b/src/ca/cpapprolesched.cpp @@ -0,0 +1,843 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + + +// sql queries + +LPCWSTR vcsApplicationRoleQuery = + L"SELECT `ApplicationRole`, `Application_`, `Component_`, `Name` FROM `ComPlusApplicationRole`"; +enum eApplicationRoleQuery { arqApplicationRole = 1, arqApplication, arqComponent, arqName }; + +LPCWSTR vcsUserInApplicationRoleQuery = + L"SELECT `UserInApplicationRole`, `ApplicationRole_`, `ComPlusUserInApplicationRole`.`Component_`, `Domain`, `Name` FROM `ComPlusUserInApplicationRole`, `User` WHERE `User_` = `User`"; +LPCWSTR vcsGroupInApplicationRoleQuery = + L"SELECT `GroupInApplicationRole`, `ApplicationRole_`, `ComPlusGroupInApplicationRole`.`Component_`, `Domain`, `Name` FROM `ComPlusGroupInApplicationRole`, `Group` WHERE `Group_` = `Group`"; +enum eTrusteeInApplicationRoleQuery { tiarqUserInApplicationRole = 1, tiarqApplicationRole, tiarqComponent, tiarqDomain, tiarqName }; + +LPCWSTR vcsApplicationRolePropertyQuery = + L"SELECT `Name`, `Value` FROM `ComPlusApplicationRoleProperty` WHERE `ApplicationRole_` = ?"; + + +// property definitions + +CPI_PROPERTY_DEFINITION pdlApplicationRoleProperties[] = +{ + {L"Description", cpptString, 500}, + {NULL, cpptNone, 0} +}; + + +// prototypes for private helper functions + +static HRESULT TrusteesInApplicationRolesRead( + LPCWSTR pwzQuery, + CPI_APPLICATION_ROLE_LIST* pAppRoleList, + CPI_USER_IN_APPLICATION_ROLE_LIST* pUsrInAppRoleList + ); +static void FreeApplicationRole( + CPI_APPLICATION_ROLE* pItm + ); +static void FreeUserInApplicationRole( + CPI_USER_IN_APPLICATION_ROLE* pItm + ); +//static HRESULT GetUsersCollForApplicationRole( +// CPI_APPLICATION_ROLE* pAppRole, +// ICatalogCollection** ppiUsersColl +// ); +static HRESULT FindObjectForApplicationRole( + CPI_APPLICATION_ROLE* pItm, + ICatalogObject** ppiRoleObj + ); +static HRESULT AddApplicationRoleToActionData( + CPI_APPLICATION_ROLE* pItm, + int iActionType, + int iActionCost, + LPWSTR* ppwzActionData + ); +static HRESULT AddUserInApplicationRoleToActionData( + CPI_USER_IN_APPLICATION_ROLE* pItm, + int iActionType, + int iActionCost, + LPWSTR* ppwzActionData + ); + + +// function definitions + +void CpiApplicationRoleListFree( + CPI_APPLICATION_ROLE_LIST* pList + ) +{ + CPI_APPLICATION_ROLE* pItm = pList->pFirst; + + while (pItm) + { + CPI_APPLICATION_ROLE* pDelete = pItm; + pItm = pItm->pNext; + FreeApplicationRole(pDelete); + } +} + +HRESULT CpiApplicationRolesRead( + CPI_APPLICATION_LIST* pAppList, + CPI_APPLICATION_ROLE_LIST* pAppRoleList + ) +{ + HRESULT hr = S_OK; + UINT er = ERROR_SUCCESS; + + PMSIHANDLE hView, hRec; + + CPI_APPLICATION_ROLE* pItm = NULL; + LPWSTR pwzData = NULL; + BOOL fMatchingArchitecture = FALSE; + + // loop through all application roles + hr = WcaOpenExecuteView(vcsApplicationRoleQuery, &hView); + ExitOnFailure(hr, "Failed to execute view on ComPlusApplicationRole table"); + + while (S_OK == (hr = WcaFetchRecord(hView, &hRec))) + { + // get component + hr = WcaGetRecordString(hRec, arqComponent, &pwzData); + ExitOnFailure(hr, "Failed to get component"); + + // check if the component is our processor architecture + if (pwzData && *pwzData) + { + hr = CpiVerifyComponentArchitecure(pwzData, &fMatchingArchitecture); + ExitOnFailure(hr, "Failed to get component architecture."); + + if (!fMatchingArchitecture) + { + continue; // not the same architecture, ignore + } + } + + // create entry + pItm = (CPI_APPLICATION_ROLE*)::HeapAlloc(::GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(CPI_APPLICATION_ROLE)); + if (!pItm) + ExitFunction1(hr = E_OUTOFMEMORY); + + // get component install state + if (pwzData && *pwzData) + { + pItm->fHasComponent = TRUE; + + er = ::MsiGetComponentStateW(WcaGetInstallHandle(), pwzData, &pItm->isInstalled, &pItm->isAction); + ExitOnFailure(hr = HRESULT_FROM_WIN32(er), "Failed to get component state"); + } + + // get key + hr = WcaGetRecordString(hRec, arqApplicationRole, &pwzData); + ExitOnFailure(hr, "Failed to get key"); + StringCchCopyW(pItm->wzKey, countof(pItm->wzKey), pwzData); + + // get application + hr = WcaGetRecordString(hRec, arqApplication, &pwzData); + ExitOnFailure(hr, "Failed to get application"); + + hr = CpiApplicationFindByKey(pAppList, pwzData, &pItm->pApplication); + if (S_FALSE == hr) + hr = HRESULT_FROM_WIN32(ERROR_NOT_FOUND); + ExitOnFailure(hr, "Failed to find application, key: %S", pwzData); + + // get name + hr = WcaGetRecordFormattedString(hRec, arqName, &pwzData); + ExitOnFailure(hr, "Failed to get name"); + StringCchCopyW(pItm->wzName, countof(pItm->wzName), pwzData); + + // get properties + if (CpiTableExists(cptComPlusApplicationRoleProperty)) + { + hr = CpiPropertiesRead(vcsApplicationRolePropertyQuery, pItm->wzKey, pdlApplicationRoleProperties, &pItm->pProperties, &pItm->iPropertyCount); + ExitOnFailure(hr, "Failed to get properties"); + } + + // set references & increment counters + if (pItm->fHasComponent) + { + if (WcaIsInstalling(pItm->isInstalled, pItm->isAction)) + { + CpiApplicationAddReferenceInstall(pItm->pApplication); + pAppRoleList->iInstallCount++; + } + if (WcaIsUninstalling(pItm->isInstalled, pItm->isAction)) + { + CpiApplicationAddReferenceUninstall(pItm->pApplication); + pAppRoleList->iUninstallCount++; + } + } + + // add entry + if (pAppRoleList->pFirst) + pItm->pNext = pAppRoleList->pFirst; + pAppRoleList->pFirst = pItm; + pItm = NULL; + } + + if (E_NOMOREITEMS == hr) + hr = S_OK; + +LExit: + // clean up + if (pItm) + FreeApplicationRole(pItm); + + ReleaseStr(pwzData); + + return hr; +} + +HRESULT CpiApplicationRolesVerifyInstall( + CPI_APPLICATION_ROLE_LIST* pList + ) +{ + HRESULT hr = S_OK; + UINT er = ERROR_SUCCESS; + + ICatalogObject* piRoleObj = NULL; + + for (CPI_APPLICATION_ROLE* pItm = pList->pFirst; pItm; pItm = pItm->pNext) + { + // referenced locaters or roles that are being installed + if (!pItm->fReferencedForInstall && !(pItm->fHasComponent && WcaIsInstalling(pItm->isInstalled, pItm->isAction))) + continue; + + // if the role is referensed and is not a locater, it must be installed + if (pItm->fReferencedForInstall && pItm->fHasComponent && !CpiWillBeInstalled(pItm->isInstalled, pItm->isAction)) + MessageExitOnFailure(hr = E_FAIL, msierrComPlusApplicationRoleDependency, "An application role is used by another entity being installed, but is not installed itself, key: %S", pItm->wzKey); + + // role is a locater + if (!pItm->fHasComponent) + { + // get collection object for role + hr = FindObjectForApplicationRole(pItm, &piRoleObj); + ExitOnFailure(hr, "Failed to find collection object for role"); + + // if the role was not found + if (S_FALSE == hr) + MessageExitOnFailure(hr = HRESULT_FROM_WIN32(ERROR_NOT_FOUND), msierrComPlusApplicationRoleNotFound, "An application role required by this installation was not found, key: %S", pItm->wzKey); + } + + // role is supposed to be created + else if (!CpiIsInstalled(pItm->isInstalled)) + { + do { + // find roles with conflicting name or id + hr = FindObjectForApplicationRole(pItm, NULL); + ExitOnFailure(hr, "Failed to find collection object for role"); + + if (S_OK == hr) + { + er = WcaErrorMessage(msierrComPlusApplicationRoleConflict, hr, INSTALLMESSAGE_ERROR | MB_ABORTRETRYIGNORE, 0); + switch (er) + { + case IDABORT: + ExitOnFailure(hr = E_FAIL, "An application with a conflictiong name exists, key: %S", pItm->wzKey); + break; + case IDRETRY: + break; + case IDIGNORE: + default: + hr = S_FALSE; // indicate that this is not a conflict + } + } + } while (S_OK == hr); // hr = S_FALSE if we don't have any conflicts + } + + // clean up + ReleaseNullObject(piRoleObj); + } + + hr = S_OK; + +LExit: + // clean up + ReleaseObject(piRoleObj); + + return hr; +} + +HRESULT CpiApplicationRolesVerifyUninstall( + CPI_APPLICATION_ROLE_LIST* pList + ) +{ + HRESULT hr = S_OK; + + for (CPI_APPLICATION_ROLE* pItm = pList->pFirst; pItm; pItm = pItm->pNext) + { + // referenced locaters or roles that are being installed + if (!pItm->fReferencedForUninstall && !(pItm->fHasComponent && WcaIsUninstalling(pItm->isInstalled, pItm->isAction))) + continue; + + // get collection object for role + hr = FindObjectForApplicationRole(pItm, NULL); + ExitOnFailure(hr, "Failed to find collection object for role"); + + // if the role was not found + if (S_FALSE == hr) + { + pItm->fObjectNotFound = TRUE; + if (pItm->fHasComponent) + pList->iUninstallCount--; // elements with the fObjectNotFound flag set will not be scheduled for uninstall + } + } + + hr = S_OK; + +LExit: + return hr; +} + +void CpiApplicationRoleAddReferenceInstall( + CPI_APPLICATION_ROLE* pItm + ) +{ + pItm->fReferencedForInstall = TRUE; + CpiApplicationAddReferenceInstall(pItm->pApplication); +} + +void CpiApplicationRoleAddReferenceUninstall( + CPI_APPLICATION_ROLE* pItm + ) +{ + pItm->fReferencedForUninstall = TRUE; + CpiApplicationAddReferenceUninstall(pItm->pApplication); +} + +HRESULT CpiApplicationRolesInstall( + CPI_APPLICATION_ROLE_LIST* pList, + int iRunMode, + LPWSTR* ppwzActionData, + int* piProgress + ) +{ + HRESULT hr = S_OK; + + int iActionType; + + // add action text + hr = CpiAddActionTextToActionData(L"CreateComPlusApplicationRoles", ppwzActionData); + ExitOnFailure(hr, "Failed to add action text to custom action data"); + + // add count to action data + hr = WcaWriteIntegerToCaData(pList->iInstallCount, ppwzActionData); + ExitOnFailure(hr, "Failed to add count to custom action data"); + + // add roles to custom action data + for (CPI_APPLICATION_ROLE* pItm = pList->pFirst; pItm; pItm = pItm->pNext) + { + // roles that are being installed only + if (!WcaIsInstalling(pItm->isInstalled, pItm->isAction)) + continue; + + // action type + if (rmRollback == iRunMode) + { + if (CpiIsInstalled(pItm->isInstalled)) + iActionType = atNoOp; + else + iActionType = atRemove; + } + else + iActionType = atCreate; + + // add to action data + hr = AddApplicationRoleToActionData(pItm, iActionType, COST_APPLICATION_ROLE_CREATE, ppwzActionData); + ExitOnFailure(hr, "Failed to add application role to custom action data, key: %S", pItm->wzKey); + } + + // add progress tics + if (piProgress) + *piProgress += COST_APPLICATION_ROLE_CREATE * pList->iInstallCount; + + hr = S_OK; + +LExit: + return hr; +} + +HRESULT CpiApplicationRolesUninstall( + CPI_APPLICATION_ROLE_LIST* pList, + int iRunMode, + LPWSTR* ppwzActionData, + int* piProgress + ) +{ + HRESULT hr = S_OK; + + int iActionType; + + // add action text + hr = CpiAddActionTextToActionData(L"RemoveComPlusApplicationRoles", ppwzActionData); + ExitOnFailure(hr, "Failed to add action text to custom action data"); + + // add count to action data + hr = WcaWriteIntegerToCaData(pList->iUninstallCount, ppwzActionData); + ExitOnFailure(hr, "Failed to add count to custom action data"); + + // add roles to custom action data + for (CPI_APPLICATION_ROLE* pItm = pList->pFirst; pItm; pItm = pItm->pNext) + { + // roles that are being uninstalled only + if (pItm->fObjectNotFound || !WcaIsUninstalling(pItm->isInstalled, pItm->isAction)) + continue; + + // action type + if (rmRollback == iRunMode) + iActionType = atCreate; + else + iActionType = atRemove; + + // add to action data + hr = AddApplicationRoleToActionData(pItm, iActionType, COST_APPLICATION_ROLE_DELETE, ppwzActionData); + ExitOnFailure(hr, "Failed to add application role to custom action data, key: %S", pItm->wzKey); + } + + // add progress tics + if (piProgress) + *piProgress += COST_APPLICATION_ROLE_DELETE * pList->iUninstallCount; + + hr = S_OK; + +LExit: + return hr; +} + +HRESULT CpiApplicationRoleFindByKey( + CPI_APPLICATION_ROLE_LIST* pList, + LPCWSTR pwzKey, + CPI_APPLICATION_ROLE** ppAppRole + ) +{ + for (CPI_APPLICATION_ROLE* pItm = pList->pFirst; pItm; pItm = pItm->pNext) + { + if (0 == lstrcmpW(pItm->wzKey, pwzKey)) + { + *ppAppRole = pItm; + return S_OK; + } + } + + return S_FALSE; +} + +void CpiUserInApplicationRoleListFree( + CPI_USER_IN_APPLICATION_ROLE_LIST* pList + ) +{ + CPI_USER_IN_APPLICATION_ROLE* pItm = pList->pFirst; + + while (pItm) + { + CPI_USER_IN_APPLICATION_ROLE* pDelete = pItm; + pItm = pItm->pNext; + FreeUserInApplicationRole(pDelete); + } +} + +HRESULT CpiUsersInApplicationRolesRead( + CPI_APPLICATION_ROLE_LIST* pAppRoleList, + CPI_USER_IN_APPLICATION_ROLE_LIST* pUsrInAppRoleList + ) +{ + HRESULT hr = S_OK; + + // read users in application roles + if (CpiTableExists(cptComPlusUserInApplicationRole)) + { + hr = TrusteesInApplicationRolesRead(vcsUserInApplicationRoleQuery, pAppRoleList, pUsrInAppRoleList); + ExitOnFailure(hr, "Failed to read users in application roles"); + } + + // read groups in application roles + if (CpiTableExists(cptComPlusGroupInApplicationRole)) + { + hr = TrusteesInApplicationRolesRead(vcsGroupInApplicationRoleQuery, pAppRoleList, pUsrInAppRoleList); + ExitOnFailure(hr, "Failed to read groups in application roles"); + } + + hr = S_OK; + +LExit: + return hr; +} + +HRESULT CpiUsersInApplicationRolesInstall( + CPI_USER_IN_APPLICATION_ROLE_LIST* pList, + int iRunMode, + LPWSTR* ppwzActionData, + int* piProgress + ) +{ + HRESULT hr = S_OK; + + int iActionType; + + // add action text + hr = CpiAddActionTextToActionData(L"AddUsersToComPlusApplicationRoles", ppwzActionData); + ExitOnFailure(hr, "Failed to add action text to custom action data"); + + // add count to action data + hr = WcaWriteIntegerToCaData(pList->iInstallCount, ppwzActionData); + ExitOnFailure(hr, "Failed to add count to custom action data"); + + // add roles to custom action data + for (CPI_USER_IN_APPLICATION_ROLE* pItm = pList->pFirst; pItm; pItm = pItm->pNext) + { + // roles that are being installed only + if (!WcaIsInstalling(pItm->isInstalled, pItm->isAction)) + continue; + + // action type + if (rmRollback == iRunMode) + { + if (CpiIsInstalled(pItm->isInstalled)) + iActionType = atNoOp; + else + iActionType = atRemove; + } + else + iActionType = atCreate; + + // add to action data + hr = AddUserInApplicationRoleToActionData(pItm, iActionType, COST_USER_IN_APPLICATION_ROLE_CREATE, ppwzActionData); + ExitOnFailure(hr, "Failed to add user in application role to custom action data, key: %S", pItm->wzKey); + } + + // add progress tics + if (piProgress) + *piProgress += COST_USER_IN_APPLICATION_ROLE_CREATE * pList->iInstallCount; + + hr = S_OK; + +LExit: + return hr; +} + +HRESULT CpiUsersInApplicationRolesUninstall( + CPI_USER_IN_APPLICATION_ROLE_LIST* pList, + int iRunMode, + LPWSTR* ppwzActionData, + int* piProgress + ) +{ + HRESULT hr = S_OK; + + int iActionType; + + // add action text + hr = CpiAddActionTextToActionData(L"RemoveUsersFromComPlusAppRoles", ppwzActionData); + ExitOnFailure(hr, "Failed to add action text to custom action data"); + + // add count to action data + hr = WcaWriteIntegerToCaData(pList->iUninstallCount, ppwzActionData); + ExitOnFailure(hr, "Failed to add count to custom action data"); + + // add roles to custom action data + for (CPI_USER_IN_APPLICATION_ROLE* pItm = pList->pFirst; pItm; pItm = pItm->pNext) + { + // roles that are being uninstalled only + if (!WcaIsUninstalling(pItm->isInstalled, pItm->isAction)) + continue; + + // action type + if (rmRollback == iRunMode) + iActionType = atCreate; + else + iActionType = atRemove; + + // add to action data + hr = AddUserInApplicationRoleToActionData(pItm, iActionType, COST_USER_IN_APPLICATION_ROLE_DELETE, ppwzActionData); + ExitOnFailure(hr, "Failed to add user in application role to custom action data, key: %S", pItm->wzKey); + } + + // add progress tics + if (piProgress) + *piProgress += COST_USER_IN_APPLICATION_ROLE_DELETE * pList->iUninstallCount; + + hr = S_OK; + +LExit: + return hr; +} + + +// helper function definitions + +static HRESULT TrusteesInApplicationRolesRead( + LPCWSTR pwzQuery, + CPI_APPLICATION_ROLE_LIST* pAppRoleList, + CPI_USER_IN_APPLICATION_ROLE_LIST* pUsrInAppRoleList + ) +{ + HRESULT hr = S_OK; + UINT er = ERROR_SUCCESS; + + PMSIHANDLE hView, hRec; + + CPI_USER_IN_APPLICATION_ROLE* pItm = NULL; + LPWSTR pwzData = NULL; + LPWSTR pwzDomain = NULL; + LPWSTR pwzName = NULL; + BOOL fMatchingArchitecture = FALSE; + + // loop through all application roles + hr = WcaOpenExecuteView(pwzQuery, &hView); + ExitOnFailure(hr, "Failed to execute view on table"); + + while (S_OK == (hr = WcaFetchRecord(hView, &hRec))) + { + // get component + hr = WcaGetRecordString(hRec, tiarqComponent, &pwzData); + ExitOnFailure(hr, "Failed to get component"); + + // check if the component is our processor architecture + hr = CpiVerifyComponentArchitecure(pwzData, &fMatchingArchitecture); + ExitOnFailure(hr, "Failed to get component architecture."); + + if (!fMatchingArchitecture) + { + continue; // not the same architecture, ignore + } + + // create entry + pItm = (CPI_USER_IN_APPLICATION_ROLE*)::HeapAlloc(::GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(CPI_USER_IN_APPLICATION_ROLE)); + if (!pItm) + ExitFunction1(hr = E_OUTOFMEMORY); + + // get component install state + er = ::MsiGetComponentStateW(WcaGetInstallHandle(), pwzData, &pItm->isInstalled, &pItm->isAction); + ExitOnFailure(hr = HRESULT_FROM_WIN32(er), "Failed to get component state"); + + // get key + hr = WcaGetRecordString(hRec, tiarqUserInApplicationRole, &pwzData); + ExitOnFailure(hr, "Failed to get key"); + StringCchCopyW(pItm->wzKey, countof(pItm->wzKey), pwzData); + + // get application role + hr = WcaGetRecordString(hRec, tiarqApplicationRole, &pwzData); + ExitOnFailure(hr, "Failed to get application role"); + + hr = CpiApplicationRoleFindByKey(pAppRoleList, pwzData, &pItm->pApplicationRole); + if (S_FALSE == hr) + hr = HRESULT_FROM_WIN32(ERROR_NOT_FOUND); + ExitOnFailure(hr, "Failed to find application role, key: %S", pwzData); + + // get user domain + hr = WcaGetRecordFormattedString(hRec, tiarqDomain, &pwzDomain); + ExitOnFailure(hr, "Failed to get domain"); + + // get user name + hr = WcaGetRecordFormattedString(hRec, tiarqName, &pwzName); + ExitOnFailure(hr, "Failed to get name"); + + // build account name + hr = CpiBuildAccountName(pwzDomain, pwzName, &pItm->pwzAccount); + ExitOnFailure(hr, "Failed to build account name"); + + // set references & increment counters + if (WcaIsInstalling(pItm->isInstalled, pItm->isAction)) + { + CpiApplicationRoleAddReferenceInstall(pItm->pApplicationRole); + pUsrInAppRoleList->iInstallCount++; + } + if (WcaIsUninstalling(pItm->isInstalled, pItm->isAction)) + { + CpiApplicationRoleAddReferenceUninstall(pItm->pApplicationRole); + pUsrInAppRoleList->iUninstallCount++; + } + + // add entry + if (pUsrInAppRoleList->pFirst) + pItm->pNext = pUsrInAppRoleList->pFirst; + pUsrInAppRoleList->pFirst = pItm; + pItm = NULL; + } + + if (E_NOMOREITEMS == hr) + hr = S_OK; + +LExit: + // clean up + if (pItm) + FreeUserInApplicationRole(pItm); + + ReleaseStr(pwzData); + ReleaseStr(pwzDomain); + ReleaseStr(pwzName); + + return hr; +} + +static void FreeApplicationRole( + CPI_APPLICATION_ROLE* pItm + ) +{ + if (pItm->pProperties) + CpiPropertiesFreeList(pItm->pProperties); + + ReleaseObject(pItm->piUsersColl); + + ::HeapFree(::GetProcessHeap(), 0, pItm); +} + +static void FreeUserInApplicationRole( + CPI_USER_IN_APPLICATION_ROLE* pItm + ) +{ + ReleaseStr(pItm->pwzAccount); + + ::HeapFree(::GetProcessHeap(), 0, pItm); +} + +//static HRESULT GetUsersCollForApplicationRole( +// CPI_APPLICATION_ROLE* pAppRole, +// ICatalogCollection** ppiUsersColl +// ) +//{ +// HRESULT hr = S_OK; +// +// ICatalogCollection* piRoleColl = NULL; +// ICatalogObject* piRoleObj = NULL; +// +// // if a previous attempt to locate the collection object failed +// if (pAppRole->fObjectNotFound) +// ExitFunction1(hr = S_FALSE); +// +// // get applications collection +// if (!pAppRole->piUsersColl) +// { +// // get collection object for role +// hr = FindObjectForApplicationRole(pAppRole, &piRoleObj); +// ExitOnFailure(hr, "Failed to find collection object for role"); +// +// if (S_FALSE == hr) +// ExitFunction(); // exit with hr = S_FALSE +// +// // get users collection +// hr = CpiGetCatalogCollection(piRoleColl, piRoleObj, L"UsersInRole", &pAppRole->piUsersColl); +// ExitOnFailure(hr, "Failed to get users in role collection"); +// } +// +// // return value +// *ppiUsersColl = pAppRole->piUsersColl; +// (*ppiUsersColl)->AddRef(); +// +// hr = S_OK; +// +//LExit: +// // clean up +// ReleaseObject(piRoleColl); +// ReleaseObject(piRoleObj); +// +// return hr; +//} + +static HRESULT FindObjectForApplicationRole( + CPI_APPLICATION_ROLE* pItm, + ICatalogObject** ppiRoleObj + ) +{ + HRESULT hr = S_OK; + + ICatalogCollection* piRoleColl = NULL; + + // get roles collection + hr = CpiGetRolesCollForApplication(pItm->pApplication, &piRoleColl); + ExitOnFailure(hr, "Failed to get collection"); + + if (S_FALSE == hr) + ExitFunction(); // exit with hr = S_FALSE + + // find role object + hr = CpiFindCollectionObject(piRoleColl, NULL, pItm->wzName, ppiRoleObj); + ExitOnFailure(hr, "Failed to find object"); + + // exit with hr from CpiFindCollectionObject() + +LExit: + // clean up + ReleaseObject(piRoleColl); + + return hr; +} + +static HRESULT AddApplicationRoleToActionData( + CPI_APPLICATION_ROLE* pItm, + int iActionType, + int iActionCost, + LPWSTR* ppwzActionData + ) +{ + HRESULT hr = S_OK; + + // add action information to custom action data + hr = WcaWriteIntegerToCaData(iActionType, ppwzActionData); + ExitOnFailure(hr, "Failed to add action type to custom action data"); + hr = WcaWriteIntegerToCaData(iActionCost, ppwzActionData); + ExitOnFailure(hr, "Failed to add action cost to custom action data"); + + // add application role information to custom action data + hr = WcaWriteStringToCaData(pItm->wzKey, ppwzActionData); + ExitOnFailure(hr, "Failed to add application role key to custom action data"); + hr = WcaWriteStringToCaData(pItm->wzName, ppwzActionData); + ExitOnFailure(hr, "Failed to add application role name to custom action data"); + + // add application information to custom action data + hr = WcaWriteStringToCaData(pItm->pApplication->wzID, ppwzActionData); + ExitOnFailure(hr, "Failed to add application id to custom action data"); + + // add partition information to custom action data + hr = WcaWriteStringToCaData(pItm->pApplication->pPartition ? pItm->pApplication->pPartition->wzID : L"", ppwzActionData); + ExitOnFailure(hr, "Failed to add partition id to custom action data"); + + // add properties to custom action data + hr = CpiAddPropertiesToActionData(atCreate == iActionType ? pItm->iPropertyCount : 0, pItm->pProperties, ppwzActionData); + ExitOnFailure(hr, "Failed to add properties to custom action data"); + + hr = S_OK; + +LExit: + return hr; +} + +static HRESULT AddUserInApplicationRoleToActionData( + CPI_USER_IN_APPLICATION_ROLE* pItm, + int iActionType, + int iActionCost, + LPWSTR* ppwzActionData + ) +{ + HRESULT hr = S_OK; + + // add action information to custom action data + hr = WcaWriteIntegerToCaData(iActionType, ppwzActionData); + ExitOnFailure(hr, "Failed to add action type to custom action data"); + hr = WcaWriteIntegerToCaData(iActionCost, ppwzActionData); + ExitOnFailure(hr, "Failed to add action cost to custom action data"); + + // add application role information to custom action data + hr = WcaWriteStringToCaData(pItm->wzKey, ppwzActionData); + ExitOnFailure(hr, "Failed to add key to custom action data"); + hr = WcaWriteStringToCaData(pItm->pApplicationRole->wzName, ppwzActionData); + ExitOnFailure(hr, "Failed to add role name to custom action data"); + hr = WcaWriteStringToCaData(pItm->pwzAccount, ppwzActionData); + ExitOnFailure(hr, "Failed to add user account to custom action data"); + + // add application information to custom action data + CPI_APPLICATION* pApplication = pItm->pApplicationRole->pApplication; + hr = WcaWriteStringToCaData(pApplication->wzID, ppwzActionData); + ExitOnFailure(hr, "Failed to add application id to custom action data"); + + // add partition information to custom action data + hr = WcaWriteStringToCaData(pApplication->pPartition ? pApplication->pPartition->wzID : L"", ppwzActionData); + ExitOnFailure(hr, "Failed to add partition id to custom action data"); + + hr = S_OK; + +LExit: + return hr; +} diff --git a/src/ca/cpapprolesched.h b/src/ca/cpapprolesched.h new file mode 100644 index 00000000..02852eef --- /dev/null +++ b/src/ca/cpapprolesched.h @@ -0,0 +1,112 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + + +struct CPI_APPLICATION_ROLE +{ + WCHAR wzKey[MAX_DARWIN_KEY + 1]; + WCHAR wzName[MAX_DARWIN_COLUMN + 1]; + + int iPropertyCount; + CPI_PROPERTY* pProperties; + + BOOL fHasComponent; + BOOL fReferencedForInstall; + BOOL fReferencedForUninstall; + BOOL fObjectNotFound; + + INSTALLSTATE isInstalled, isAction; + + CPI_APPLICATION* pApplication; + + ICatalogCollection* piUsersColl; + + CPI_APPLICATION_ROLE* pNext; +}; + +struct CPI_APPLICATION_ROLE_LIST +{ + CPI_APPLICATION_ROLE* pFirst; + + int iInstallCount; + int iUninstallCount; +}; + +struct CPI_USER_IN_APPLICATION_ROLE +{ + WCHAR wzKey[MAX_DARWIN_KEY + 1]; + LPWSTR pwzAccount; + + INSTALLSTATE isInstalled, isAction; + + CPI_APPLICATION_ROLE* pApplicationRole; + + CPI_USER_IN_APPLICATION_ROLE* pNext; +}; + +struct CPI_USER_IN_APPLICATION_ROLE_LIST +{ + CPI_USER_IN_APPLICATION_ROLE* pFirst; + + int iInstallCount; + int iUninstallCount; +}; + + +// function prototypes + +void CpiApplicationRoleListFree( + CPI_APPLICATION_ROLE_LIST* pList + ); +HRESULT CpiApplicationRolesRead( + CPI_APPLICATION_LIST* pAppList, + CPI_APPLICATION_ROLE_LIST* pAppRoleList + ); +HRESULT CpiApplicationRolesVerifyInstall( + CPI_APPLICATION_ROLE_LIST* pList + ); +HRESULT CpiApplicationRolesVerifyUninstall( + CPI_APPLICATION_ROLE_LIST* pList + ); +void CpiApplicationRoleAddReferenceInstall( + CPI_APPLICATION_ROLE* pItm + ); +void CpiApplicationRoleAddReferenceUninstall( + CPI_APPLICATION_ROLE* pItm + ); +HRESULT CpiApplicationRolesInstall( + CPI_APPLICATION_ROLE_LIST* pList, + int iRunMode, + LPWSTR* ppwzActionData, + int* piProgress + ); +HRESULT CpiApplicationRolesUninstall( + CPI_APPLICATION_ROLE_LIST* pList, + int iRunMode, + LPWSTR* ppwzActionData, + int* piProgress + ); +HRESULT CpiApplicationRoleFindByKey( + CPI_APPLICATION_ROLE_LIST* pList, + LPCWSTR pwzKey, + CPI_APPLICATION_ROLE** ppAppRole + ); +void CpiUserInApplicationRoleListFree( + CPI_USER_IN_APPLICATION_ROLE_LIST* pList + ); +HRESULT CpiUsersInApplicationRolesRead( + CPI_APPLICATION_ROLE_LIST* pAppRoleList, + CPI_USER_IN_APPLICATION_ROLE_LIST* pUsrInAppRoleList + ); +HRESULT CpiUsersInApplicationRolesInstall( + CPI_USER_IN_APPLICATION_ROLE_LIST* pList, + int iRunMode, + LPWSTR* ppwzActionData, + int* piProgress + ); +HRESULT CpiUsersInApplicationRolesUninstall( + CPI_USER_IN_APPLICATION_ROLE_LIST* pList, + int iRunMode, + LPWSTR* ppwzActionData, + int* piProgress + ); diff --git a/src/ca/cpappsched.cpp b/src/ca/cpappsched.cpp new file mode 100644 index 00000000..cec99794 --- /dev/null +++ b/src/ca/cpappsched.cpp @@ -0,0 +1,752 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + + +// sql queries + +LPCWSTR vcsApplicationQuery = + L"SELECT `Application`, `Component_`, `Partition_`, `Id`, `Name` FROM `ComPlusApplication`"; +enum eApplicationQuery { aqApplication = 1, aqComponent, aqPartition, aqID, aqName }; + +LPCWSTR vcsApplicationPropertyQuery = + L"SELECT `Name`, `Value` FROM `ComPlusApplicationProperty` WHERE `Application_` = ?"; + + +// property definitions + +CPI_PROPERTY_DEFINITION pdlApplicationProperties[] = +{ + {L"3GigSupportEnabled", cpptBoolean, 500}, + {L"AccessChecksLevel", cpptInteger, 500}, + {L"Activation", cpptInteger, 500}, + {L"ApplicationAccessChecksEnabled", cpptBoolean, 500}, + {L"ApplicationDirectory", cpptString, 501}, + {L"Authentication", cpptInteger, 500}, + {L"AuthenticationCapability", cpptInteger, 500}, + {L"Changeable", cpptBoolean, 500}, + {L"CommandLine", cpptString, 500}, + {L"ConcurrentApps", cpptInteger, 501}, + {L"CreatedBy", cpptString, 500}, + {L"CRMEnabled", cpptBoolean, 500}, + {L"CRMLogFile", cpptString, 500}, + {L"Deleteable", cpptBoolean, 500}, + {L"Description", cpptString, 500}, + {L"DumpEnabled", cpptBoolean, 501}, + {L"DumpOnException", cpptBoolean, 501}, + {L"DumpOnFailfast", cpptBoolean, 501}, + {L"DumpPath", cpptString, 501}, + {L"EventsEnabled", cpptBoolean, 500}, + {L"Identity", cpptString, 500}, + {L"ImpersonationLevel", cpptInteger, 500}, + {L"IsEnabled", cpptBoolean, 501}, + {L"MaxDumpCount", cpptInteger, 501}, + {L"Password", cpptString, 500}, + {L"QCAuthenticateMsgs", cpptInteger, 501}, + {L"QCListenerMaxThreads", cpptInteger, 501}, + {L"QueueListenerEnabled", cpptBoolean, 500}, + {L"QueuingEnabled", cpptBoolean, 500}, + {L"RecycleActivationLimit", cpptInteger, 501}, + {L"RecycleCallLimit", cpptInteger, 501}, + {L"RecycleExpirationTimeout", cpptInteger, 501}, + {L"RecycleLifetimeLimit", cpptInteger, 501}, + {L"RecycleMemoryLimit", cpptInteger, 501}, + {L"Replicable", cpptBoolean, 501}, + {L"RunForever", cpptBoolean, 500}, + {L"ShutdownAfter", cpptInteger, 500}, + {L"SoapActivated", cpptBoolean, 502}, + {L"SoapBaseUrl", cpptString, 502}, + {L"SoapMailTo", cpptString, 502}, + {L"SoapVRoot", cpptString, 502}, + {L"SRPEnabled", cpptBoolean, 501}, + {L"SRPTrustLevel", cpptInteger, 501}, + {NULL, cpptNone, 0} +}; + + +// prototypes for private helper functions + +static void FreeApplication( + CPI_APPLICATION* pItm + ); +static HRESULT FindObjectForApplication( + CPI_APPLICATION* pItm, + BOOL fFindId, + BOOL fFindName, + ICatalogObject** ppiAppObj + ); +static HRESULT AddApplicationToActionData( + CPI_APPLICATION* pItm, + int iActionType, + int iActionCost, + LPWSTR* ppwzActionData + ); + + +// function definitions + +void CpiApplicationListFree( + CPI_APPLICATION_LIST* pList + ) +{ + CPI_APPLICATION* pItm = pList->pFirst; + + while (pItm) + { + CPI_APPLICATION* pDelete = pItm; + pItm = pItm->pNext; + FreeApplication(pDelete); + } +} + +HRESULT CpiApplicationsRead( + CPI_PARTITION_LIST* pPartList, + CPI_APPLICATION_LIST* pAppList + ) +{ + HRESULT hr = S_OK; + UINT er = ERROR_SUCCESS; + + int iVersionNT = 0; + + PMSIHANDLE hView, hRec; + + CPI_APPLICATION* pItm = NULL; + LPWSTR pwzData = NULL; + BOOL fMatchingArchitecture = FALSE; + + // get NT version + hr = WcaGetIntProperty(L"VersionNT", &iVersionNT); + ExitOnFailure(hr, "Failed to get VersionNT property"); + + // loop through all applications + hr = WcaOpenExecuteView(vcsApplicationQuery, &hView); + ExitOnFailure(hr, "Failed to execute view on ComPlusApplication table"); + + while (S_OK == (hr = WcaFetchRecord(hView, &hRec))) + { + // get component + hr = WcaGetRecordString(hRec, aqComponent, &pwzData); + ExitOnFailure(hr, "Failed to get component"); + + // check if the component is our processor architecture + if (pwzData && *pwzData) + { + hr = CpiVerifyComponentArchitecure(pwzData, &fMatchingArchitecture); + ExitOnFailure(hr, "Failed to get component architecture."); + + if (!fMatchingArchitecture) + { + continue; // not the same architecture, ignore + } + } + + // create entry + pItm = (CPI_APPLICATION*)::HeapAlloc(::GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(CPI_APPLICATION)); + if (!pItm) + ExitFunction1(hr = E_OUTOFMEMORY); + + // get component install state + if (pwzData && *pwzData) + { + pItm->fHasComponent = TRUE; + + er = ::MsiGetComponentStateW(WcaGetInstallHandle(), pwzData, &pItm->isInstalled, &pItm->isAction); + ExitOnFailure(hr = HRESULT_FROM_WIN32(er), "Failed to get component state"); + } + + // get key + hr = WcaGetRecordString(hRec, aqApplication, &pwzData); + ExitOnFailure(hr, "Failed to get key"); + StringCchCopyW(pItm->wzKey, countof(pItm->wzKey), pwzData); + + // get partition + if (502 <= iVersionNT) + { + hr = WcaGetRecordString(hRec, aqPartition, &pwzData); + ExitOnFailure(hr, "Failed to get partition"); + + if (pwzData && *pwzData) + { + hr = CpiPartitionFindByKey(pPartList, pwzData, &pItm->pPartition); + ExitOnFailure(hr, "Failed to find partition, key: %S", pwzData); + } + } + + // get id + hr = WcaGetRecordFormattedString(hRec, aqID, &pwzData); + ExitOnFailure(hr, "Failed to get id"); + + if (pwzData && *pwzData) + { + hr = PcaGuidToRegFormat(pwzData, pItm->wzID, countof(pItm->wzID)); + ExitOnFailure(hr, "Failed to parse id guid value, key: %S, value: '%S'", pItm->wzKey, pwzData); + } + + // get name + hr = WcaGetRecordFormattedString(hRec, aqName, &pwzData); + ExitOnFailure(hr, "Failed to get name"); + StringCchCopyW(pItm->wzName, countof(pItm->wzName), pwzData); + + // if application is a locater, either an id or a name must be provided + if (!pItm->fHasComponent && !*pItm->wzID && !*pItm->wzName) + ExitOnFailure(hr = E_FAIL, "An application locater must have either an id or a name associated, key: %S", pItm->wzKey); + + // if application is not a locater, an name must be provided + if (pItm->fHasComponent && !*pItm->wzName) + ExitOnFailure(hr = E_FAIL, "An application must have a name associated, key: %S", pItm->wzKey); + + // get properties + if (CpiTableExists(cptComPlusApplicationProperty) && pItm->fHasComponent) + { + hr = CpiPropertiesRead(vcsApplicationPropertyQuery, pItm->wzKey, pdlApplicationProperties, &pItm->pProperties, &pItm->iPropertyCount); + ExitOnFailure(hr, "Failed to get properties"); + } + + // set references & increment counters + if (pItm->fHasComponent) + { + if (WcaIsInstalling(pItm->isInstalled, pItm->isAction)) + { + if (pItm->pPartition) + CpiPartitionAddReferenceInstall(pItm->pPartition); + pAppList->iInstallCount++; + } + if (WcaIsUninstalling(pItm->isInstalled, pItm->isAction)) + { + if (pItm->pPartition) + CpiPartitionAddReferenceUninstall(pItm->pPartition); + pAppList->iUninstallCount++; + } + } + + // add entry + if (pAppList->pFirst) + pItm->pNext = pAppList->pFirst; + pAppList->pFirst = pItm; + pItm = NULL; + } + + if (E_NOMOREITEMS == hr) + hr = S_OK; + +LExit: + // clean up + if (pItm) + FreeApplication(pItm); + + ReleaseStr(pwzData); + + return hr; +} + +HRESULT CpiApplicationsVerifyInstall( + CPI_APPLICATION_LIST* pList + ) +{ + HRESULT hr = S_OK; + UINT er = ERROR_SUCCESS; + + ICatalogObject* piAppObj = NULL; + + for (CPI_APPLICATION* pItm = pList->pFirst; pItm; pItm = pItm->pNext) + { + // referenced locaters or applications that are being installed + if (!pItm->fReferencedForInstall && !(pItm->fHasComponent && WcaIsInstalling(pItm->isInstalled, pItm->isAction))) + continue; + + // if the application is referensed and is not a locater, it must be installed + if (pItm->fReferencedForInstall && pItm->fHasComponent && !CpiWillBeInstalled(pItm->isInstalled, pItm->isAction)) + MessageExitOnFailure(hr = E_FAIL, msierrComPlusApplicationDependency, "An application is used by another entity being installed, but is not installed itself, key: %S", pItm->wzKey); + + // application is supposed to exist + if (!pItm->fHasComponent || CpiIsInstalled(pItm->isInstalled)) + { + // get collection object for application + hr = FindObjectForApplication(pItm, 0 != *pItm->wzID, 0 == *pItm->wzID, &piAppObj); + ExitOnFailure(hr, "Failed to find collection object for application"); + + // if the application was found + if (S_OK == hr) + { + // if we don't have an id, copy id from object + if (!*pItm->wzID) + { + hr = CpiGetKeyForObject(piAppObj, pItm->wzID, countof(pItm->wzID)); + ExitOnFailure(hr, "Failed to get id"); + } + } + + // if the application was not found + else + { + // if the application is a locater, this is an error + if (!pItm->fHasComponent) + MessageExitOnFailure(hr = HRESULT_FROM_WIN32(ERROR_NOT_FOUND), msierrComPlusApplicationNotFound, "An application required by this installation was not found, key: %S", pItm->wzKey); + + // create a new id if one is missing + if (!*pItm->wzID) + { + hr = CpiCreateId(pItm->wzID, countof(pItm->wzID)); + ExitOnFailure(hr, "Failed to create id"); + } + } + } + + // application is supposed to be created + else + { + // check for conflicts + do { + if (*pItm->wzID) + { + // find applications with conflicting id + hr = FindObjectForApplication(pItm, TRUE, FALSE, &piAppObj); + ExitOnFailure(hr, "Failed to find collection object for application"); + + if (S_FALSE == hr) + { + // find applications with conflicting name + hr = FindObjectForApplication(pItm, FALSE, TRUE, &piAppObj); + ExitOnFailure(hr, "Failed to find collection object for application"); + + if (S_OK == hr) + // "A application with a conflictiong name exists. retry cancel" + er = WcaErrorMessage(msierrComPlusApplicationNameConflict, hr, INSTALLMESSAGE_ERROR | MB_RETRYCANCEL, 0); + else + break; // no conflicting entry found, break loop + } + else + // "A application with a conflicting id exists. abort retry ignore" + er = WcaErrorMessage(msierrComPlusApplicationIdConflict, hr, INSTALLMESSAGE_ERROR | MB_ABORTRETRYIGNORE, 0); + } + else + { + // find applications with conflicting name + hr = FindObjectForApplication(pItm, FALSE, TRUE, &piAppObj); + ExitOnFailure(hr, "Failed to find collection object for application"); + + if (S_OK == hr) + // "A subscription with a conflictiong name exists. abort retry ignore" + er = WcaErrorMessage(msierrComPlusApplicationNameConflict, hr, INSTALLMESSAGE_ERROR | MB_ABORTRETRYIGNORE, 0); + else + break; // no conflicting entry found, break loop + } + + switch (er) + { + case IDCANCEL: + case IDABORT: + ExitOnFailure(hr = E_FAIL, "An application with a conflictiong name or id exists, key: %S", pItm->wzKey); + break; + case IDRETRY: + break; + case IDIGNORE: + default: + // if we don't have an id, copy id from object + if (!*pItm->wzID) + { + hr = CpiGetKeyForObject(piAppObj, pItm->wzID, countof(pItm->wzID)); + ExitOnFailure(hr, "Failed to get id"); + } + hr = S_FALSE; // indicate that this is not a conflict + } + } while (S_OK == hr); // hr = S_FALSE if we don't have any conflicts + + // create a new id if one is missing + if (!*pItm->wzID) + { + hr = CpiCreateId(pItm->wzID, countof(pItm->wzID)); + ExitOnFailure(hr, "Failed to create id"); + } + } + + // clean up + ReleaseNullObject(piAppObj); + } + + hr = S_OK; + +LExit: + // clean up + ReleaseObject(piAppObj); + + return hr; +} + +HRESULT CpiApplicationsVerifyUninstall( + CPI_APPLICATION_LIST* pList + ) +{ + HRESULT hr = S_OK; + ICatalogObject* piAppObj = NULL; + + for (CPI_APPLICATION* pItm = pList->pFirst; pItm; pItm = pItm->pNext) + { + // referenced locaters or applications that are being installed + if (!pItm->fReferencedForUninstall && !(pItm->fHasComponent && WcaIsUninstalling(pItm->isInstalled, pItm->isAction))) + continue; + + // get collection object for application + hr = FindObjectForApplication(pItm, 0 != *pItm->wzID, 0 == *pItm->wzID, &piAppObj); + ExitOnFailure(hr, "Failed to find collection object for application"); + + // if the application was found + if (S_OK == hr) + { + // if we don't have an id, copy id from object + if (!*pItm->wzID) + { + hr = CpiGetKeyForObject(piAppObj, pItm->wzID, countof(pItm->wzID)); + ExitOnFailure(hr, "Failed to get id"); + } + } + + // if the application was not found + else + { + pItm->fObjectNotFound = TRUE; + if (pItm->fHasComponent) + pList->iUninstallCount--; // elements with the fObjectNotFound flag set will not be scheduled for uninstall + } + + // clean up + ReleaseNullObject(piAppObj); + } + + hr = S_OK; + +LExit: + // clean up + ReleaseObject(piAppObj); + + return hr; +} + +void CpiApplicationAddReferenceInstall( + CPI_APPLICATION* pItm + ) +{ + pItm->fReferencedForInstall = TRUE; + if (pItm->pPartition) + CpiPartitionAddReferenceInstall(pItm->pPartition); +} + +void CpiApplicationAddReferenceUninstall( + CPI_APPLICATION* pItm + ) +{ + pItm->fReferencedForUninstall = TRUE; + if (pItm->pPartition) + CpiPartitionAddReferenceUninstall(pItm->pPartition); +} + +HRESULT CpiApplicationsInstall( + CPI_APPLICATION_LIST* pList, + int iRunMode, + LPWSTR* ppwzActionData, + int* piProgress + ) +{ + HRESULT hr = S_OK; + + int iActionType; + + // add action text + hr = CpiAddActionTextToActionData(L"CreateComPlusApplications", ppwzActionData); + ExitOnFailure(hr, "Failed to add action text to custom action data"); + + // add applicaton count to action data + hr = WcaWriteIntegerToCaData(pList->iInstallCount, ppwzActionData); + ExitOnFailure(hr, "Failed to add count to custom action data"); + + // add applications to custom action data + for (CPI_APPLICATION* pItm = pList->pFirst; pItm; pItm = pItm->pNext) + { + // applications that are being installed only + if (!pItm->fHasComponent || !WcaIsInstalling(pItm->isInstalled, pItm->isAction)) + continue; + + // action type + if (rmRollback == iRunMode) + { + if (CpiIsInstalled(pItm->isInstalled)) + iActionType = atNoOp; + else + iActionType = atRemove; + } + else + iActionType = atCreate; + + // add to action data + hr = AddApplicationToActionData(pItm, iActionType, COST_APPLICATION_CREATE, ppwzActionData); + ExitOnFailure(hr, "Failed to add applicaton to custom action data, key: %S", pItm->wzKey); + } + + // add progress tics + if (piProgress) + *piProgress += COST_APPLICATION_CREATE * pList->iInstallCount; + + hr = S_OK; + +LExit: + return hr; +} + +HRESULT CpiApplicationsUninstall( + CPI_APPLICATION_LIST* pList, + int iRunMode, + LPWSTR* ppwzActionData, + int* piProgress + ) +{ + HRESULT hr = S_OK; + + int iActionType; + + // add action text + hr = CpiAddActionTextToActionData(L"RemoveComPlusApplications", ppwzActionData); + ExitOnFailure(hr, "Failed to add action text to custom action data"); + + // add applicaton count to action data + hr = WcaWriteIntegerToCaData(pList->iUninstallCount, ppwzActionData); + ExitOnFailure(hr, "Failed to add count to custom action data"); + + // add applications to custom action data + for (CPI_APPLICATION* pItm = pList->pFirst; pItm; pItm = pItm->pNext) + { + // applications that are being uninstalled only + if (!pItm->fHasComponent || !WcaIsUninstalling(pItm->isInstalled, pItm->isAction)) + continue; + + // action type + if (rmRollback == iRunMode) + iActionType = atCreate; + else + iActionType = atRemove; + + // add to action data + hr = AddApplicationToActionData(pItm, iActionType, COST_APPLICATION_DELETE, ppwzActionData); + ExitOnFailure(hr, "Failed to add applicaton to custom action data, key: %S", pItm->wzKey); + } + + // add progress tics + if (piProgress) + *piProgress += COST_APPLICATION_DELETE * pList->iUninstallCount; + + hr = S_OK; + +LExit: + return hr; +} + +HRESULT CpiApplicationFindByKey( + CPI_APPLICATION_LIST* pList, + LPCWSTR pwzKey, + CPI_APPLICATION** ppApp + ) +{ + for (CPI_APPLICATION* pItm = pList->pFirst; pItm; pItm = pItm->pNext) + { + if (0 == lstrcmpW(pItm->wzKey, pwzKey)) + { + *ppApp = pItm; + return S_OK; + } + } + + return S_FALSE; +} + +HRESULT CpiGetRolesCollForApplication( + CPI_APPLICATION* pApp, + ICatalogCollection** ppiRolesColl + ) +{ + HRESULT hr = S_OK; + + ICatalogCollection* piAppColl = NULL; + ICatalogObject* piAppObj = NULL; + + // if a previous attempt to locate the collection object failed + if (pApp->fObjectNotFound) + ExitFunction1(hr = S_FALSE); + + // get applications collection + if (!pApp->piRolesColl) + { + // get applications collection + if (pApp->pPartition) + hr = CpiGetApplicationsCollForPartition(pApp->pPartition, &piAppColl); + else + hr = CpiGetApplicationsCollection(&piAppColl); + ExitOnFailure(hr, "Failed to get applications collection"); + + if (S_FALSE == hr) + ExitFunction(); // exit with hr = S_FALSE + + // find application object + hr = CpiFindCollectionObject(piAppColl, pApp->wzID, *pApp->wzID ? NULL : pApp->wzName, &piAppObj); + ExitOnFailure(hr, "Failed to find application object"); + + if (S_FALSE == hr) + ExitFunction(); // exit with hr = S_FALSE + + // get roles collection + hr = CpiGetCatalogCollection(piAppColl, piAppObj, L"Roles", &pApp->piRolesColl); + ExitOnFailure(hr, "Failed to get roles collection"); + } + + // return value + *ppiRolesColl = pApp->piRolesColl; + (*ppiRolesColl)->AddRef(); + + hr = S_OK; + +LExit: + // clean up + ReleaseObject(piAppColl); + ReleaseObject(piAppObj); + + return hr; +} + +HRESULT CpiGetComponentsCollForApplication( + CPI_APPLICATION* pApp, + ICatalogCollection** ppiCompsColl + ) +{ + HRESULT hr = S_OK; + + ICatalogCollection* piAppColl = NULL; + ICatalogObject* piAppObj = NULL; + + // if a previous attempt to locate the collection object failed + if (pApp->fObjectNotFound) + ExitFunction1(hr = S_FALSE); + + // get applications collection + if (!pApp->piCompsColl) + { + // get applications collection + if (pApp->pPartition) + hr = CpiGetApplicationsCollForPartition(pApp->pPartition, &piAppColl); + else + hr = CpiGetApplicationsCollection(&piAppColl); + ExitOnFailure(hr, "Failed to get applications collection"); + + if (S_FALSE == hr) + ExitFunction(); // exit with hr = S_FALSE + + // find application object + hr = CpiFindCollectionObject(piAppColl, pApp->wzID, *pApp->wzID ? NULL : pApp->wzName, &piAppObj); + ExitOnFailure(hr, "Failed to find application object"); + + if (S_FALSE == hr) + ExitFunction(); // exit with hr = S_FALSE + + // get roles collection + hr = CpiGetCatalogCollection(piAppColl, piAppObj, L"Components", &pApp->piCompsColl); + ExitOnFailure(hr, "Failed to get components collection"); + } + + // return value + *ppiCompsColl = pApp->piCompsColl; + (*ppiCompsColl)->AddRef(); + + hr = S_OK; + +LExit: + // clean up + ReleaseObject(piAppColl); + ReleaseObject(piAppObj); + + return hr; +} + + +// helper function definitions + +static void FreeApplication( + CPI_APPLICATION* pItm + ) +{ + if (pItm->pProperties) + CpiPropertiesFreeList(pItm->pProperties); + + ReleaseObject(pItm->piRolesColl); + ReleaseObject(pItm->piCompsColl); + + ::HeapFree(::GetProcessHeap(), 0, pItm); +} + +static HRESULT FindObjectForApplication( + CPI_APPLICATION* pItm, + BOOL fFindId, + BOOL fFindName, + ICatalogObject** ppiAppObj + ) +{ + HRESULT hr = S_OK; + + ICatalogCollection* piAppColl = NULL; + + // get applications collection + if (pItm->pPartition) + hr = CpiGetApplicationsCollForPartition(pItm->pPartition, &piAppColl); + else + hr = CpiGetApplicationsCollection(&piAppColl); + ExitOnFailure(hr, "Failed to get applications collection"); + + if (S_FALSE == hr) + ExitFunction(); // exit with hr = S_FALSE + + // find application object + hr = CpiFindCollectionObject(piAppColl, fFindId ? pItm->wzID : NULL, fFindName ? pItm->wzName : NULL, ppiAppObj); + ExitOnFailure(hr, "Failed to find application object"); + + // exit with hr from CpiFindCollectionObject() + +LExit: + // clean up + ReleaseObject(piAppColl); + + return hr; +} + +static HRESULT AddApplicationToActionData( + CPI_APPLICATION* pItm, + int iActionType, + int iActionCost, + LPWSTR* ppwzActionData + ) +{ + HRESULT hr = S_OK; + + // add action information to custom action data + hr = WcaWriteIntegerToCaData(iActionType, ppwzActionData); + ExitOnFailure(hr, "Failed to add action type to custom action data"); + hr = WcaWriteIntegerToCaData(iActionCost, ppwzActionData); + ExitOnFailure(hr, "Failed to add action cost to custom action data"); + + // add application information to custom action data + hr = WcaWriteStringToCaData(pItm->wzKey, ppwzActionData); + ExitOnFailure(hr, "Failed to add application key to custom action data"); + hr = WcaWriteStringToCaData(pItm->wzID, ppwzActionData); + ExitOnFailure(hr, "Failed to add application id to custom action data"); + hr = WcaWriteStringToCaData(pItm->wzName, ppwzActionData); + ExitOnFailure(hr, "Failed to add application name to custom action data"); + + // add partition information to custom action data + hr = WcaWriteStringToCaData(pItm->pPartition ? pItm->pPartition->wzID : L"", ppwzActionData); + ExitOnFailure(hr, "Failed to add partition id to custom action data"); + + // add properties to custom action data + hr = CpiAddPropertiesToActionData(atCreate == iActionType ? pItm->iPropertyCount : 0, pItm->pProperties, ppwzActionData); + ExitOnFailure(hr, "Failed to add properties to custom action data"); + + hr = S_OK; + +LExit: + return hr; +} diff --git a/src/ca/cpappsched.h b/src/ca/cpappsched.h new file mode 100644 index 00000000..2cd6a0ee --- /dev/null +++ b/src/ca/cpappsched.h @@ -0,0 +1,83 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + + +struct CPI_APPLICATION +{ + WCHAR wzKey[MAX_DARWIN_KEY + 1]; + WCHAR wzID[CPI_MAX_GUID + 1]; + WCHAR wzName[MAX_DARWIN_COLUMN + 1]; + + int iPropertyCount; + CPI_PROPERTY* pProperties; + + BOOL fHasComponent; + BOOL fReferencedForInstall; + BOOL fReferencedForUninstall; + BOOL fObjectNotFound; + + INSTALLSTATE isInstalled, isAction; + + CPI_PARTITION* pPartition; + + ICatalogCollection* piRolesColl; + ICatalogCollection* piCompsColl; + + CPI_APPLICATION* pNext; +}; + +struct CPI_APPLICATION_LIST +{ + CPI_APPLICATION* pFirst; + + int iInstallCount; + int iUninstallCount; +}; + + +// function prototypes + +void CpiApplicationListFree( + CPI_APPLICATION_LIST* pList + ); +HRESULT CpiApplicationsRead( + CPI_PARTITION_LIST* pPartList, + CPI_APPLICATION_LIST* pAppList + ); +HRESULT CpiApplicationsVerifyInstall( + CPI_APPLICATION_LIST* pList + ); +HRESULT CpiApplicationsVerifyUninstall( + CPI_APPLICATION_LIST* pList + ); +void CpiApplicationAddReferenceInstall( + CPI_APPLICATION* pItm + ); +void CpiApplicationAddReferenceUninstall( + CPI_APPLICATION* pItm + ); +HRESULT CpiApplicationsInstall( + CPI_APPLICATION_LIST* pList, + int iRunMode, + LPWSTR* ppwzActionData, + int* piProgress + ); +HRESULT CpiApplicationsUninstall( + CPI_APPLICATION_LIST* pList, + int iRunMode, + LPWSTR* ppwzActionData, + int* piProgress + ); +HRESULT CpiApplicationFindByKey( + CPI_APPLICATION_LIST* pList, + LPCWSTR pwzKey, + CPI_APPLICATION** ppApp + ); +HRESULT CpiGetRolesCollForApplication( + CPI_APPLICATION* pApp, + ICatalogCollection** ppiRolesColl + ); +HRESULT CpiGetComponentsCollForApplication( + CPI_APPLICATION* pApp, + ICatalogCollection** ppiCompsColl + ); diff --git a/src/ca/cpasmexec.cpp b/src/ca/cpasmexec.cpp new file mode 100644 index 00000000..339c08e1 --- /dev/null +++ b/src/ca/cpasmexec.cpp @@ -0,0 +1,1888 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + + +// GAC related declarations + +typedef struct _FUSION_INSTALL_REFERENCE_ +{ + DWORD cbSize; + DWORD dwFlags; + GUID guidScheme; + LPCWSTR szIdentifier; + LPCWSTR szNonCannonicalData; +} FUSION_INSTALL_REFERENCE; + +typedef struct _FUSION_INSTALL_REFERENCE_ *LPFUSION_INSTALL_REFERENCE; + +typedef const FUSION_INSTALL_REFERENCE *LPCFUSION_INSTALL_REFERENCE; + +typedef struct _ASSEMBLY_INFO +{ + ULONG cbAssemblyInfo; + DWORD dwAssemblyFlags; + ULARGE_INTEGER uliAssemblySizeInKB; + LPWSTR pszCurrentAssemblyPathBuf; + ULONG cchBuf; +} ASSEMBLY_INFO; + +typedef interface IAssemblyCacheItem IAssemblyCacheItem; + +MIDL_INTERFACE("e707dcde-d1cd-11d2-bab9-00c04f8eceae") +IAssemblyCache : public IUnknown +{ +public: + virtual HRESULT STDMETHODCALLTYPE UninstallAssembly( + /* [in] */ DWORD dwFlags, + /* [in] */ LPCWSTR pszAssemblyName, + /* [in] */ LPCFUSION_INSTALL_REFERENCE pRefData, + /* [optional][out] */ ULONG *pulDisposition) = 0; + + virtual HRESULT STDMETHODCALLTYPE QueryAssemblyInfo( + /* [in] */ DWORD dwFlags, + /* [in] */ LPCWSTR pszAssemblyName, + /* [out][in] */ ASSEMBLY_INFO *pAsmInfo) = 0; + + virtual HRESULT STDMETHODCALLTYPE CreateAssemblyCacheItem( + /* [in] */ DWORD dwFlags, + /* [in] */ PVOID pvReserved, + /* [out] */ IAssemblyCacheItem **ppAsmItem, + /* [optional][in] */ LPCWSTR pszAssemblyName) = 0; + + virtual HRESULT STDMETHODCALLTYPE CreateAssemblyScavenger( + /* [out] */ IUnknown **ppUnkReserved) = 0; + + virtual HRESULT STDMETHODCALLTYPE InstallAssembly( + /* [in] */ DWORD dwFlags, + /* [in] */ LPCWSTR pszManifestFilePath, + /* [in] */ LPCFUSION_INSTALL_REFERENCE pRefData) = 0; +}; + +typedef HRESULT (__stdcall *LoadLibraryShimFunc)(LPCWSTR szDllName, LPCWSTR szVersion, LPVOID pvReserved, HMODULE *phModDll); +typedef HRESULT (__stdcall *CreateAssemblyCacheFunc)(IAssemblyCache **ppAsmCache, DWORD dwReserved); + + +// RegistrationHelper related declarations + +static const GUID CLSID_RegistrationHelper = + { 0x89a86e7b, 0xc229, 0x4008, { 0x9b, 0xaa, 0x2f, 0x5c, 0x84, 0x11, 0xd7, 0xe0 } }; + +enum eInstallationFlags { + ifConfigureComponentsOnly = 16, + ifFindOrCreateTargetApplication = 4, + ifExpectExistingTypeLib = 1 +}; + + +// private constants + +enum eAssemblyAttributes +{ + aaEventClass = (1 << 0), + aaDotNetAssembly = (1 << 1), + aaPathFromGAC = (1 << 2), + aaRunInCommit = (1 << 3) +}; + + +// private structs + +struct CPI_ROLE_ASSIGNMENT +{ + WCHAR wzKey[MAX_DARWIN_KEY + 1]; + WCHAR wzRoleName[MAX_DARWIN_COLUMN + 1]; + + CPI_ROLE_ASSIGNMENT* pNext; +}; + +struct CPI_METHOD +{ + WCHAR wzIndex[11 + 1]; + WCHAR wzName[MAX_DARWIN_COLUMN + 1]; + + CPI_PROPERTY* pPropertyList; + CPI_ROLE_ASSIGNMENT* pRoleAssignmentList; + + CPI_METHOD* pNext; +}; + +struct CPI_INTERFACE +{ + WCHAR wzIID[CPI_MAX_GUID + 1]; + + CPI_PROPERTY* pPropertyList; + CPI_ROLE_ASSIGNMENT* pRoleAssignmentList; + CPI_METHOD* pMethodList; + + CPI_INTERFACE* pNext; +}; + +struct CPI_COMPONENT +{ + WCHAR wzCLSID[CPI_MAX_GUID + 1]; + + CPI_PROPERTY* pPropertyList; + CPI_ROLE_ASSIGNMENT* pRoleAssignmentList; + CPI_INTERFACE* pInterfaceList; + + CPI_COMPONENT* pNext; +}; + +struct CPI_ASSEMBLY_ATTRIBUTES +{ + int iActionType; + int iActionCost; + LPWSTR pwzKey; + LPWSTR pwzAssemblyName; + LPWSTR pwzDllPath; + LPWSTR pwzTlbPath; + LPWSTR pwzPSDllPath; + LPWSTR pwzAppID; + LPWSTR pwzPartID; + int iAttributes; + CPI_COMPONENT* pCompList; +}; + +struct CPI_ROLE_ASSIGNMENTS_ATTRIBUTES +{ + int iActionType; + int iActionCost; + LPWSTR pwzKey; + LPWSTR pwzAppID; + LPWSTR pwzPartID; + int iRoleCount; + CPI_COMPONENT* pCompList; +}; + + +// prototypes for private helper functions + +static HRESULT RegisterAssembly( + CPI_ASSEMBLY_ATTRIBUTES* pAttrs + ); +static HRESULT UnregisterAssembly( + CPI_ASSEMBLY_ATTRIBUTES* pAttrs + ); +static void InitAssemblyExec(); +static void UninitAssemblyExec(); +static HRESULT GetRegistrationHelper( + IDispatch** ppiRegHlp + ); +static HRESULT GetAssemblyCacheObject( + IAssemblyCache** ppAssemblyCache + ); +static HRESULT GetAssemblyPathFromGAC( + LPCWSTR pwzAssemblyName, + LPWSTR* ppwzAssemblyPath + ); +static HRESULT RegisterDotNetAssembly( + CPI_ASSEMBLY_ATTRIBUTES* pAttrs + ); +static HRESULT RegisterNativeAssembly( + CPI_ASSEMBLY_ATTRIBUTES* pAttrs + ); +static HRESULT UnregisterDotNetAssembly( + CPI_ASSEMBLY_ATTRIBUTES* pAttrs + ); +static HRESULT RemoveComponents( + ICatalogCollection* piCompColl, + CPI_COMPONENT* pCompList + ); +static HRESULT ReadAssemblyAttributes( + LPWSTR* ppwzData, + CPI_ASSEMBLY_ATTRIBUTES* pAttrs + ); +static void FreeAssemblyAttributes( + CPI_ASSEMBLY_ATTRIBUTES* pAttrs + ); +static HRESULT ReadRoleAssignmentsAttributes( + LPWSTR* ppwzData, + CPI_ROLE_ASSIGNMENTS_ATTRIBUTES* pAttrs + ); +static void FreeRoleAssignmentsAttributes( + CPI_ROLE_ASSIGNMENTS_ATTRIBUTES* pAttrs + ); +static HRESULT ConfigureComponents( + LPCWSTR pwzPartID, + LPCWSTR pwzAppID, + CPI_COMPONENT* pCompList, + BOOL fCreate, + BOOL fProgress + ); +static HRESULT ConfigureInterfaces( + ICatalogCollection* piCompColl, + ICatalogObject* piCompObj, + CPI_INTERFACE* pIntfList, + BOOL fCreate + ); +static HRESULT ConfigureMethods( + ICatalogCollection* piIntfColl, + ICatalogObject* piIntfObj, + CPI_METHOD* pMethList, + BOOL fCreate + ); +static HRESULT ConfigureRoleAssignments( + LPCWSTR pwzCollName, + ICatalogCollection* piCompColl, + ICatalogObject* piCompObj, + CPI_ROLE_ASSIGNMENT* pRoleList, + BOOL fCreate + ); +static HRESULT ReadComponentList( + LPWSTR* ppwzData, + CPI_COMPONENT** ppCompList + ); +static HRESULT ReadInterfaceList( + LPWSTR* ppwzData, + CPI_INTERFACE** ppIntfList + ); +static HRESULT ReadMethodList( + LPWSTR* ppwzData, + CPI_METHOD** ppMethList + ); +static HRESULT ReadRoleAssignmentList( + LPWSTR* ppwzData, + CPI_ROLE_ASSIGNMENT** ppRoleList + ); +static void FreeComponentList( + CPI_COMPONENT* pList + ); +static void FreeInterfaceList( + CPI_INTERFACE* pList + ); +static void FreeMethodList( + CPI_METHOD* pList + ); +static void FreeRoleAssignmentList( + CPI_ROLE_ASSIGNMENT* pList + ); + + +// variables + +static IDispatch* gpiRegHlp; +static IAssemblyCache* gpAssemblyCache; +static HMODULE ghMscoree; +static HMODULE ghFusion; + + +// function definitions + +HRESULT CpiConfigureAssemblies( + LPWSTR* ppwzData, + HANDLE hRollbackFile + ) +{ + HRESULT hr = S_OK; + + CPI_ASSEMBLY_ATTRIBUTES attrs; + ::ZeroMemory(&attrs, sizeof(attrs)); + + // initialize + InitAssemblyExec(); + + // read action text + hr = CpiActionStartMessage(ppwzData, FALSE); + ExitOnFailure(hr, "Failed to send action start message"); + + // get count + int iCnt = 0; + hr = WcaReadIntegerFromCaData(ppwzData, &iCnt); + ExitOnFailure(hr, "Failed to read count"); + + // write count to rollback file + hr = CpiWriteIntegerToRollbackFile(hRollbackFile, iCnt); + ExitOnFailure(hr, "Failed to write count to rollback file"); + + for (int i = 0; i < iCnt; i++) + { + // read attributes from CustomActionData + hr = ReadAssemblyAttributes(ppwzData, &attrs); + ExitOnFailure(hr, "Failed to read assembly attributes"); + + // write key to rollback file + hr = CpiWriteKeyToRollbackFile(hRollbackFile, attrs.pwzKey); + ExitOnFailure(hr, "Failed to write key to rollback file"); + + // action + switch (attrs.iActionType) + { + case atCreate: + hr = RegisterAssembly(&attrs); + ExitOnFailure(hr, "Failed to register assembly, key: %S", attrs.pwzKey); + break; + case atRemove: + hr = UnregisterAssembly(&attrs); + ExitOnFailure(hr, "Failed to unregister assembly, key: %S", attrs.pwzKey); + break; + default: + hr = S_OK; + break; + } + + if (S_FALSE == hr) + ExitFunction(); // aborted by user + + // write completion status to rollback file + hr = CpiWriteIntegerToRollbackFile(hRollbackFile, 1); + ExitOnFailure(hr, "Failed to write completion status to rollback file"); + + // progress + hr = WcaProgressMessage(attrs.iActionCost, FALSE); + ExitOnFailure(hr, "Failed to update progress"); + } + + hr = S_OK; + +LExit: + // clean up + FreeAssemblyAttributes(&attrs); + + // uninitialize + UninitAssemblyExec(); + + return hr; +} + +HRESULT CpiRollbackConfigureAssemblies( + LPWSTR* ppwzData, + CPI_ROLLBACK_DATA* pRollbackDataList + ) +{ + HRESULT hr = S_OK; + + int iRollbackStatus; + + CPI_ASSEMBLY_ATTRIBUTES attrs; + ::ZeroMemory(&attrs, sizeof(attrs)); + + // initialize + InitAssemblyExec(); + + // read action text + hr = CpiActionStartMessage(ppwzData, NULL == pRollbackDataList); + ExitOnFailure(hr, "Failed to send action start message"); + + // get count + int iCnt = 0; + hr = WcaReadIntegerFromCaData(ppwzData, &iCnt); + ExitOnFailure(hr, "Failed to read count"); + + for (int i = 0; i < iCnt; i++) + { + // read attributes from CustomActionData + hr = ReadAssemblyAttributes(ppwzData, &attrs); + ExitOnFailure(hr, "Failed to read assembly attributes"); + + // rollback status + hr = CpiFindRollbackStatus(pRollbackDataList, attrs.pwzKey, &iRollbackStatus); + + if (S_FALSE == hr) + continue; // not found, nothing to rollback + + // action + switch (attrs.iActionType) + { + case atCreate: + hr = RegisterAssembly(&attrs); + if (FAILED(hr)) + WcaLog(LOGMSG_STANDARD, "Failed to register assembly, hr: 0x%x, key: %S", hr, attrs.pwzKey); + break; + case atRemove: + hr = UnregisterAssembly(&attrs); + if (FAILED(hr)) + WcaLog(LOGMSG_STANDARD, "Failed to unregister assembly, hr: 0x%x, key: %S", hr, attrs.pwzKey); + break; + } + + // check rollback status + if (0 == iRollbackStatus) + continue; // operation did not complete, skip progress + + // progress + hr = WcaProgressMessage(attrs.iActionCost, FALSE); + ExitOnFailure(hr, "Failed to update progress"); + } + + hr = S_OK; + +LExit: + // clean up + FreeAssemblyAttributes(&attrs); + + // uninitialize + UninitAssemblyExec(); + + return hr; +} + +HRESULT CpiConfigureRoleAssignments( + LPWSTR* ppwzData, + HANDLE hRollbackFile + ) +{ + HRESULT hr = S_OK; + + CPI_ROLE_ASSIGNMENTS_ATTRIBUTES attrs; + ::ZeroMemory(&attrs, sizeof(attrs)); + + // read action text + hr = CpiActionStartMessage(ppwzData, FALSE); + ExitOnFailure(hr, "Failed to send action start message"); + + // get count + int iCnt = 0; + hr = WcaReadIntegerFromCaData(ppwzData, &iCnt); + ExitOnFailure(hr, "Failed to read count"); + + // write count to rollback file + hr = CpiWriteIntegerToRollbackFile(hRollbackFile, iCnt); + ExitOnFailure(hr, "Failed to write count to rollback file"); + + for (int i = 0; i < iCnt; i++) + { + // read attributes from CustomActionData + hr = ReadRoleAssignmentsAttributes(ppwzData, &attrs); + ExitOnFailure(hr, "Failed to read role assignments attributes"); + + // write key to rollback file + hr = CpiWriteKeyToRollbackFile(hRollbackFile, attrs.pwzKey); + ExitOnFailure(hr, "Failed to write key to rollback file"); + + // action + if (atNoOp != attrs.iActionType) + { + hr = ConfigureComponents(attrs.pwzPartID, attrs.pwzAppID, attrs.pCompList, atCreate == attrs.iActionType, TRUE); + ExitOnFailure(hr, "Failed to configure components"); + + if (S_FALSE == hr) + ExitFunction(); // aborted by user + } + + // write completion status to rollback file + hr = CpiWriteIntegerToRollbackFile(hRollbackFile, 1); + ExitOnFailure(hr, "Failed to write completion status to rollback file"); + + // progress + hr = WcaProgressMessage(attrs.iActionCost * attrs.iRoleCount, FALSE); + ExitOnFailure(hr, "Failed to update progress"); + } + + hr = S_OK; + +LExit: + // clean up + FreeRoleAssignmentsAttributes(&attrs); + + return hr; +} + +HRESULT CpiRollbackConfigureRoleAssignments( + LPWSTR* ppwzData, + CPI_ROLLBACK_DATA* pRollbackDataList + ) +{ + HRESULT hr = S_OK; + + int iRollbackStatus; + + CPI_ROLE_ASSIGNMENTS_ATTRIBUTES attrs; + ::ZeroMemory(&attrs, sizeof(attrs)); + + // read action text + hr = CpiActionStartMessage(ppwzData, NULL == pRollbackDataList); + ExitOnFailure(hr, "Failed to send action start message"); + + // get count + int iCnt = 0; + hr = WcaReadIntegerFromCaData(ppwzData, &iCnt); + ExitOnFailure(hr, "Failed to read count"); + + for (int i = 0; i < iCnt; i++) + { + // read attributes from CustomActionData + hr = ReadRoleAssignmentsAttributes(ppwzData, &attrs); + ExitOnFailure(hr, "Failed to read role assignments attributes"); + + // rollback status + hr = CpiFindRollbackStatus(pRollbackDataList, attrs.pwzKey, &iRollbackStatus); + + if (S_FALSE == hr) + continue; // not found, nothing to rollback + + // action + if (atNoOp != attrs.iActionType) + { + hr = ConfigureComponents(attrs.pwzPartID, attrs.pwzAppID, attrs.pCompList, atCreate == attrs.iActionType, TRUE); + ExitOnFailure(hr, "Failed to configure components"); + + if (S_FALSE == hr) + ExitFunction(); // aborted by user + } + + // check rollback status + if (0 == iRollbackStatus) + continue; // operation did not complete, skip progress + + // progress + hr = WcaProgressMessage(attrs.iActionCost * attrs.iRoleCount, FALSE); + ExitOnFailure(hr, "Failed to update progress"); + } + + hr = S_OK; + +LExit: + // clean up + FreeRoleAssignmentsAttributes(&attrs); + + return hr; +} + + +// helper function definitions + +static HRESULT RegisterAssembly( + CPI_ASSEMBLY_ATTRIBUTES* pAttrs + ) +{ + HRESULT hr = S_OK; + + // progress message + hr = CpiActionDataMessage(1, (pAttrs->iAttributes & aaPathFromGAC) ? pAttrs->pwzAssemblyName : pAttrs->pwzDllPath); + ExitOnFailure(hr, "Failed to send progress messages"); + + if (S_FALSE == hr) + ExitFunction(); // aborted by user + + // log + WcaLog(LOGMSG_VERBOSE, "Registering assembly, key: %S", pAttrs->pwzKey); + + // extract path from GAC + if (pAttrs->iAttributes & aaPathFromGAC) + { + hr = GetAssemblyPathFromGAC(pAttrs->pwzAssemblyName, &pAttrs->pwzDllPath); + if (S_FALSE == hr) + hr = HRESULT_FROM_WIN32(ERROR_NOT_FOUND); + ExitOnFailure(hr, "Failed to get path for assembly from GAC"); + + // log + WcaLog(LOGMSG_VERBOSE, "Assembly path extracted from GAC, key: %S, path: '%S'", pAttrs->pwzKey, pAttrs->pwzDllPath); + } + + // .net assembly + if (pAttrs->iAttributes & aaDotNetAssembly) + { + hr = RegisterDotNetAssembly(pAttrs); + ExitOnFailure(hr, "Failed to register .NET assembly"); + } + + // native assembly + else + { + hr = RegisterNativeAssembly(pAttrs); + ExitOnFailure(hr, "Failed to register native assembly"); + } + + // configure components + if (pAttrs->pCompList) + { + hr = ConfigureComponents(pAttrs->pwzPartID, pAttrs->pwzAppID, pAttrs->pCompList, TRUE, FALSE); + ExitOnFailure(hr, "Failed to configure components"); + } + + hr = S_OK; + +LExit: + return hr; +} + +static HRESULT UnregisterAssembly( + CPI_ASSEMBLY_ATTRIBUTES* pAttrs + ) +{ + HRESULT hr = S_OK; + + long lChanges = 0; + + ICatalogCollection* piColl = NULL; + ICatalogObject* piObj = NULL; + + // progress message + hr = CpiActionDataMessage(1, (pAttrs->iAttributes & aaPathFromGAC) ? pAttrs->pwzAssemblyName : pAttrs->pwzDllPath); + ExitOnFailure(hr, "Failed to send progress messages"); + + if (S_FALSE == hr) + ExitFunction(); // aborted by user + + // log + WcaLog(LOGMSG_VERBOSE, "Unregistering assembly, key: %S", pAttrs->pwzKey); + + // extract path from GAC + if (pAttrs->iAttributes & aaPathFromGAC) + { + hr = GetAssemblyPathFromGAC(pAttrs->pwzAssemblyName, &pAttrs->pwzDllPath); + ExitOnFailure(hr, "Failed to get path for assembly from GAC"); + + if (S_FALSE == hr) + { + WcaLog(LOGMSG_VERBOSE, "Unable to locate assembly in GAC, assembly will not be unregistered from COM+, key: %S", pAttrs->pwzKey); + ExitFunction1(hr = S_OK); + } + + // log + WcaLog(LOGMSG_VERBOSE, "Assembly path extracted from GAC, key: %S, path: '%S'", pAttrs->pwzKey, pAttrs->pwzDllPath); + } + + // .NET assembly + if (pAttrs->iAttributes & aaDotNetAssembly) + { + if (pAttrs->pwzAppID && *pAttrs->pwzAppID) + { + // When unregistering a .net assembly using the RegistrationHelper class, and the application is + // left empty after all components in the assembly are removed, the RegistrationHelper class also + // attempts to remove the application for some reason. However, it does not handle the situation + // when the application has its deleteable property set to false, and will simply fail if this is + // the case. This is the reason we are clearing the deleatable property of the application here. + // + // TODO: handle rollbacks + + // get applications collection + hr = CpiGetApplicationsCollection(pAttrs->pwzPartID, &piColl); + ExitOnFailure(hr, "Failed to get applications collection"); + + if (S_FALSE == hr) + { + // applications collection not found + WcaLog(LOGMSG_VERBOSE, "Unable to retrieve applications collection, nothing to delete, key: %S", pAttrs->pwzKey); + ExitFunction1(hr = S_OK); + } + + // find application object + hr = CpiFindCollectionObjectByStringKey(piColl, pAttrs->pwzAppID, &piObj); + ExitOnFailure(hr, "Failed to find application object"); + + if (S_FALSE == hr) + { + // application not found + WcaLog(LOGMSG_VERBOSE, "Unable to find application object, nothing to delete, key: %S", pAttrs->pwzKey); + ExitFunction1(hr = S_OK); + } + + // reset deleteable property + hr = CpiResetObjectProperty(piColl, piObj, L"Deleteable"); + ExitOnFailure(hr, "Failed to reset deleteable property"); + } + + // unregister assembly + hr = UnregisterDotNetAssembly(pAttrs); + ExitOnFailure(hr, "Failed to unregister .NET assembly"); + } + + // native assembly + else + { + // get components collection + hr = CpiGetComponentsCollection(pAttrs->pwzPartID, pAttrs->pwzAppID, &piColl); + ExitOnFailure(hr, "Failed to get components collection"); + + if (S_FALSE == hr) + { + // components collection not found + WcaLog(LOGMSG_VERBOSE, "Unable to retrieve components collection, nothing to delete, key: %S", pAttrs->pwzKey); + ExitFunction1(hr = S_OK); + } + + // remove components + hr = RemoveComponents(piColl, pAttrs->pCompList); + ExitOnFailure(hr, "Failed to get remove components"); + + // save changes + hr = piColl->SaveChanges(&lChanges); + if (COMADMIN_E_OBJECTERRORS == hr) + CpiLogCatalogErrorInfo(); + ExitOnFailure(hr, "Failed to save changes"); + } + + hr = S_OK; + +LExit: + // clean up + ReleaseObject(piColl); + ReleaseObject(piObj); + + return hr; +} + +static void InitAssemblyExec() +{ + gpiRegHlp = NULL; + gpAssemblyCache = NULL; + ghMscoree = NULL; + ghFusion = NULL; +} + +static void UninitAssemblyExec() +{ + ReleaseObject(gpiRegHlp); + ReleaseObject(gpAssemblyCache); + if (ghFusion) + ::FreeLibrary(ghFusion); + if (ghMscoree) + ::FreeLibrary(ghMscoree); +} + +static HRESULT GetRegistrationHelper( + IDispatch** ppiRegHlp + ) +{ + HRESULT hr = S_OK; + + if (!gpiRegHlp) + { + // create registration helper object + hr = ::CoCreateInstance(CLSID_RegistrationHelper, NULL, CLSCTX_ALL, IID_IDispatch, (void**)&gpiRegHlp); + ExitOnFailure(hr, "Failed to create registration helper object"); + } + + gpiRegHlp->AddRef(); + *ppiRegHlp = gpiRegHlp; + + hr = S_OK; + +LExit: + return hr; +} + +static HRESULT GetAssemblyCacheObject( + IAssemblyCache** ppAssemblyCache + ) +{ + HRESULT hr = S_OK; + + if (!gpAssemblyCache) + { + // mscoree.dll + if (!ghMscoree) + { + // load mscoree.dll + ghMscoree = ::LoadLibraryW(L"mscoree.dll"); + ExitOnNull(ghMscoree, hr, E_FAIL, "Failed to load mscoree.dll"); + } + + // fusion.dll + if (!ghFusion) + { + // get LoadLibraryShim function address + LoadLibraryShimFunc pfnLoadLibraryShim = (LoadLibraryShimFunc)::GetProcAddress(ghMscoree, "LoadLibraryShim"); + ExitOnNull(pfnLoadLibraryShim, hr, HRESULT_FROM_WIN32(::GetLastError()), "Failed get address for LoadLibraryShim() function"); + + // load fusion.dll + hr = pfnLoadLibraryShim(L"fusion.dll", NULL, NULL, &ghFusion); + ExitOnFailure(hr, "Failed to load fusion.dll"); + } + + // get CreateAssemblyCache function address + CreateAssemblyCacheFunc pfnCreateAssemblyCache = (CreateAssemblyCacheFunc)::GetProcAddress(ghFusion, "CreateAssemblyCache"); + ExitOnNull(pfnCreateAssemblyCache, hr, HRESULT_FROM_WIN32(::GetLastError()), "Failed get address for CreateAssemblyCache() function"); + + // create AssemblyCache object + hr = pfnCreateAssemblyCache(&gpAssemblyCache, 0); + ExitOnFailure(hr, "Failed to create AssemblyCache object"); + } + + gpAssemblyCache->AddRef(); + *ppAssemblyCache = gpAssemblyCache; + + hr = S_OK; + +LExit: + return hr; +} + +static HRESULT GetAssemblyPathFromGAC( + LPCWSTR pwzAssemblyName, + LPWSTR* ppwzAssemblyPath + ) +{ + HRESULT hr = S_OK; + + IAssemblyCache* pAssemblyCache = NULL; + + ASSEMBLY_INFO assemblyInfo; + WCHAR wzPathBuf[MAX_PATH]; + + ::ZeroMemory(&assemblyInfo, sizeof(ASSEMBLY_INFO)); + ::ZeroMemory(wzPathBuf, countof(wzPathBuf)); + + // get AssemblyCache object + hr = GetAssemblyCacheObject(&pAssemblyCache); + ExitOnFailure(hr, "Failed to get AssemblyCache object"); + + // get assembly info + assemblyInfo.cbAssemblyInfo = sizeof(ASSEMBLY_INFO); + assemblyInfo.pszCurrentAssemblyPathBuf = wzPathBuf; + assemblyInfo.cchBuf = countof(wzPathBuf); + + hr = pAssemblyCache->QueryAssemblyInfo(0, pwzAssemblyName, &assemblyInfo); + if (HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND) == hr) + ExitFunction1(hr = S_FALSE); + ExitOnFailure(hr, "Failed to get assembly info"); + + // copy assembly path + hr = StrAllocString(ppwzAssemblyPath, wzPathBuf, 0); + ExitOnFailure(hr, "Failed to copy assembly path"); + + hr = S_OK; + +LExit: + // clean up + ReleaseObject(pAssemblyCache); + + return hr; +} + +static HRESULT RegisterDotNetAssembly( + CPI_ASSEMBLY_ATTRIBUTES* pAttrs + ) +{ + HRESULT hr = S_OK; + + IDispatch* piRegHlp = NULL; + + DISPID dispid; + BSTR bstrMember = NULL; + + long lInstallationFlags = 0; + + VARIANTARG rgvarg[5]; + DISPPARAMS dispparams; + EXCEPINFO excepInfo; + + BSTR bstrPartName = NULL; + BSTR bstrAppName = NULL; + BSTR bstrDllPath = NULL; + BSTR bstrTlbPath = NULL; + + ::ZeroMemory(rgvarg, sizeof(rgvarg)); + ::ZeroMemory(&dispparams, sizeof(dispparams)); + ::ZeroMemory(&excepInfo, sizeof(excepInfo)); + + bstrMember = ::SysAllocString(L"InstallAssembly_2"); + ExitOnNull(bstrMember, hr, E_OUTOFMEMORY, "Failed to allocate BSTR for method name"); + + // create BSTRs for parameters + if (pAttrs->pwzPartID && *pAttrs->pwzPartID) + { + bstrPartName = ::SysAllocString(pAttrs->pwzPartID); + ExitOnNull(bstrPartName, hr, E_OUTOFMEMORY, "Failed to allocate BSTR for partition id"); + } + + if (pAttrs->pwzAppID && *pAttrs->pwzAppID) + { + bstrAppName = ::SysAllocString(pAttrs->pwzAppID); + ExitOnNull(bstrAppName, hr, E_OUTOFMEMORY, "Failed to allocate BSTR for application id"); + } + + bstrDllPath = ::SysAllocString(pAttrs->pwzDllPath); + ExitOnNull(bstrDllPath, hr, E_OUTOFMEMORY, "Failed to allocate BSTR for dll path"); + + if (pAttrs->pwzTlbPath && *pAttrs->pwzTlbPath) + { + bstrTlbPath = ::SysAllocString(pAttrs->pwzTlbPath); + ExitOnNull(bstrTlbPath, hr, E_OUTOFMEMORY, "Failed to allocate BSTR for tlb path"); + } + + // get registration helper object + hr = GetRegistrationHelper(&piRegHlp); + ExitOnFailure(hr, "Failed to get registration helper object"); + + // get dispatch id of InstallAssembly() method + hr = piRegHlp->GetIDsOfNames(IID_NULL, &bstrMember, 1, LOCALE_USER_DEFAULT, &dispid); + ExitOnFailure(hr, "Failed to get dispatch id of InstallAssembly() method"); + + // set installation flags + lInstallationFlags = ifExpectExistingTypeLib; + + if (!bstrAppName) + lInstallationFlags |= ifFindOrCreateTargetApplication; + + // invoke InstallAssembly() method + rgvarg[0].vt = VT_I4; + rgvarg[0].lVal = lInstallationFlags; + rgvarg[1].vt = VT_BYREF|VT_BSTR; + rgvarg[1].pbstrVal = &bstrTlbPath; + rgvarg[2].vt = VT_BSTR; + rgvarg[2].bstrVal = bstrPartName; + rgvarg[3].vt = VT_BYREF|VT_BSTR; + rgvarg[3].pbstrVal = &bstrAppName; + rgvarg[4].vt = VT_BSTR; + rgvarg[4].bstrVal = bstrDllPath; + dispparams.rgvarg = rgvarg; + dispparams.cArgs = 5; + dispparams.cNamedArgs = 0; + + hr = piRegHlp->Invoke(dispid, IID_NULL, LOCALE_USER_DEFAULT, DISPATCH_METHOD, &dispparams, NULL, &excepInfo, NULL); + if (DISP_E_EXCEPTION == hr) + { + // log exception information + if (!excepInfo.pfnDeferredFillIn || (excepInfo.pfnDeferredFillIn && SUCCEEDED(excepInfo.pfnDeferredFillIn(&excepInfo)))) + { + WcaLog(LOGMSG_STANDARD, "ExceptionInfo: Code='%hu', Source='%S', Description='%S', HelpFile='%S', HelpContext='%u'", + excepInfo.wCode, excepInfo.bstrSource, + excepInfo.bstrDescription ? excepInfo.bstrDescription : L"", + excepInfo.bstrHelpFile ? excepInfo.bstrHelpFile : L"", + excepInfo.dwHelpContext); + } + } + ExitOnFailure(hr, "Failed to invoke RegistrationHelper.InstallAssembly() method"); + + hr = S_OK; + +LExit: + // clean up + ReleaseObject(piRegHlp); + + ReleaseBSTR(bstrMember); + + ReleaseBSTR(excepInfo.bstrSource); + ReleaseBSTR(excepInfo.bstrDescription); + ReleaseBSTR(excepInfo.bstrHelpFile); + + ReleaseBSTR(bstrPartName); + ReleaseBSTR(bstrAppName); + ReleaseBSTR(bstrDllPath); + ReleaseBSTR(bstrTlbPath); + + return hr; +} + +static HRESULT RegisterNativeAssembly( + CPI_ASSEMBLY_ATTRIBUTES* pAttrs + ) +{ + HRESULT hr = S_OK; + + ICOMAdminCatalog* piCatalog = NULL; + ICOMAdminCatalog2* piCatalog2 = NULL; + BSTR bstrGlobPartID = NULL; + + BSTR bstrPartID = NULL; + BSTR bstrAppID = NULL; + BSTR bstrDllPath = NULL; + BSTR bstrTlbPath = NULL; + BSTR bstrPSDllPath = NULL; + + // create BSTRs for parameters + if (pAttrs->pwzPartID && *pAttrs->pwzPartID) + { + bstrPartID = ::SysAllocString(pAttrs->pwzPartID); + ExitOnNull(bstrPartID, hr, E_OUTOFMEMORY, "Failed to allocate BSTR for partition id"); + } + + bstrAppID = ::SysAllocString(pAttrs->pwzAppID); + ExitOnNull(bstrAppID, hr, E_OUTOFMEMORY, "Failed to allocate BSTR for application id"); + + bstrDllPath = ::SysAllocString(pAttrs->pwzDllPath); + ExitOnNull(bstrDllPath, hr, E_OUTOFMEMORY, "Failed to allocate BSTR for dll path"); + + bstrTlbPath = ::SysAllocString(pAttrs->pwzTlbPath ? pAttrs->pwzTlbPath : L""); + ExitOnNull(bstrTlbPath, hr, E_OUTOFMEMORY, "Failed to allocate BSTR for tlb path"); + + bstrPSDllPath = ::SysAllocString(pAttrs->pwzPSDllPath ? pAttrs->pwzPSDllPath : L""); + ExitOnNull(bstrPSDllPath, hr, E_OUTOFMEMORY, "Failed to allocate BSTR for tlb path"); + + // get catalog + hr = CpiGetAdminCatalog(&piCatalog); + ExitOnFailure(hr, "Failed to get COM+ admin catalog"); + + // get ICOMAdminCatalog2 interface + hr = piCatalog->QueryInterface(IID_ICOMAdminCatalog2, (void**)&piCatalog2); + + // COM+ 1.5 or later + if (E_NOINTERFACE != hr) + { + ExitOnFailure(hr, "Failed to get IID_ICOMAdminCatalog2 interface"); + + // partition id + if (!bstrPartID) + { + // get global partition id + hr = piCatalog2->get_GlobalPartitionID(&bstrGlobPartID); + ExitOnFailure(hr, "Failed to get global partition id"); + } + + // set current partition + hr = piCatalog2->put_CurrentPartition(bstrPartID ? bstrPartID : bstrGlobPartID); + ExitOnFailure(hr, "Failed to set current partition"); + } + + // COM+ pre 1.5 + else + { + // this version of COM+ does not support partitions, make sure a partition was not specified + if (bstrPartID) + ExitOnFailure(hr = E_FAIL, "Partitions are not supported by this version of COM+"); + } + + // install event classes + if (pAttrs->iAttributes & aaEventClass) + { + hr = piCatalog->InstallEventClass(bstrAppID, bstrDllPath, bstrTlbPath, bstrPSDllPath); + if (COMADMIN_E_OBJECTERRORS == hr) + CpiLogCatalogErrorInfo(); + ExitOnFailure(hr, "Failed to install event classes"); + } + + // install components + else + { + hr = piCatalog->InstallComponent(bstrAppID, bstrDllPath, bstrTlbPath, bstrPSDllPath); + if (COMADMIN_E_OBJECTERRORS == hr) + CpiLogCatalogErrorInfo(); + ExitOnFailure(hr, "Failed to install components"); + } + + hr = S_OK; + +LExit: + // clean up + ReleaseObject(piCatalog); + ReleaseObject(piCatalog2); + ReleaseBSTR(bstrGlobPartID); + + ReleaseBSTR(bstrPartID); + ReleaseBSTR(bstrAppID); + ReleaseBSTR(bstrDllPath); + ReleaseBSTR(bstrTlbPath); + ReleaseBSTR(bstrPSDllPath); + + return hr; +} + +static HRESULT UnregisterDotNetAssembly( + CPI_ASSEMBLY_ATTRIBUTES* pAttrs + ) +{ + HRESULT hr = S_OK; + + IDispatch* piRegHlp = NULL; + + DISPID dispid; + BSTR bstrMember = NULL; + + VARIANTARG rgvarg[3]; + DISPPARAMS dispparams; + EXCEPINFO excepInfo; + + BSTR bstrPartName = NULL; + BSTR bstrAppName = NULL; + BSTR bstrDllPath = NULL; + + ::ZeroMemory(rgvarg, sizeof(rgvarg)); + ::ZeroMemory(&dispparams, sizeof(dispparams)); + ::ZeroMemory(&excepInfo, sizeof(excepInfo)); + + bstrMember = ::SysAllocString(L"UninstallAssembly_2"); + ExitOnNull(bstrMember, hr, E_OUTOFMEMORY, "Failed to allocate BSTR for method name"); + + // create BSTRs for parameters + if (pAttrs->pwzPartID && *pAttrs->pwzPartID) + { + bstrPartName = ::SysAllocString(pAttrs->pwzPartID); + ExitOnNull(bstrPartName, hr, E_OUTOFMEMORY, "Failed to allocate BSTR for partition id"); + } + + bstrAppName = ::SysAllocString(pAttrs->pwzAppID); + ExitOnNull(bstrAppName, hr, E_OUTOFMEMORY, "Failed to allocate BSTR for application id"); + + bstrDllPath = ::SysAllocString(pAttrs->pwzDllPath); + ExitOnNull(bstrDllPath, hr, E_OUTOFMEMORY, "Failed to allocate BSTR for dll path"); + + // get registration helper object + hr = GetRegistrationHelper(&piRegHlp); + ExitOnFailure(hr, "Failed to get registration helper object"); + + // get dispatch id of UninstallAssembly() method + hr = piRegHlp->GetIDsOfNames(IID_NULL, &bstrMember, 1, LOCALE_USER_DEFAULT, &dispid); + ExitOnFailure(hr, "Failed to get dispatch id of UninstallAssembly() method"); + + // invoke UninstallAssembly() method + rgvarg[0].vt = VT_BSTR; + rgvarg[0].bstrVal = bstrPartName; + rgvarg[1].vt = VT_BSTR; + rgvarg[1].bstrVal = bstrAppName; + rgvarg[2].vt = VT_BSTR; + rgvarg[2].bstrVal = bstrDllPath; + dispparams.rgvarg = rgvarg; + dispparams.cArgs = 3; + dispparams.cNamedArgs = 0; + + hr = piRegHlp->Invoke(dispid, IID_NULL, LOCALE_USER_DEFAULT, DISPATCH_METHOD, &dispparams, NULL, &excepInfo, NULL); + if (DISP_E_EXCEPTION == hr) + { + // log exception information + if (!excepInfo.pfnDeferredFillIn || (excepInfo.pfnDeferredFillIn && SUCCEEDED(excepInfo.pfnDeferredFillIn(&excepInfo)))) + { + WcaLog(LOGMSG_STANDARD, "ExceptionInfo: Code='%hu', Source='%S', Description='%S', HelpFile='%S', HelpContext='%u'", + excepInfo.wCode, excepInfo.bstrSource, + excepInfo.bstrDescription ? excepInfo.bstrDescription : L"", + excepInfo.bstrHelpFile ? excepInfo.bstrHelpFile : L"", + excepInfo.dwHelpContext); + } + } + ExitOnFailure(hr, "Failed to invoke RegistrationHelper.UninstallAssembly() method"); + + hr = S_OK; + +LExit: + // clean up + ReleaseObject(piRegHlp); + + ReleaseBSTR(bstrMember); + + ReleaseBSTR(excepInfo.bstrSource); + ReleaseBSTR(excepInfo.bstrDescription); + ReleaseBSTR(excepInfo.bstrHelpFile); + + ReleaseBSTR(bstrPartName); + ReleaseBSTR(bstrAppName); + ReleaseBSTR(bstrDllPath); + + return hr; +} + +static HRESULT RemoveComponents( + ICatalogCollection* piCompColl, + CPI_COMPONENT* pCompList + ) +{ + HRESULT hr = S_OK; + + for (CPI_COMPONENT* pItm = pCompList; pItm; pItm = pItm->pNext) + { + // remove + hr = CpiRemoveCollectionObject(piCompColl, pItm->wzCLSID, NULL, FALSE); + ExitOnFailure(hr, "Failed to remove component"); + + if (S_FALSE == hr) + WcaLog(LOGMSG_VERBOSE, "Component not found, nothing to delete, key: %S", pItm->wzCLSID); + } + + hr = S_OK; + +LExit: + return hr; +} + +static HRESULT ReadAssemblyAttributes( + LPWSTR* ppwzData, + CPI_ASSEMBLY_ATTRIBUTES* pAttrs + ) +{ + HRESULT hr = S_OK; + + // read attributes + hr = WcaReadIntegerFromCaData(ppwzData, &pAttrs->iActionType); + ExitOnFailure(hr, "Failed to read action type"); + hr = WcaReadIntegerFromCaData(ppwzData, &pAttrs->iActionCost); + ExitOnFailure(hr, "Failed to read action cost"); + hr = WcaReadStringFromCaData(ppwzData, &pAttrs->pwzKey); + ExitOnFailure(hr, "Failed to read key"); + hr = WcaReadStringFromCaData(ppwzData, &pAttrs->pwzAssemblyName); + ExitOnFailure(hr, "Failed to read assembly name"); + hr = WcaReadStringFromCaData(ppwzData, &pAttrs->pwzDllPath); + ExitOnFailure(hr, "Failed to read dll path"); + hr = WcaReadStringFromCaData(ppwzData, &pAttrs->pwzTlbPath); + ExitOnFailure(hr, "Failed to read tlb path"); + hr = WcaReadStringFromCaData(ppwzData, &pAttrs->pwzPSDllPath); + ExitOnFailure(hr, "Failed to read proxy-stub dll path"); + hr = WcaReadIntegerFromCaData(ppwzData, &pAttrs->iAttributes); + ExitOnFailure(hr, "Failed to read attributes"); + hr = WcaReadStringFromCaData(ppwzData, &pAttrs->pwzAppID); + ExitOnFailure(hr, "Failed to read application id"); + hr = WcaReadStringFromCaData(ppwzData, &pAttrs->pwzPartID); + ExitOnFailure(hr, "Failed to read partition id"); + + // free existing component list + if (pAttrs->pCompList) + { + FreeComponentList(pAttrs->pCompList); + pAttrs->pCompList = NULL; + } + + // read components + hr = ReadComponentList(ppwzData, &pAttrs->pCompList); + ExitOnFailure(hr, "Failed to read components"); + + hr = S_OK; + +LExit: + return hr; +} + +static void FreeAssemblyAttributes( + CPI_ASSEMBLY_ATTRIBUTES* pAttrs + ) +{ + ReleaseStr(pAttrs->pwzKey); + ReleaseStr(pAttrs->pwzAssemblyName); + ReleaseStr(pAttrs->pwzDllPath); + ReleaseStr(pAttrs->pwzTlbPath); + ReleaseStr(pAttrs->pwzPSDllPath); + ReleaseStr(pAttrs->pwzAppID); + ReleaseStr(pAttrs->pwzPartID); + + if (pAttrs->pCompList) + FreeComponentList(pAttrs->pCompList); +} + +static HRESULT ReadRoleAssignmentsAttributes( + LPWSTR* ppwzData, + CPI_ROLE_ASSIGNMENTS_ATTRIBUTES* pAttrs + ) +{ + HRESULT hr = S_OK; + + // read attributes + hr = WcaReadIntegerFromCaData(ppwzData, &pAttrs->iActionType); + ExitOnFailure(hr, "Failed to read action type"); + hr = WcaReadIntegerFromCaData(ppwzData, &pAttrs->iActionCost); + ExitOnFailure(hr, "Failed to read action cost"); + hr = WcaReadStringFromCaData(ppwzData, &pAttrs->pwzKey); + ExitOnFailure(hr, "Failed to read key"); + hr = WcaReadIntegerFromCaData(ppwzData, &pAttrs->iRoleCount); + ExitOnFailure(hr, "Failed to read role assignments count"); + hr = WcaReadStringFromCaData(ppwzData, &pAttrs->pwzAppID); + ExitOnFailure(hr, "Failed to read application id"); + hr = WcaReadStringFromCaData(ppwzData, &pAttrs->pwzPartID); + ExitOnFailure(hr, "Failed to read partition id"); + + // free existing component list + if (pAttrs->pCompList) + { + FreeComponentList(pAttrs->pCompList); + pAttrs->pCompList = NULL; + } + + // read components + hr = ReadComponentList(ppwzData, &pAttrs->pCompList); + ExitOnFailure(hr, "Failed to read components"); + + hr = S_OK; + +LExit: + return hr; +} + +static void FreeRoleAssignmentsAttributes( + CPI_ROLE_ASSIGNMENTS_ATTRIBUTES* pAttrs + ) +{ + ReleaseStr(pAttrs->pwzKey); + ReleaseStr(pAttrs->pwzAppID); + ReleaseStr(pAttrs->pwzPartID); + + if (pAttrs->pCompList) + FreeComponentList(pAttrs->pCompList); +} + + +static HRESULT ConfigureComponents( + LPCWSTR pwzPartID, + LPCWSTR pwzAppID, + CPI_COMPONENT* pCompList, + BOOL fCreate, + BOOL fProgress + ) +{ + HRESULT hr = S_OK; + + ICatalogCollection* piCompColl = NULL; + ICatalogObject* piCompObj = NULL; + + long lChanges = 0; + + // get components collection + hr = CpiGetComponentsCollection(pwzPartID, pwzAppID, &piCompColl); + if (S_FALSE == hr) + if (fCreate) + hr = HRESULT_FROM_WIN32(ERROR_NOT_FOUND); + else + ExitFunction1(hr = S_OK); + ExitOnFailure(hr, "Failed to get components collection"); + + // read components + for (CPI_COMPONENT* pItm = pCompList; pItm; pItm = pItm->pNext) + { + // progress message + if (fProgress) + { + hr = CpiActionDataMessage(1, pItm->wzCLSID); + ExitOnFailure(hr, "Failed to send progress messages"); + + if (S_FALSE == hr) + ExitFunction(); // aborted by user + } + + // find component + hr = CpiFindCollectionObjectByStringKey(piCompColl, pItm->wzCLSID, &piCompObj); + if (S_FALSE == hr) + if (fCreate) + hr = HRESULT_FROM_WIN32(ERROR_NOT_FOUND); + else + continue; + ExitOnFailure(hr, "Failed to find component object"); + + // properties + hr = CpiPutCollectionObjectValues(piCompObj, pItm->pPropertyList); + ExitOnFailure(hr, "Failed to write properties"); + + // read roles + if (pItm->pRoleAssignmentList) + { + hr = ConfigureRoleAssignments(L"RolesForComponent", piCompColl, piCompObj, pItm->pRoleAssignmentList, fCreate); + ExitOnFailure(hr, "Failed to read roles"); + } + + // read interfaces + if (pItm->pInterfaceList) + { + hr = ConfigureInterfaces(piCompColl, piCompObj, pItm->pInterfaceList, fCreate); + ExitOnFailure(hr, "Failed to read interfaces"); + } + + // clean up + ReleaseNullObject(piCompObj); + } + + // save changes + hr = piCompColl->SaveChanges(&lChanges); + if (COMADMIN_E_OBJECTERRORS == hr) + CpiLogCatalogErrorInfo(); + ExitOnFailure(hr, "Failed to save changes"); + + hr = S_OK; + +LExit: + // clean up + ReleaseObject(piCompColl); + ReleaseObject(piCompObj); + + return hr; +} + +static HRESULT ConfigureInterfaces( + ICatalogCollection* piCompColl, + ICatalogObject* piCompObj, + CPI_INTERFACE* pIntfList, + BOOL fCreate + ) +{ + HRESULT hr = S_OK; + + ICatalogCollection* piIntfColl = NULL; + ICatalogObject* piIntfObj = NULL; + + long lChanges = 0; + + // get interfaces collection + hr = CpiGetInterfacesCollection(piCompColl, piCompObj, &piIntfColl); + if (S_FALSE == hr) + if (fCreate) + hr = HRESULT_FROM_WIN32(ERROR_NOT_FOUND); + else + ExitFunction1(hr = S_OK); + ExitOnFailure(hr, "Failed to get interfaces collection"); + + // read interfaces + for (CPI_INTERFACE* pItm = pIntfList; pItm; pItm = pItm->pNext) + { + // find interface + hr = CpiFindCollectionObjectByStringKey(piIntfColl, pItm->wzIID, &piIntfObj); + if (S_FALSE == hr) + if (fCreate) + hr = HRESULT_FROM_WIN32(ERROR_NOT_FOUND); + else + continue; + ExitOnFailure(hr, "Failed to find interface object"); + + // properties + hr = CpiPutCollectionObjectValues(piIntfObj, pItm->pPropertyList); + ExitOnFailure(hr, "Failed to write properties"); + + // read roles + if (pItm->pRoleAssignmentList) + { + hr = ConfigureRoleAssignments(L"RolesForInterface", piIntfColl, piIntfObj, pItm->pRoleAssignmentList, fCreate); + ExitOnFailure(hr, "Failed to read roles"); + } + + // read methods + if (pItm->pMethodList) + { + hr = ConfigureMethods(piIntfColl, piIntfObj, pItm->pMethodList, fCreate); + ExitOnFailure(hr, "Failed to read methods"); + } + + // clean up + ReleaseNullObject(piIntfObj); + } + + // save changes + hr = piIntfColl->SaveChanges(&lChanges); + if (COMADMIN_E_OBJECTERRORS == hr) + CpiLogCatalogErrorInfo(); + ExitOnFailure(hr, "Failed to save changes"); + + hr = S_OK; + +LExit: + // clean up + ReleaseObject(piIntfColl); + ReleaseObject(piIntfObj); + + return hr; +} + +static HRESULT ConfigureMethods( + ICatalogCollection* piIntfColl, + ICatalogObject* piIntfObj, + CPI_METHOD* pMethList, + BOOL fCreate + ) +{ + HRESULT hr = S_OK; + + ICatalogCollection* piMethColl = NULL; + ICatalogObject* piMethObj = NULL; + + long lChanges = 0; + + // get methods collection + hr = CpiGetMethodsCollection(piIntfColl, piIntfObj, &piMethColl); + if (S_FALSE == hr) + if (fCreate) + hr = HRESULT_FROM_WIN32(ERROR_NOT_FOUND); + else + ExitFunction1(hr = S_OK); + ExitOnFailure(hr, "Failed to get methods collection"); + + // read methods + for (CPI_METHOD* pItm = pMethList; pItm; pItm = pItm->pNext) + { + // find method + if (*pItm->wzIndex) + hr = CpiFindCollectionObjectByIntegerKey(piMethColl, _wtol(pItm->wzIndex), &piMethObj); + else + hr = CpiFindCollectionObjectByName(piMethColl, pItm->wzName, &piMethObj); + + if (S_FALSE == hr) + if (fCreate) + hr = HRESULT_FROM_WIN32(ERROR_NOT_FOUND); + else + continue; + ExitOnFailure(hr, "Failed to find method object"); + + // properties + hr = CpiPutCollectionObjectValues(piMethObj, pItm->pPropertyList); + ExitOnFailure(hr, "Failed to write properties"); + + // read roles + if (pItm->pRoleAssignmentList) + { + hr = ConfigureRoleAssignments(L"RolesForMethod", piMethColl, piMethObj, pItm->pRoleAssignmentList, fCreate); + ExitOnFailure(hr, "Failed to read roles"); + } + + // clean up + ReleaseNullObject(piMethObj); + } + + // save changes + hr = piMethColl->SaveChanges(&lChanges); + if (COMADMIN_E_OBJECTERRORS == hr) + CpiLogCatalogErrorInfo(); + ExitOnFailure(hr, "Failed to save changes"); + + hr = S_OK; + +LExit: + // clean up + ReleaseObject(piMethColl); + ReleaseObject(piMethObj); + + return hr; +} + +static HRESULT ConfigureRoleAssignments( + LPCWSTR pwzCollName, + ICatalogCollection* piCompColl, + ICatalogObject* piCompObj, + CPI_ROLE_ASSIGNMENT* pRoleList, + BOOL fCreate + ) +{ + HRESULT hr = S_OK; + + ICatalogCollection* piRoleColl = NULL; + ICatalogObject* piRoleObj = NULL; + + long lChanges = 0; + + // get roles collection + hr = CpiGetCatalogCollection(piCompColl, piCompObj, pwzCollName, &piRoleColl); + if (S_FALSE == hr) + if (fCreate) + hr = HRESULT_FROM_WIN32(ERROR_NOT_FOUND); + else + ExitFunction1(hr = S_OK); + ExitOnFailure(hr, "Failed to get role assignments collection"); + + // read roles + for (CPI_ROLE_ASSIGNMENT* pItm = pRoleList; pItm; pItm = pItm->pNext) + { + if (fCreate) + { + // find existing role + hr = CpiFindCollectionObjectByName(piRoleColl, pItm->wzRoleName, NULL); + ExitOnFailure(hr, "Failed to find role, key: %S", pItm->wzKey); + + if (S_OK == hr) + continue; // role already exists + + // add object + hr = CpiAddCollectionObject(piRoleColl, &piRoleObj); + ExitOnFailure(hr, "Failed to add role assignment to collection"); + + // role name + hr = CpiPutCollectionObjectValue(piRoleObj, L"Name", pItm->wzRoleName); + ExitOnFailure(hr, "Failed to set role name property, key: %S", pItm->wzKey); + + // clean up + ReleaseNullObject(piRoleObj); + } + else + { + // remove role + hr = CpiRemoveCollectionObject(piRoleColl, NULL, pItm->wzRoleName, FALSE); + ExitOnFailure(hr, "Failed to remove role, key: %S", pItm->wzKey); + } + } + + // save changes + hr = piRoleColl->SaveChanges(&lChanges); + if (COMADMIN_E_OBJECTERRORS == hr) + CpiLogCatalogErrorInfo(); + ExitOnFailure(hr, "Failed to save changes"); + + hr = S_OK; + +LExit: + // clean up + ReleaseObject(piRoleColl); + ReleaseObject(piRoleObj); + + return hr; +} + +static HRESULT ReadComponentList( + LPWSTR* ppwzData, + CPI_COMPONENT** ppCompList + ) +{ + HRESULT hr = S_OK; + + LPWSTR pwzData = NULL; + + CPI_COMPONENT* pItm = NULL; + + int iCnt = 0; + + // read count + hr = WcaReadIntegerFromCaData(ppwzData, &iCnt); + ExitOnFailure(hr, "Failed to read count"); + + // read components + for (int i = 0; i < iCnt; i++) + { + pItm = (CPI_COMPONENT*)::HeapAlloc(::GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(CPI_COMPONENT)); + if (!pItm) + ExitFunction1(hr = E_OUTOFMEMORY); + + // read clsid + hr = WcaReadStringFromCaData(ppwzData, &pwzData); + ExitOnFailure(hr, "Failed to read clsid"); + StringCchCopyW(pItm->wzCLSID, countof(pItm->wzCLSID), pwzData); + + // read properties + hr = CpiReadPropertyList(ppwzData, &pItm->pPropertyList); + ExitOnFailure(hr, "Failed to read properties"); + + // read role assignments + hr = ReadRoleAssignmentList(ppwzData, &pItm->pRoleAssignmentList); + ExitOnFailure(hr, "Failed to read role assignments"); + + // read interfaces + hr = ReadInterfaceList(ppwzData, &pItm->pInterfaceList); + ExitOnFailure(hr, "Failed to read interfaces"); + + // add to list + if (*ppCompList) + pItm->pNext = *ppCompList; + *ppCompList = pItm; + pItm = NULL; + } + + hr = S_OK; + +LExit: + // clean up + ReleaseStr(pwzData); + + if (pItm) + FreeComponentList(pItm); + + return hr; +} + +static HRESULT ReadInterfaceList( + LPWSTR* ppwzData, + CPI_INTERFACE** ppIntfList + ) +{ + HRESULT hr = S_OK; + + LPWSTR pwzData = NULL; + + CPI_INTERFACE* pItm = NULL; + + int iCnt = 0; + + // read count + hr = WcaReadIntegerFromCaData(ppwzData, &iCnt); + ExitOnFailure(hr, "Failed to read count"); + + // read interfaces + for (int i = 0; i < iCnt; i++) + { + pItm = (CPI_INTERFACE*)::HeapAlloc(::GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(CPI_INTERFACE)); + if (!pItm) + ExitFunction1(hr = E_OUTOFMEMORY); + + // read iid + hr = WcaReadStringFromCaData(ppwzData, &pwzData); + ExitOnFailure(hr, "Failed to read iid"); + StringCchCopyW(pItm->wzIID, countof(pItm->wzIID), pwzData); + + // read properties + hr = CpiReadPropertyList(ppwzData, &pItm->pPropertyList); + ExitOnFailure(hr, "Failed to read properties"); + + // read role assignments + hr = ReadRoleAssignmentList(ppwzData, &pItm->pRoleAssignmentList); + ExitOnFailure(hr, "Failed to read role assignments"); + + // read methods + hr = ReadMethodList(ppwzData, &pItm->pMethodList); + ExitOnFailure(hr, "Failed to read methods"); + + // add to list + if (*ppIntfList) + pItm->pNext = *ppIntfList; + *ppIntfList = pItm; + pItm = NULL; + } + + hr = S_OK; + +LExit: + // clean up + ReleaseStr(pwzData); + + if (pItm) + FreeInterfaceList(pItm); + + return hr; +} + +static HRESULT ReadMethodList( + LPWSTR* ppwzData, + CPI_METHOD** ppMethList + ) +{ + HRESULT hr = S_OK; + + LPWSTR pwzData = NULL; + + CPI_METHOD* pItm = NULL; + + int iCnt = 0; + + // read count + hr = WcaReadIntegerFromCaData(ppwzData, &iCnt); + ExitOnFailure(hr, "Failed to read count"); + + // read methods + for (int i = 0; i < iCnt; i++) + { + pItm = (CPI_METHOD*)::HeapAlloc(::GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(CPI_METHOD)); + if (!pItm) + ExitFunction1(hr = E_OUTOFMEMORY); + + // read index + hr = WcaReadStringFromCaData(ppwzData, &pwzData); + ExitOnFailure(hr, "Failed to read index"); + StringCchCopyW(pItm->wzIndex, countof(pItm->wzIndex), pwzData); + + // read name + hr = WcaReadStringFromCaData(ppwzData, &pwzData); + ExitOnFailure(hr, "Failed to read name"); + StringCchCopyW(pItm->wzName, countof(pItm->wzName), pwzData); + + // read properties + hr = CpiReadPropertyList(ppwzData, &pItm->pPropertyList); + ExitOnFailure(hr, "Failed to read properties"); + + // read role assignments + hr = ReadRoleAssignmentList(ppwzData, &pItm->pRoleAssignmentList); + ExitOnFailure(hr, "Failed to read role assignments"); + + // add to list + if (*ppMethList) + pItm->pNext = *ppMethList; + *ppMethList = pItm; + pItm = NULL; + } + + hr = S_OK; + +LExit: + // clean up + ReleaseStr(pwzData); + + if (pItm) + FreeMethodList(pItm); + + return hr; +} + +static HRESULT ReadRoleAssignmentList( + LPWSTR* ppwzData, + CPI_ROLE_ASSIGNMENT** ppRoleList + ) +{ + HRESULT hr = S_OK; + + LPWSTR pwzData = NULL; + + CPI_ROLE_ASSIGNMENT* pItm = NULL; + + int iCnt = 0; + + // read role count + hr = WcaReadIntegerFromCaData(ppwzData, &iCnt); + ExitOnFailure(hr, "Failed to read role assignments count"); + + // read roles + for (int i = 0; i < iCnt; i++) + { + pItm = (CPI_ROLE_ASSIGNMENT*)::HeapAlloc(::GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(CPI_ROLE_ASSIGNMENT)); + if (!pItm) + ExitFunction1(hr = E_OUTOFMEMORY); + + // read key + hr = WcaReadStringFromCaData(ppwzData, &pwzData); + ExitOnFailure(hr, "Failed to read key"); + StringCchCopyW(pItm->wzKey, countof(pItm->wzKey), pwzData); + + // read role name + hr = WcaReadStringFromCaData(ppwzData, &pwzData); + ExitOnFailure(hr, "Failed to read role name"); + StringCchCopyW(pItm->wzRoleName, countof(pItm->wzRoleName), pwzData); + + // add to list + if (*ppRoleList) + pItm->pNext = *ppRoleList; + *ppRoleList = pItm; + pItm = NULL; + } + + hr = S_OK; + +LExit: + // clean up + ReleaseStr(pwzData); + + if (pItm) + FreeRoleAssignmentList(pItm); + + return hr; +} + +static void FreeComponentList( + CPI_COMPONENT* pList + ) +{ + while (pList) + { + if (pList->pPropertyList) + CpiFreePropertyList(pList->pPropertyList); + if (pList->pRoleAssignmentList) + FreeRoleAssignmentList(pList->pRoleAssignmentList); + if (pList->pInterfaceList) + FreeInterfaceList(pList->pInterfaceList); + + CPI_COMPONENT* pDelete = pList; + pList = pList->pNext; + ::HeapFree(::GetProcessHeap(), 0, pDelete); + } +} + +static void FreeInterfaceList( + CPI_INTERFACE* pList + ) +{ + while (pList) + { + if (pList->pPropertyList) + CpiFreePropertyList(pList->pPropertyList); + if (pList->pRoleAssignmentList) + FreeRoleAssignmentList(pList->pRoleAssignmentList); + if (pList->pMethodList) + FreeMethodList(pList->pMethodList); + + CPI_INTERFACE* pDelete = pList; + pList = pList->pNext; + ::HeapFree(::GetProcessHeap(), 0, pDelete); + } +} + +static void FreeMethodList( + CPI_METHOD* pList + ) +{ + while (pList) + { + if (pList->pPropertyList) + CpiFreePropertyList(pList->pPropertyList); + if (pList->pRoleAssignmentList) + FreeRoleAssignmentList(pList->pRoleAssignmentList); + + CPI_METHOD* pDelete = pList; + pList = pList->pNext; + ::HeapFree(::GetProcessHeap(), 0, pDelete); + } +} + +static void FreeRoleAssignmentList( + CPI_ROLE_ASSIGNMENT* pList + ) +{ + while (pList) + { + CPI_ROLE_ASSIGNMENT* pDelete = pList; + pList = pList->pNext; + ::HeapFree(::GetProcessHeap(), 0, pDelete); + } +} diff --git a/src/ca/cpasmexec.h b/src/ca/cpasmexec.h new file mode 100644 index 00000000..56184c01 --- /dev/null +++ b/src/ca/cpasmexec.h @@ -0,0 +1,20 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + + +HRESULT CpiConfigureAssemblies( + LPWSTR* ppwzData, + HANDLE hRollbackFile + ); +HRESULT CpiRollbackConfigureAssemblies( + LPWSTR* ppwzData, + CPI_ROLLBACK_DATA* pRollbackDataList + ); +HRESULT CpiConfigureRoleAssignments( + LPWSTR* ppwzData, + HANDLE hRollbackFile + ); +HRESULT CpiRollbackConfigureRoleAssignments( + LPWSTR* ppwzData, + CPI_ROLLBACK_DATA* pRollbackDataList + ); diff --git a/src/ca/cpasmsched.cpp b/src/ca/cpasmsched.cpp new file mode 100644 index 00000000..97ecff61 --- /dev/null +++ b/src/ca/cpasmsched.cpp @@ -0,0 +1,2135 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + + +// sql queries + +LPCWSTR vcsMsiAssemblyNameQuery = + L"SELECT `Name`, `Value` FROM `MsiAssemblyName` WHERE `Component_` = ?"; +enum eMsiAssemblyNameQuery { manqName = 1, manqValue }; + +LPCWSTR vcsModuleQuery = + L"SELECT `ModuleID` FROM `ModuleSignature`"; +enum eModuleQuery { mqModule = 1 }; + +LPCWSTR vcsAssemblyQuery = + L"SELECT `Assembly`, `Component_`, `Application_`, `AssemblyName`, `DllPath`, `TlbPath`, `PSDllPath`, `Attributes` FROM `ComPlusAssembly`"; +enum eAssemblyQuery { aqAssembly = 1, aqComponent, aqApplication, aqAssemblyName, aqDllPath, aqTlbPath, aqPSDllPath, aqAttributes }; + +LPCWSTR vcsComponentQuery = + L"SELECT `ComPlusComponent`, `CLSID` FROM `ComPlusComponent` WHERE `Assembly_` = ?"; +enum eComponentQuery { cqComponent = 1, cqCLSID }; + +LPCWSTR vcsComponentPropertyQuery = + L"SELECT `Name`, `Value` FROM `ComPlusComponentProperty` WHERE `ComPlusComponent_` = ?"; + +LPCWSTR vcsInterfaceQuery = + L"SELECT `Interface`, `IID` FROM `ComPlusInterface` WHERE `ComPlusComponent_` = ?"; +enum eInterfaceQuery { iqInterface = 1, iqIID }; + +LPCWSTR vcsInterfacePropertyQuery = + L"SELECT `Name`, `Value` FROM `ComPlusInterfaceProperty` WHERE `Interface_` = ?"; + +LPCWSTR vcsMethodQuery = + L"SELECT `Method`, `Index`, `Name` FROM `ComPlusMethod` WHERE `Interface_` = ?"; +enum eMethodQuery { mqMethod = 1, mqIndex, mqName }; + +LPCWSTR vcsMethodPropertyQuery = + L"SELECT `Name`, `Value` FROM `ComPlusMethodProperty` WHERE `Method_` = ?"; + +LPCWSTR vcsRoleForComponentQuery = + L"SELECT `RoleForComponent`, `ApplicationRole_`, `Component_` FROM `ComPlusRoleForComponent` WHERE `ComPlusComponent_` = ?"; +LPCWSTR vcsRoleForInterfaceQuery = + L"SELECT `RoleForInterface`, `ApplicationRole_`, `Component_` FROM `ComPlusRoleForInterface` WHERE `Interface_` = ?"; +LPCWSTR vcsRoleForMethodQuery = + L"SELECT `RoleForMethod`, `ApplicationRole_`, `Component_` FROM `ComPlusRoleForMethod` WHERE `Method_` = ?"; + +enum eRoleAssignmentQuery { raqKey = 1, raqApplicationRole, raqComponent }; + +LPCWSTR vcsModuleComponentsQuery = + L"SELECT `Component`, `ModuleID` FROM `ModuleComponents`"; +LPCWSTR vcsModuleDependencyQuery = + L"SELECT `ModuleID`, `RequiredID` FROM `ModuleDependency`"; +LPCWSTR vcsAssemblyDependencyQuery = + L"SELECT `Assembly_`, `RequiredAssembly_` FROM `ComPlusAssemblyDependency`"; + +enum eKeyPairQuery { kpqFirstKey = 1, kpqSecondKey }; + + +// private structs + +struct CPI_KEY_PAIR +{ + WCHAR wzFirstKey[MAX_DARWIN_KEY + 1]; + WCHAR wzSecondKey[MAX_DARWIN_KEY + 1]; + + CPI_KEY_PAIR* pNext; +}; + +struct CPI_DEPENDENCY_CHAIN +{ + LPCWSTR pwzKey; + + CPI_DEPENDENCY_CHAIN* pPrev; +}; + +struct CPI_MODULE +{ + WCHAR wzKey[MAX_DARWIN_KEY + 1]; + + CPI_MODULE* pPrev; + CPI_MODULE* pNext; +}; + +struct CPI_MODULE_LIST +{ + CPI_MODULE* pFirst; + CPI_MODULE* pLast; +}; + + +// property definitions + +CPI_PROPERTY_DEFINITION pdlComponentProperties[] = +{ + {L"AllowInprocSubscribers", cpptBoolean, 500}, + {L"ComponentAccessChecksEnabled", cpptBoolean, 500}, + {L"ComponentTransactionTimeout", cpptInteger, 500}, + {L"ComponentTransactionTimeoutEnabled", cpptBoolean, 500}, + {L"COMTIIntrinsics", cpptBoolean, 500}, + {L"ConstructionEnabled", cpptBoolean, 500}, + {L"ConstructorString", cpptString, 500}, + {L"CreationTimeout", cpptInteger, 500}, + {L"Description", cpptString, 500}, + {L"EventTrackingEnabled", cpptBoolean, 500}, + {L"ExceptionClass", cpptString, 500}, + {L"FireInParallel", cpptBoolean, 500}, + {L"IISIntrinsics", cpptBoolean, 500}, + {L"InitializesServerApplication", cpptBoolean, 500}, + {L"IsEnabled", cpptBoolean, 501}, + {L"IsPrivateComponent", cpptBoolean, 501}, + {L"JustInTimeActivation", cpptBoolean, 500}, + {L"LoadBalancingSupported", cpptBoolean, 500}, + {L"MaxPoolSize", cpptInteger, 500}, + {L"MinPoolSize", cpptInteger, 500}, + {L"MultiInterfacePublisherFilterCLSID", cpptString, 500}, + {L"MustRunInClientContext", cpptBoolean, 500}, + {L"MustRunInDefaultContext", cpptBoolean, 501}, + {L"ObjectPoolingEnabled", cpptBoolean, 500}, + {L"PublisherID", cpptString, 500}, + {L"SoapAssemblyName", cpptString, 502}, + {L"SoapTypeName", cpptString, 502}, + {L"Synchronization", cpptInteger, 500}, + {L"Transaction", cpptInteger, 500}, + {L"TxIsolationLevel", cpptInteger, 501}, + {NULL, cpptNone, 0} +}; + +CPI_PROPERTY_DEFINITION pdlInterfaceProperties[] = +{ + {L"Description", cpptString, 500}, + {L"QueuingEnabled", cpptBoolean, 500}, + {NULL, cpptNone, 0} +}; + +CPI_PROPERTY_DEFINITION pdlMethodProperties[] = +{ + {L"AutoComplete", cpptBoolean, 500}, + {L"Description", cpptString, 500}, + {NULL, cpptNone, 0} +}; + + +// prototypes for private helper functions + +static HRESULT GetAssemblyName( + LPCWSTR pwzComponent, + LPWSTR* ppwzAssemblyName + ); +static HRESULT KeyPairsRead( + LPCWSTR pwzQuery, + CPI_KEY_PAIR** ppKeyPairList + ); +static HRESULT ModulesRead( + CPI_MODULE_LIST* pModList + ); +static HRESULT AssembliesRead( + CPI_KEY_PAIR* pModCompList, + CPI_APPLICATION_LIST* pAppList, + CPI_APPLICATION_ROLE_LIST* pAppRoleList, + CPI_ASSEMBLY_LIST* pAsmList + ); +static HRESULT ComponentsRead( + LPCWSTR pwzAsmKey, + CPI_APPLICATION_ROLE_LIST* pAppRoleList, + CPI_ASSEMBLY* pAsm + ); +static HRESULT InterfacesRead( + LPCWSTR pwzCompKey, + CPI_APPLICATION_ROLE_LIST* pAppRoleList, + CPI_ASSEMBLY* pAsm, + CPI_COMPONENT* pComp + ); +static HRESULT MethodsRead( + LPCWSTR pwzIntfKey, + CPI_APPLICATION_ROLE_LIST* pAppRoleList, + CPI_ASSEMBLY* pAsm, + CPI_INTERFACE* pIntf + ); +static HRESULT RoleAssignmentsRead( + LPCWSTR pwzQuery, + LPCWSTR pwzKey, + CPI_APPLICATION_ROLE_LIST* pAppRoleList, + CPI_ROLE_ASSIGNMENT** ppRoleList, + int* piInstallCount, + int* piUninstallCount + ); +static HRESULT TopSortModuleList( + CPI_KEY_PAIR* pDepList, + CPI_MODULE_LIST* pList + ); +static HRESULT SwapDependentModules( + CPI_DEPENDENCY_CHAIN* pdcPrev, + CPI_KEY_PAIR* pDepList, + CPI_MODULE_LIST* pList, + CPI_MODULE* pRoot, + CPI_MODULE* pItm + ); +static HRESULT ModuleFindByKey( + CPI_MODULE* pItm, + LPCWSTR pwzKey, + BOOL fReverse, + CPI_MODULE** ppItm + ); +static void SortAssemblyListByModule( + CPI_MODULE_LIST* pModList, + CPI_ASSEMBLY_LIST* pAsmList + ); +static HRESULT TopSortAssemblyList( + CPI_KEY_PAIR* pDepList, + CPI_ASSEMBLY_LIST* pList + ); +static HRESULT SwapDependentAssemblies( + CPI_DEPENDENCY_CHAIN* pdcPrev, + CPI_KEY_PAIR* pDepList, + CPI_ASSEMBLY_LIST* pList, + CPI_ASSEMBLY* pRoot, + CPI_ASSEMBLY* pItm + ); +static HRESULT AssemblyFindByKey( + CPI_ASSEMBLY* pItm, + LPCWSTR pwzKey, + BOOL fReverse, + CPI_ASSEMBLY** ppItm + ); +static HRESULT AddAssemblyToActionData( + CPI_ASSEMBLY* pItm, + BOOL fInstall, + int iActionType, + int iActionCost, + LPWSTR* ppwzActionData + ); +static HRESULT AddRoleAssignmentsToActionData( + CPI_ASSEMBLY* pItm, + BOOL fInstall, + int iActionType, + int iActionCost, + LPWSTR* ppwzActionData + ); +static HRESULT AddComponentToActionData( + CPI_COMPONENT* pItm, + BOOL fInstall, + BOOL fProps, + BOOL fRoles, + LPWSTR* ppwzActionData + ); +static HRESULT AddInterfaceToActionData( + CPI_INTERFACE* pItm, + BOOL fInstall, + BOOL fProps, + BOOL fRoles, + LPWSTR* ppwzActionData + ); +static HRESULT AddMethodToActionData( + CPI_METHOD* pItm, + BOOL fInstall, + BOOL fProps, + BOOL fRoles, + LPWSTR* ppwzActionData + ); +static HRESULT AddRolesToActionData( + int iRoleInstallCount, + int iRoleUninstallCount, + CPI_ROLE_ASSIGNMENT* pRoleList, + BOOL fInstall, + BOOL fRoles, + LPWSTR* ppwzActionData + ); +static HRESULT KeyPairFindByFirstKey( + CPI_KEY_PAIR* pList, + LPCWSTR pwzKey, + CPI_KEY_PAIR** ppItm + ); +static void AssemblyFree( + CPI_ASSEMBLY* pItm + ); +static void KeyPairsFreeList( + CPI_KEY_PAIR* pList + ); +void ModuleListFree( + CPI_MODULE_LIST* pList + ); +static void ModuleFree( + CPI_MODULE* pItm + ); +static void ComponentsFreeList( + CPI_COMPONENT* pList + ); +static void InterfacesFreeList( + CPI_INTERFACE* pList + ); +static void MethodsFreeList( + CPI_METHOD* pList + ); +static void RoleAssignmentsFreeList( + CPI_ROLE_ASSIGNMENT* pList + ); + + +// function definitions + +void CpiAssemblyListFree( + CPI_ASSEMBLY_LIST* pList + ) +{ + CPI_ASSEMBLY* pItm = pList->pFirst; + + while (pItm) + { + CPI_ASSEMBLY* pDelete = pItm; + pItm = pItm->pNext; + AssemblyFree(pDelete); + } +} + +HRESULT CpiAssembliesRead( + CPI_APPLICATION_LIST* pAppList, + CPI_APPLICATION_ROLE_LIST* pAppRoleList, + CPI_ASSEMBLY_LIST* pAsmList + ) +{ + HRESULT hr = S_OK; + CPI_MODULE_LIST modList; + CPI_KEY_PAIR* pModCompList = NULL; + CPI_KEY_PAIR* pModDepList = NULL; + CPI_KEY_PAIR* pAsmDepList = NULL; + + ::ZeroMemory(&modList, sizeof(CPI_MODULE_LIST)); + + BOOL fModuleSignatureTable = (S_OK == WcaTableExists(L"ModuleSignature")); + BOOL fModuleComponentsTable = (S_OK == WcaTableExists(L"ModuleComponents")); + BOOL fModuleDependencyTable = (S_OK == WcaTableExists(L"ModuleDependency")); + + // read modules + if (fModuleSignatureTable) + { + hr = ModulesRead(&modList); + ExitOnFailure(hr, "Failed to read ModuleSignature table"); + } + + // read module components + if (fModuleComponentsTable) + { + hr = KeyPairsRead(vcsModuleComponentsQuery, &pModCompList); + ExitOnFailure(hr, "Failed to read ModuleComponents table"); + } + + // read module dependencies + if (fModuleDependencyTable) + { + hr = KeyPairsRead(vcsModuleDependencyQuery, &pModDepList); + ExitOnFailure(hr, "Failed to read ModuleDependency table"); + } + + // read assemblies + hr = AssembliesRead(pModCompList, pAppList, pAppRoleList, pAsmList); + ExitOnFailure(hr, "Failed to read ComPlusAssembly table"); + + // read assembly dependencies + if (CpiTableExists(cptComPlusAssemblyDependency)) + { + hr = KeyPairsRead(vcsAssemblyDependencyQuery, &pAsmDepList); + ExitOnFailure(hr, "Failed to read ComPlusAssemblyDependency table"); + } + + // sort modules + if (modList.pFirst && pModDepList) + { + hr = TopSortModuleList(pModDepList, &modList); + ExitOnFailure(hr, "Failed to sort modules"); + } + + // sort assemblies by module + if (pAsmList->pFirst && modList.pFirst && pModDepList) + SortAssemblyListByModule(&modList, pAsmList); + + // sort assemblies by dependency + if (pAsmList->pFirst && pAsmDepList) + { + hr = TopSortAssemblyList(pAsmDepList, pAsmList); + ExitOnFailure(hr, "Failed to sort assemblies"); + } + + hr = S_OK; + +LExit: + // clean up + ModuleListFree(&modList); + if (pModCompList) + KeyPairsFreeList(pModCompList); + if (pModDepList) + KeyPairsFreeList(pModDepList); + if (pAsmDepList) + KeyPairsFreeList(pAsmDepList); + + return hr; +} + +HRESULT CpiAssembliesVerifyInstall( + CPI_ASSEMBLY_LIST* pList + ) +{ + HRESULT hr = S_OK; + + for (CPI_ASSEMBLY* pItm = pList->pFirst; pItm; pItm = pItm->pNext) + { + // assemblies that are being installed + if (!pItm->fReferencedForInstall && !pItm->iRoleAssignmentsInstallCount && !WcaIsInstalling(pItm->isInstalled, pItm->isAction)) + continue; + + // if the assembly is referensed, it must be installed + if ((pItm->fReferencedForInstall || pItm->iRoleAssignmentsInstallCount) && !CpiWillBeInstalled(pItm->isInstalled, pItm->isAction)) + MessageExitOnFailure(hr = E_FAIL, msierrComPlusAssemblyDependency, "An assembly is used by another entity being installed, but is not installed itself, key: %S", pItm->wzKey); + } + + hr = S_OK; + +LExit: + return hr; +} + +HRESULT CpiAssembliesVerifyUninstall( + CPI_ASSEMBLY_LIST* pList + ) +{ + HRESULT hr = S_OK; + + for (CPI_ASSEMBLY* pItm = pList->pFirst; pItm; pItm = pItm->pNext) + { + // assemblies that are being uninstalled + if (!pItm->fReferencedForUninstall && !pItm->iRoleAssignmentsUninstallCount && (!WcaIsUninstalling(pItm->isInstalled, pItm->isAction) && !WcaIsReInstalling(pItm->isInstalled, pItm->isAction))) + continue; + + // if the application is not present, there is no need to remove the components + if (pItm->pApplication && pItm->pApplication->fObjectNotFound) + { + pItm->fIgnore = TRUE; + pList->iUninstallCount--; // elements with the fIgnore flag set will not be scheduled for uninstall + pList->iRoleUninstallCount--; + } + } + + hr = S_OK; + +//LExit: + return hr; +} + +HRESULT CpiAssembliesInstall( + CPI_ASSEMBLY_LIST* pList, + int iRunMode, + LPWSTR* ppwzActionData, + int* piProgress + ) +{ + HRESULT hr = S_OK; + + int iActionType; + int iCount = 0; + + // add action text + hr = CpiAddActionTextToActionData(L"RegisterComPlusAssemblies", ppwzActionData); + ExitOnFailure(hr, "Failed to add action text to custom action data"); + + // assembly count + switch (iRunMode) + { + case rmDeferred: + iCount = pList->iInstallCount - pList->iCommitCount; + break; + case rmCommit: + iCount = pList->iCommitCount; + break; + case rmRollback: + iCount = pList->iInstallCount; + break; + } + + // add assembly count to action data + hr = WcaWriteIntegerToCaData(iCount, ppwzActionData); + ExitOnFailure(hr, "Failed to add count to custom action data"); + + // add assemblies to custom action data in forward order + for (CPI_ASSEMBLY* pItm = pList->pFirst; pItm; pItm = pItm->pNext) + { + // assemblies that are being installed, or contains roll assignments to install + if (!WcaIsInstalling(pItm->isInstalled, pItm->isAction)) + continue; + + // assemblies that are being installed must be scheduled during the right type of action + BOOL fRunInCommit = 0 != (pItm->iAttributes & aaRunInCommit); + if (((rmCommit == iRunMode && !fRunInCommit) || (rmDeferred == iRunMode && fRunInCommit))) + continue; + + // action type + if (rmRollback == iRunMode) + { + if (CpiIsInstalled(pItm->isInstalled)) + iActionType = atNoOp; + else + iActionType = atRemove; + } + else + iActionType = atCreate; + + // add to action data + hr = AddAssemblyToActionData(pItm, TRUE, iActionType, COST_ASSEMBLY_REGISTER, ppwzActionData); + ExitOnFailure(hr, "Failed to add assembly to custom action data, key: %S", pItm->wzKey); + } + + // add progress tics + if (piProgress) + *piProgress += COST_ASSEMBLY_REGISTER * iCount; + + hr = S_OK; + +LExit: + return hr; +} + +HRESULT CpiAssembliesUninstall( + CPI_ASSEMBLY_LIST* pList, + int iRunMode, + LPWSTR* ppwzActionData, + int* piProgress + ) +{ + HRESULT hr = S_OK; + + int iActionType; + + // add action text + hr = CpiAddActionTextToActionData(L"UnregisterComPlusAssemblies", ppwzActionData); + ExitOnFailure(hr, "Failed to add action text to custom action data"); + + // add assembly count to action data + hr = WcaWriteIntegerToCaData(pList->iUninstallCount, ppwzActionData); + ExitOnFailure(hr, "Failed to add count to custom action data"); + + // add assemblies to custom action data in reverse order + for (CPI_ASSEMBLY* pItm = pList->pLast; pItm; pItm = pItm->pPrev) + { + // assemblies that are being uninstalled + if (pItm->fIgnore || (!WcaIsUninstalling(pItm->isInstalled, pItm->isAction) && !WcaIsReInstalling(pItm->isInstalled, pItm->isAction))) + continue; + + // action type + if (rmRollback == iRunMode) + iActionType = atCreate; + else + iActionType = atRemove; + + // add to action data + hr = AddAssemblyToActionData(pItm, FALSE, iActionType, COST_ASSEMBLY_UNREGISTER, ppwzActionData); + ExitOnFailure(hr, "Failed to add assembly to custom action data, key: %S", pItm->wzKey); + } + + // add progress tics + if (piProgress) + *piProgress += COST_ASSEMBLY_UNREGISTER * pList->iUninstallCount; + + hr = S_OK; + +LExit: + return hr; +} + +HRESULT CpiRoleAssignmentsInstall( + CPI_ASSEMBLY_LIST* pList, + int iRunMode, + LPWSTR* ppwzActionData, + int* piProgress + ) +{ + HRESULT hr = S_OK; + + int iActionType; + int iCount = 0; + + // add action text + hr = CpiAddActionTextToActionData(L"AddComPlusRoleAssignments", ppwzActionData); + ExitOnFailure(hr, "Failed to add action text to custom action data"); + + // assembly count + switch (iRunMode) + { + case rmDeferred: + iCount = pList->iRoleInstallCount - pList->iRoleCommitCount; + break; + case rmCommit: + iCount = pList->iRoleCommitCount; + break; + case rmRollback: + iCount = pList->iRoleInstallCount; + break; + } + + // add assembly count to action data + hr = WcaWriteIntegerToCaData(iCount, ppwzActionData); + ExitOnFailure(hr, "Failed to add count to custom action data"); + + // add assemblies to custom action data in forward order + for (CPI_ASSEMBLY* pItm = pList->pFirst; pItm; pItm = pItm->pNext) + { + // assemblies that are being installed, or contains roll assignments to install + if (!pItm->iRoleAssignmentsInstallCount) + continue; + + // assemblies that are being installed must be scheduled during the right type of action + BOOL fRunInCommit = 0 != (pItm->iAttributes & aaRunInCommit); + if (((rmCommit == iRunMode && !fRunInCommit) || (rmDeferred == iRunMode && fRunInCommit))) + continue; + + // action type + if (rmRollback == iRunMode) + { + if (CpiIsInstalled(pItm->isInstalled)) + iActionType = atNoOp; + else + iActionType = atRemove; + } + else + iActionType = atCreate; + + // add to action data + hr = AddRoleAssignmentsToActionData(pItm, TRUE, iActionType, COST_ROLLASSIGNMENT_CREATE, ppwzActionData); + ExitOnFailure(hr, "Failed to add assembly to custom action data, key: %S", pItm->wzKey); + + // add progress tics + if (piProgress) + *piProgress += COST_ROLLASSIGNMENT_CREATE * pItm->iRoleAssignmentsInstallCount; + } + + hr = S_OK; + +LExit: + return hr; +} + +HRESULT CpiRoleAssignmentsUninstall( + CPI_ASSEMBLY_LIST* pList, + int iRunMode, + LPWSTR* ppwzActionData, + int* piProgress + ) +{ + HRESULT hr = S_OK; + + int iActionType; + + // add action text + hr = CpiAddActionTextToActionData(L"RemoveComPlusRoleAssignments", ppwzActionData); + ExitOnFailure(hr, "Failed to add action text to custom action data"); + + // add assembly count to action data + hr = WcaWriteIntegerToCaData(pList->iRoleUninstallCount, ppwzActionData); + ExitOnFailure(hr, "Failed to add count to custom action data"); + + // add assemblies to custom action data in reverse order + for (CPI_ASSEMBLY* pItm = pList->pLast; pItm; pItm = pItm->pPrev) + { + // assemblies that are being uninstalled + if (pItm->fIgnore || !pItm->iRoleAssignmentsUninstallCount) + continue; + + // action type + if (rmRollback == iRunMode) + iActionType = atCreate; + else + iActionType = atRemove; + + // add to action data + hr = AddRoleAssignmentsToActionData(pItm, FALSE, iActionType, COST_ROLLASSIGNMENT_DELETE, ppwzActionData); + ExitOnFailure(hr, "Failed to add assembly to custom action data, key: %S", pItm->wzKey); + + // add progress tics + if (piProgress) + *piProgress += COST_ROLLASSIGNMENT_DELETE * pItm->iRoleAssignmentsUninstallCount; + } + + hr = S_OK; + +LExit: + return hr; +} + +HRESULT CpiGetSubscriptionsCollForComponent( + CPI_ASSEMBLY* pAsm, + CPI_COMPONENT* pComp, + ICatalogCollection** ppiSubsColl + ) +{ + HRESULT hr = S_OK; + + ICatalogCollection* piCompColl = NULL; + ICatalogObject* piCompObj = NULL; + + // get applications collection + if (!pComp->piSubsColl) + { + // get components collection for application + hr = CpiGetComponentsCollForApplication(pAsm->pApplication, &piCompColl); + ExitOnFailure(hr, "Failed to get components collection for application"); + + if (S_FALSE == hr) + ExitFunction(); // exit with hr = S_FALSE + + // find component object + hr = CpiFindCollectionObject(piCompColl, pComp->wzCLSID, NULL, &piCompObj); + ExitOnFailure(hr, "Failed to find component object"); + + if (S_FALSE == hr) + ExitFunction(); // exit with hr = S_FALSE + + // get roles collection + hr = CpiGetCatalogCollection(piCompColl, piCompObj, L"SubscriptionsForComponent", &pComp->piSubsColl); + ExitOnFailure(hr, "Failed to get subscriptions collection"); + } + + // return value + *ppiSubsColl = pComp->piSubsColl; + (*ppiSubsColl)->AddRef(); + + hr = S_OK; + +LExit: + // clean up + ReleaseObject(piCompColl); + ReleaseObject(piCompObj); + + return hr; +} + + +// helper function definitions + +static HRESULT GetAssemblyName( + LPCWSTR pwzComponent, + LPWSTR* ppwzAssemblyName + ) +{ + HRESULT hr = S_OK; + + PMSIHANDLE hView, hRecKey, hRec; + + LPWSTR pwzKey = NULL; + + LPWSTR pwzName = NULL; + LPWSTR pwzVersion = NULL; + LPWSTR pwzCulture = NULL; + LPWSTR pwzPublicKeyToken = NULL; + + // create parameter record + hRecKey = ::MsiCreateRecord(1); + ExitOnNull(hRecKey, hr, E_OUTOFMEMORY, "Failed to create record"); + hr = WcaSetRecordString(hRecKey, 1, pwzComponent); + ExitOnFailure(hr, "Failed to set record string"); + + // open view + hr = WcaOpenView(vcsMsiAssemblyNameQuery, &hView); + ExitOnFailure(hr, "Failed to open view on MsiAssemblyName table"); + hr = WcaExecuteView(hView, hRecKey); + ExitOnFailure(hr, "Failed to execute view on MsiAssemblyName table"); + + while (S_OK == (hr = WcaFetchRecord(hView, &hRec))) + { + // read key + hr = WcaGetRecordString(hRec, manqName, &pwzKey); + ExitOnFailure(hr, "Failed to get name"); + + // read value + if (0 == lstrcmpiW(L"name", pwzKey)) + hr = WcaGetRecordString(hRec, manqValue, &pwzName); + else if (0 == lstrcmpiW(L"version", pwzKey)) + hr = WcaGetRecordString(hRec, manqValue, &pwzVersion); + else if (0 == lstrcmpiW(L"culture", pwzKey)) + hr = WcaGetRecordString(hRec, manqValue, &pwzCulture); + else if (0 == lstrcmpiW(L"publicKeyToken", pwzKey)) + hr = WcaGetRecordString(hRec, manqValue, &pwzPublicKeyToken); + else + { + WcaLog(LOGMSG_VERBOSE, "Unknown name in MsiAssemblyName table: %S, %S", pwzComponent, pwzKey); + hr = S_OK; + } + + ExitOnFailure(hr, "Failed to get value"); + } + + if (E_NOMOREITEMS != hr) + ExitOnFailure(hr, "Failed to fetch record"); + + // verify + if (!(pwzName && *pwzName) || !(pwzVersion && *pwzVersion)) + ExitOnFailure(hr = E_FAIL, "Incomplete assembly name"); + + // build name string + hr = StrAllocFormatted(ppwzAssemblyName, L"%s, Version=%s, Culture=%s, PublicKeyToken=%s", + pwzName, pwzVersion, + pwzCulture && *pwzCulture ? pwzCulture : L"Neutral", + pwzPublicKeyToken && *pwzPublicKeyToken ? pwzPublicKeyToken : L"null"); + ExitOnFailure(hr, "Failed to build assembly name string"); + + hr = S_OK; + +LExit: + // clean up + ReleaseStr(pwzKey); + ReleaseStr(pwzName); + ReleaseStr(pwzVersion); + ReleaseStr(pwzCulture); + ReleaseStr(pwzPublicKeyToken); + + return hr; +} + +static HRESULT KeyPairsRead( + LPCWSTR pwzQuery, + CPI_KEY_PAIR** ppKeyPairList + ) +{ + HRESULT hr = S_OK; + + PMSIHANDLE hView, hRec; + + CPI_KEY_PAIR* pItm = NULL; + LPWSTR pwzData = NULL; + + // loop through all dependencies + hr = WcaOpenExecuteView(pwzQuery, &hView); + ExitOnFailure(hr, "Failed to execute view on table"); + + while (S_OK == (hr = WcaFetchRecord(hView, &hRec))) + { + // create entry + pItm = (CPI_KEY_PAIR*)::HeapAlloc(::GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(CPI_KEY_PAIR)); + if (!pItm) + ExitFunction1(hr = E_OUTOFMEMORY); + + // get key + hr = WcaGetRecordString(hRec, kpqFirstKey, &pwzData); + ExitOnFailure(hr, "Failed to get first key"); + StringCchCopyW(pItm->wzFirstKey, countof(pItm->wzFirstKey), pwzData); + + // get key + hr = WcaGetRecordString(hRec, kpqSecondKey, &pwzData); + ExitOnFailure(hr, "Failed to get second key"); + StringCchCopyW(pItm->wzSecondKey, countof(pItm->wzSecondKey), pwzData); + + // add entry + if (*ppKeyPairList) + pItm->pNext = *ppKeyPairList; + *ppKeyPairList = pItm; + pItm = NULL; + } + + if (E_NOMOREITEMS == hr) + hr = S_OK; + +LExit: + // clean up + if (pItm) + KeyPairsFreeList(pItm); + + ReleaseStr(pwzData); + + return hr; +} + +static HRESULT ModulesRead( + CPI_MODULE_LIST* pModList + ) +{ + HRESULT hr = S_OK; + + PMSIHANDLE hView, hRec; + + CPI_MODULE* pItm = NULL; + LPWSTR pwzData = NULL; + + // loop through all modules + hr = WcaOpenExecuteView(vcsModuleQuery, &hView); + ExitOnFailure(hr, "Failed to execute view on ModuleSignature table"); + + while (S_OK == (hr = WcaFetchRecord(hView, &hRec))) + { + // create entry + pItm = (CPI_MODULE*)::HeapAlloc(::GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(CPI_MODULE)); + if (!pItm) + ExitFunction1(hr = E_OUTOFMEMORY); + + // get key + hr = WcaGetRecordString(hRec, mqModule, &pwzData); + ExitOnFailure(hr, "Failed to get key"); + StringCchCopyW(pItm->wzKey, countof(pItm->wzKey), pwzData); + + // add entry + if (pModList->pLast) + { + pModList->pLast->pNext = pItm; + pItm->pPrev = pModList->pLast; + } + else + pModList->pFirst = pItm; + pModList->pLast = pItm; + pItm = NULL; + } + + if (E_NOMOREITEMS == hr) + hr = S_OK; + +LExit: + // clean up + if (pItm) + ModuleFree(pItm); + + ReleaseStr(pwzData); + + return hr; +} + +static HRESULT AssembliesRead( + CPI_KEY_PAIR* pModCompList, + CPI_APPLICATION_LIST* pAppList, + CPI_APPLICATION_ROLE_LIST* pAppRoleList, + CPI_ASSEMBLY_LIST* pAsmList + ) +{ + HRESULT hr = S_OK; + UINT er = ERROR_SUCCESS; + + PMSIHANDLE hView, hRec; + + CPI_ASSEMBLY* pItm = NULL; + CPI_KEY_PAIR* pModComp; + LPWSTR pwzData = NULL; + LPWSTR pwzComponent = NULL; + BOOL fMatchingArchitecture = FALSE; + + // loop through all assemblies + hr = WcaOpenExecuteView(vcsAssemblyQuery, &hView); + ExitOnFailure(hr, "Failed to execute view on ComPlusAssembly table"); + + while (S_OK == (hr = WcaFetchRecord(hView, &hRec))) + { + // get component + hr = WcaGetRecordString(hRec, aqComponent, &pwzComponent); + ExitOnFailure(hr, "Failed to get component"); + + // check if the component is our processor architecture + hr = CpiVerifyComponentArchitecure(pwzComponent, &fMatchingArchitecture); + ExitOnFailure(hr, "Failed to get component architecture."); + + if (!fMatchingArchitecture) + { + continue; // not the same architecture, ignore + } + + // create entry + pItm = (CPI_ASSEMBLY*)::HeapAlloc(::GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(CPI_ASSEMBLY)); + if (!pItm) + ExitFunction1(hr = E_OUTOFMEMORY); + + // get component install state + er = ::MsiGetComponentStateW(WcaGetInstallHandle(), pwzComponent, &pItm->isInstalled, &pItm->isAction); + ExitOnFailure(hr = HRESULT_FROM_WIN32(er), "Failed to get component state"); + + // get key + hr = WcaGetRecordString(hRec, aqAssembly, &pwzData); + ExitOnFailure(hr, "Failed to get key"); + StringCchCopyW(pItm->wzKey, countof(pItm->wzKey), pwzData); + + // get attributes + hr = WcaGetRecordInteger(hRec, aqAttributes, &pItm->iAttributes); + ExitOnFailure(hr, "Failed to get attributes"); + + // get assembly name + hr = WcaGetRecordFormattedString(hRec, aqAssemblyName, &pItm->pwzAssemblyName); + ExitOnFailure(hr, "Failed to get assembly name"); + + if (!*pItm->pwzAssemblyName && (pItm->iAttributes & aaPathFromGAC)) + { + // get assembly name for component + hr = GetAssemblyName(pwzComponent, &pItm->pwzAssemblyName); + ExitOnFailure(hr, "Failed to get assembly name for component"); + } + + // get dll path + hr = WcaGetRecordFormattedString(hRec, aqDllPath, &pItm->pwzDllPath); + ExitOnFailure(hr, "Failed to get assembly dll path"); + + // get module + // TODO: if there is a very large number of components belonging to modules, this search might be slow + hr = KeyPairFindByFirstKey(pModCompList, pwzData, &pModComp); + + if (S_OK == hr) + StringCchCopyW(pItm->wzModule, countof(pItm->wzModule), pModComp->wzSecondKey); + + // get application + hr = WcaGetRecordString(hRec, aqApplication, &pwzData); + ExitOnFailure(hr, "Failed to get application"); + + if (pwzData && *pwzData) + { + hr = CpiApplicationFindByKey(pAppList, pwzData, &pItm->pApplication); + if (S_FALSE == hr) + hr = HRESULT_FROM_WIN32(ERROR_NOT_FOUND); + ExitOnFailure(hr, "Failed to find application, key: %S", pwzData); + } + + // get tlb path + hr = WcaGetRecordFormattedString(hRec, aqTlbPath, &pItm->pwzTlbPath); + ExitOnFailure(hr, "Failed to get assembly tlb path"); + + // get proxy-stub dll path + hr = WcaGetRecordFormattedString(hRec, aqPSDllPath, &pItm->pwzPSDllPath); + ExitOnFailure(hr, "Failed to get assembly proxy-stub DLL path"); + + // read components + if (CpiTableExists(cptComPlusComponent)) + { + hr = ComponentsRead(pItm->wzKey, pAppRoleList, pItm); + ExitOnFailure(hr, "Failed to read components for assembly"); + } + + // set references & increment counters + if (WcaIsInstalling(pItm->isInstalled, pItm->isAction)) + { + pAsmList->iInstallCount++; + if (pItm->iAttributes & aaRunInCommit) + pAsmList->iCommitCount++; + } + if (WcaIsUninstalling(pItm->isInstalled, pItm->isAction) || WcaIsReInstalling(pItm->isInstalled, pItm->isAction)) + pAsmList->iUninstallCount++; + + if (pItm->iRoleAssignmentsInstallCount) + { + pAsmList->iRoleInstallCount++; + if (pItm->iAttributes & aaRunInCommit) + pAsmList->iRoleCommitCount++; + } + if (pItm->iRoleAssignmentsUninstallCount) + pAsmList->iRoleUninstallCount++; + + if (pItm->pApplication) + { + if (pItm->iRoleAssignmentsInstallCount || WcaIsInstalling(pItm->isInstalled, pItm->isAction)) + CpiApplicationAddReferenceInstall(pItm->pApplication); + if (pItm->iRoleAssignmentsUninstallCount || WcaIsUninstalling(pItm->isInstalled, pItm->isAction) || WcaIsReInstalling(pItm->isInstalled, pItm->isAction)) + CpiApplicationAddReferenceUninstall(pItm->pApplication); + } + + // add entry + if (pAsmList->pLast) + { + pAsmList->pLast->pNext = pItm; + pItm->pPrev = pAsmList->pLast; + } + else + pAsmList->pFirst = pItm; + pAsmList->pLast = pItm; + pItm = NULL; + } + + if (E_NOMOREITEMS == hr) + hr = S_OK; + +LExit: + // clean up + if (pItm) + AssemblyFree(pItm); + + ReleaseStr(pwzData); + ReleaseStr(pwzComponent); + + return hr; +} + +static HRESULT TopSortModuleList( + CPI_KEY_PAIR* pDepList, + CPI_MODULE_LIST* pList + ) +{ + HRESULT hr = S_OK; + + // top sort list + for (CPI_MODULE* pItm = pList->pFirst; pItm; pItm = pItm->pNext) + { + // append module + hr = SwapDependentModules(NULL, pDepList, pList, pItm, pItm); + ExitOnFailure(hr, "Failed to swap dependent modules"); + } + + hr = S_OK; + +LExit: + return hr; +} + +static HRESULT SwapDependentModules( + CPI_DEPENDENCY_CHAIN* pdcPrev, // list containing the entire dependency chain + CPI_KEY_PAIR* pDepList, // module dependency list + CPI_MODULE_LIST* pList, // module list being sorted + CPI_MODULE* pRoot, // first module in the chain + CPI_MODULE* pItm // current module to test for dependencies + ) +{ + HRESULT hr = S_OK; + + CPI_MODULE* pDepItm; + + // find dependencies + for (CPI_KEY_PAIR* pDep = pDepList; pDep; pDep = pDep->pNext) + { + if (0 == lstrcmpW(pItm->wzKey, pDep->wzFirstKey)) + { + CPI_DEPENDENCY_CHAIN dcItm; + dcItm.pwzKey = pItm->wzKey; + dcItm.pPrev = pdcPrev; + + // check for circular dependencies + for (CPI_DEPENDENCY_CHAIN* pdcItm = &dcItm; pdcItm; pdcItm = pdcItm->pPrev) + { + if (0 == lstrcmpW(pdcItm->pwzKey, pDep->wzSecondKey)) + { + // circular dependency found + ExitOnFailure(hr = E_FAIL, "Circular module dependency found, key: %S", pDep->wzSecondKey); + } + } + + // make sure the item is not already in the list + hr = ModuleFindByKey(pRoot->pPrev, pDep->wzSecondKey, TRUE, &pDepItm); // find in reverse order + + if (S_OK == hr) + continue; // item found, move on + + // find item in the list + hr = ModuleFindByKey(pRoot->pNext, pDep->wzSecondKey, FALSE, &pDepItm); // find in forward order + + if (S_FALSE == hr) + { + // not found + ExitOnFailure(hr = E_FAIL, "Module dependency not found, key: %S", pDep->wzSecondKey); + } + + // if this item in turn has dependencies, they have to be swaped first + hr = SwapDependentModules(&dcItm, pDepList, pList, pRoot, pDepItm); + ExitOnFailure(hr, "Failed to swap dependent module"); + + // remove item from its current position + pDepItm->pPrev->pNext = pDepItm->pNext; // pDepItm can never be the first item, no need to check pPrev + if (pDepItm->pNext) + pDepItm->pNext->pPrev = pDepItm->pPrev; + else + { + pList->pLast = pDepItm->pPrev; + pList->pLast->pNext = NULL; + } + + // insert before the current item + if (pRoot->pPrev) + pRoot->pPrev->pNext = pDepItm; + else + pList->pFirst = pDepItm; + pDepItm->pPrev = pRoot->pPrev; + pRoot->pPrev = pDepItm; + pDepItm->pNext = pRoot; + } + } + + hr = S_OK; + +LExit: + return hr; +} + +static HRESULT ModuleFindByKey( + CPI_MODULE* pItm, + LPCWSTR pwzKey, + BOOL fReverse, + CPI_MODULE** ppItm + ) +{ + for (; pItm; pItm = fReverse ? pItm->pPrev : pItm->pNext) + { + if (0 == lstrcmpW(pItm->wzKey, pwzKey)) + { + *ppItm = pItm; + return S_OK; + } + } + + return S_FALSE; +} + +static void SortAssemblyListByModule( + CPI_MODULE_LIST* pModList, + CPI_ASSEMBLY_LIST* pAsmList + ) +{ + CPI_ASSEMBLY* pMoved = NULL; // first moved item + + // loop modules in reverse order + for (CPI_MODULE* pMod = pModList->pLast; pMod; pMod = pMod->pPrev) + { + // loop assemblies in forward order, starting with the first unmoved item + CPI_ASSEMBLY* pAsm = pMoved ? pMoved->pNext : pAsmList->pFirst; + while (pAsm) + { + CPI_ASSEMBLY* pNext = pAsm->pNext; + + // check if assembly belongs to the current module + if (0 == lstrcmpW(pMod->wzKey, pAsm->wzModule)) + { + // if the item is not already first in the list + if (pAsm->pPrev) + { + // remove item from it's current position + pAsm->pPrev->pNext = pAsm->pNext; + if (pAsm->pNext) + pAsm->pNext->pPrev = pAsm->pPrev; + else + pAsmList->pLast = pAsm->pPrev; + + // insert item first in the list + pAsmList->pFirst->pPrev = pAsm; + pAsm->pNext = pAsmList->pFirst; + pAsm->pPrev = NULL; + pAsmList->pFirst = pAsm; + } + + // if we haven't moved any items yet, this is the first moved item + if (!pMoved) + pMoved = pAsm; + } + + pAsm = pNext; + } + } +} + +static HRESULT TopSortAssemblyList( + CPI_KEY_PAIR* pDepList, + CPI_ASSEMBLY_LIST* pList + ) +{ + HRESULT hr = S_OK; + + // top sort list + for (CPI_ASSEMBLY* pItm = pList->pFirst; pItm; pItm = pItm->pNext) + { + // append module + hr = SwapDependentAssemblies(NULL, pDepList, pList, pItm, pItm); + ExitOnFailure(hr, "Failed to swap dependent assemblies"); + } + + hr = S_OK; + +LExit: + return hr; +} + +static HRESULT SwapDependentAssemblies( + CPI_DEPENDENCY_CHAIN* pdcPrev, // list containing the entire dependency chain + CPI_KEY_PAIR* pDepList, // assembly dependency list + CPI_ASSEMBLY_LIST* pList, // assembly list being sorted + CPI_ASSEMBLY* pRoot, // first assembly in the chain + CPI_ASSEMBLY* pItm // current assembly to test for dependencies + ) +{ + HRESULT hr = S_OK; + + CPI_ASSEMBLY* pDepItm; + + // find dependencies + for (CPI_KEY_PAIR* pDep = pDepList; pDep; pDep = pDep->pNext) + { + if (0 == lstrcmpW(pItm->wzKey, pDep->wzFirstKey)) + { + CPI_DEPENDENCY_CHAIN dcItm; + dcItm.pwzKey = pItm->wzKey; + dcItm.pPrev = pdcPrev; + + // check for circular dependencies + for (CPI_DEPENDENCY_CHAIN* pdcItm = &dcItm; pdcItm; pdcItm = pdcItm->pPrev) + { + if (0 == lstrcmpW(pdcItm->pwzKey, pDep->wzSecondKey)) + { + // circular dependency found + ExitOnFailure(hr = E_FAIL, "Circular assembly dependency found, key: %S", pDep->wzSecondKey); + } + } + + // make sure the item is not already in the list + hr = AssemblyFindByKey(pRoot->pPrev, pDep->wzSecondKey, TRUE, &pDepItm); // find in reverse order + + if (S_OK == hr) + continue; // item found, move on + + // find item in the list + hr = AssemblyFindByKey(pRoot->pNext, pDep->wzSecondKey, FALSE, &pDepItm); // find in forward order + + if (S_FALSE == hr) + { + // not found + ExitOnFailure(hr = E_FAIL, "Assembly dependency not found, key: %S", pDep->wzSecondKey); + } + + // if the root item belongs to a module, this item must also belong to the same module + if (*pItm->wzModule) + { + if (0 != lstrcmpW(pDepItm->wzModule, pItm->wzModule)) + ExitOnFailure(hr = E_FAIL, "An assembly dependency can only exist between two assemblies not belonging to modules, or belonging to the same module. assembly: %S, required assembly: %S", pItm->wzKey, pDepItm->wzKey); + } + + // if this item in turn has dependencies, they have to be swaped first + hr = SwapDependentAssemblies(&dcItm, pDepList, pList, pRoot, pDepItm); + ExitOnFailure(hr, "Failed to swap dependent assemblies"); + + // remove item from its current position + pDepItm->pPrev->pNext = pDepItm->pNext; // pDepItm can never be the first item, no need to check pPrev + if (pDepItm->pNext) + pDepItm->pNext->pPrev = pDepItm->pPrev; + else + { + pList->pLast = pDepItm->pPrev; + pList->pLast->pNext = NULL; + } + + // insert before the current item + if (pRoot->pPrev) + pRoot->pPrev->pNext = pDepItm; + else + pList->pFirst = pDepItm; + pDepItm->pPrev = pRoot->pPrev; + pRoot->pPrev = pDepItm; + pDepItm->pNext = pRoot; + } + } + + hr = S_OK; + +LExit: + return hr; +} + +static HRESULT AssemblyFindByKey( + CPI_ASSEMBLY* pItm, + LPCWSTR pwzKey, + BOOL fReverse, + CPI_ASSEMBLY** ppItm + ) +{ + for (; pItm; pItm = fReverse ? pItm->pPrev : pItm->pNext) + { + if (0 == lstrcmpW(pItm->wzKey, pwzKey)) + { + *ppItm = pItm; + return S_OK; + } + } + + return S_FALSE; +} + +static HRESULT ComponentsRead( + LPCWSTR pwzAsmKey, + CPI_APPLICATION_ROLE_LIST* pAppRoleList, + CPI_ASSEMBLY* pAsm + ) +{ + HRESULT hr = S_OK; + PMSIHANDLE hView; + PMSIHANDLE hRec; + PMSIHANDLE hRecKey; + CPI_COMPONENT* pItm = NULL; + LPWSTR pwzData = NULL; + + // create parameter record + hRecKey = ::MsiCreateRecord(1); + ExitOnNull(hRecKey, hr, E_OUTOFMEMORY, "Failed to create record"); + hr = WcaSetRecordString(hRecKey, 1, pwzAsmKey); + ExitOnFailure(hr, "Failed to set record string"); + + // open view + hr = WcaOpenView(vcsComponentQuery, &hView); + ExitOnFailure(hr, "Failed to open view on ComPlusComponent table"); + hr = WcaExecuteView(hView, hRecKey); + ExitOnFailure(hr, "Failed to execute view on ComPlusComponent table"); + + while (S_OK == (hr = WcaFetchRecord(hView, &hRec))) + { + // create entry + pItm = (CPI_COMPONENT*)::HeapAlloc(::GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(CPI_COMPONENT)); + if (!pItm) + ExitFunction1(hr = E_OUTOFMEMORY); + + // get key + hr = WcaGetRecordString(hRec, cqComponent, &pwzData); + ExitOnFailure(hr, "Failed to get key"); + StringCchCopyW(pItm->wzKey, countof(pItm->wzKey), pwzData); + + // get clsid + hr = WcaGetRecordFormattedString(hRec, cqCLSID, &pwzData); + ExitOnFailure(hr, "Failed to get clsid"); + StringCchCopyW(pItm->wzCLSID, countof(pItm->wzCLSID), pwzData); + + // read properties + if (CpiTableExists(cptComPlusComponentProperty)) + { + hr = CpiPropertiesRead(vcsComponentPropertyQuery, pItm->wzKey, pdlComponentProperties, &pItm->pProperties, &pItm->iPropertyCount); + ExitOnFailure(hr, "Failed to get component properties"); + } + + // read roles + if (CpiTableExists(cptComPlusRoleForComponent)) + { + hr = RoleAssignmentsRead(vcsRoleForComponentQuery, pItm->wzKey, pAppRoleList, &pItm->pRoles, &pItm->iRoleInstallCount, &pItm->iRoleUninstallCount); + ExitOnFailure(hr, "Failed to get roles for component"); + } + + if (pItm->iRoleInstallCount) + pAsm->iRoleAssignmentsInstallCount++; + if (pItm->iRoleUninstallCount) + pAsm->iRoleAssignmentsUninstallCount++; + + // read interfaces + if (CpiTableExists(cptComPlusInterface)) + { + hr = InterfacesRead(pItm->wzKey, pAppRoleList, pAsm, pItm); + ExitOnFailure(hr, "Failed to get interfaces for component"); + } + + // add entry + pAsm->iComponentCount++; + if (pAsm->pComponents) + pItm->pNext = pAsm->pComponents; + pAsm->pComponents = pItm; + pItm = NULL; + } + + if (E_NOMOREITEMS == hr) + hr = S_OK; + +LExit: + // clean up + if (pItm) + ComponentsFreeList(pItm); + + ReleaseStr(pwzData); + + return hr; +} + +static HRESULT InterfacesRead( + LPCWSTR pwzCompKey, + CPI_APPLICATION_ROLE_LIST* pAppRoleList, + CPI_ASSEMBLY* pAsm, + CPI_COMPONENT* pComp + ) +{ + HRESULT hr = S_OK; + PMSIHANDLE hView; + PMSIHANDLE hRec; + PMSIHANDLE hRecKey; + CPI_INTERFACE* pItm = NULL; + LPWSTR pwzData = NULL; + + // create parameter record + hRecKey = ::MsiCreateRecord(1); + ExitOnNull(hRecKey, hr, E_OUTOFMEMORY, "Failed to create record"); + hr = WcaSetRecordString(hRecKey, 1, pwzCompKey); + ExitOnFailure(hr, "Failed to set record string"); + + // open view + hr = WcaOpenView(vcsInterfaceQuery, &hView); + ExitOnFailure(hr, "Failed to open view on ComPlusInterface table"); + hr = WcaExecuteView(hView, hRecKey); + ExitOnFailure(hr, "Failed to execute view on ComPlusInterface table"); + + while (S_OK == (hr = WcaFetchRecord(hView, &hRec))) + { + // create entry + pItm = (CPI_INTERFACE*)::HeapAlloc(::GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(CPI_INTERFACE)); + if (!pItm) + ExitFunction1(hr = E_OUTOFMEMORY); + + // get key + hr = WcaGetRecordString(hRec, iqInterface, &pwzData); + ExitOnFailure(hr, "Failed to get key"); + StringCchCopyW(pItm->wzKey, countof(pItm->wzKey), pwzData); + + // get iid + hr = WcaGetRecordFormattedString(hRec, iqIID, &pwzData); + ExitOnFailure(hr, "Failed to get iid"); + StringCchCopyW(pItm->wzIID, countof(pItm->wzIID), pwzData); + + // read properties + if (CpiTableExists(cptComPlusInterfaceProperty)) + { + hr = CpiPropertiesRead(vcsInterfacePropertyQuery, pItm->wzKey, pdlInterfaceProperties, &pItm->pProperties, &pItm->iPropertyCount); + ExitOnFailure(hr, "Failed to get interface properties"); + } + + // read roles + if (CpiTableExists(cptComPlusRoleForInterface)) + { + hr = RoleAssignmentsRead(vcsRoleForInterfaceQuery, pItm->wzKey, pAppRoleList, &pItm->pRoles, &pItm->iRoleInstallCount, &pItm->iRoleUninstallCount); + ExitOnFailure(hr, "Failed to get roles for interface"); + } + + if (pItm->iRoleInstallCount) + pAsm->iRoleAssignmentsInstallCount++; + if (pItm->iRoleUninstallCount) + pAsm->iRoleAssignmentsUninstallCount++; + + // read methods + if (CpiTableExists(cptComPlusMethod)) + { + hr = MethodsRead(pItm->wzKey, pAppRoleList, pAsm, pItm); + ExitOnFailure(hr, "Failed to get methods for interface"); + } + + // add entry + pComp->iInterfaceCount++; + if (pComp->pInterfaces) + pItm->pNext = pComp->pInterfaces; + pComp->pInterfaces = pItm; + pItm = NULL; + } + + if (E_NOMOREITEMS == hr) + hr = S_OK; + +LExit: + // clean up + if (pItm) + InterfacesFreeList(pItm); + + ReleaseStr(pwzData); + + return hr; +} + +static HRESULT MethodsRead( + LPCWSTR pwzIntfKey, + CPI_APPLICATION_ROLE_LIST* pAppRoleList, + CPI_ASSEMBLY* pAsm, + CPI_INTERFACE* pIntf + ) +{ + HRESULT hr = S_OK; + PMSIHANDLE hView, hRec, hRecKey; + CPI_METHOD* pItm = NULL; + LPWSTR pwzData = NULL; + + // create parameter record + hRecKey = ::MsiCreateRecord(1); + ExitOnNull(hRecKey, hr, E_OUTOFMEMORY, "Failed to create record"); + hr = WcaSetRecordString(hRecKey, 1, pwzIntfKey); + ExitOnFailure(hr, "Failed to set record string"); + + // open view + hr = WcaOpenView(vcsMethodQuery, &hView); + ExitOnFailure(hr, "Failed to open view on ComPlusMethod table"); + hr = WcaExecuteView(hView, hRecKey); + ExitOnFailure(hr, "Failed to execute view on ComPlusMethod table"); + + while (S_OK == (hr = WcaFetchRecord(hView, &hRec))) + { + // create entry + pItm = (CPI_METHOD*)::HeapAlloc(::GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(CPI_METHOD)); + if (!pItm) + ExitFunction1(hr = E_OUTOFMEMORY); + + // get key + hr = WcaGetRecordString(hRec, iqInterface, &pwzData); + ExitOnFailure(hr, "Failed to get key"); + StringCchCopyW(pItm->wzKey, countof(pItm->wzKey), pwzData); + + // get index + hr = WcaGetRecordFormattedString(hRec, mqIndex, &pwzData); + ExitOnFailure(hr, "Failed to get index"); + StringCchCopyW(pItm->wzIndex, countof(pItm->wzIndex), pwzData); + + // get name + hr = WcaGetRecordFormattedString(hRec, mqName, &pwzData); + ExitOnFailure(hr, "Failed to get name"); + StringCchCopyW(pItm->wzName, countof(pItm->wzName), pwzData); + + // either an index or a name must be provided + if (!*pItm->wzIndex && !*pItm->wzName) + ExitOnFailure(hr = E_FAIL, "A method must have either an index or a name associated, key: %S", pItm->wzKey); + + // read properties + if (CpiTableExists(cptComPlusMethodProperty)) + { + hr = CpiPropertiesRead(vcsMethodPropertyQuery, pItm->wzKey, pdlMethodProperties, &pItm->pProperties, &pItm->iPropertyCount); + ExitOnFailure(hr, "Failed to get method properties"); + } + + // read roles + if (CpiTableExists(cptComPlusRoleForMethod)) + { + hr = RoleAssignmentsRead(vcsRoleForMethodQuery, pItm->wzKey, pAppRoleList, &pItm->pRoles, &pItm->iRoleInstallCount, &pItm->iRoleUninstallCount); + ExitOnFailure(hr, "Failed to get roles for method"); + } + + if (pItm->iRoleInstallCount) + pAsm->iRoleAssignmentsInstallCount++; + if (pItm->iRoleUninstallCount) + pAsm->iRoleAssignmentsUninstallCount++; + + // add entry + pIntf->iMethodCount++; + if (pIntf->pMethods) + pItm->pNext = pIntf->pMethods; + pIntf->pMethods = pItm; + pItm = NULL; + } + + if (E_NOMOREITEMS == hr) + hr = S_OK; + +LExit: + // clean up + if (pItm) + MethodsFreeList(pItm); + + ReleaseStr(pwzData); + + return hr; +} + +static HRESULT RoleAssignmentsRead( + LPCWSTR pwzQuery, + LPCWSTR pwzKey, + CPI_APPLICATION_ROLE_LIST* pAppRoleList, + CPI_ROLE_ASSIGNMENT** ppRoleList, + int* piInstallCount, + int* piUninstallCount + ) +{ + HRESULT hr = S_OK; + UINT er = ERROR_SUCCESS; + + PMSIHANDLE hView, hRec, hRecKey; + + CPI_ROLE_ASSIGNMENT* pItm = NULL; + LPWSTR pwzData = NULL; + BOOL fMatchingArchitecture = FALSE; + + // create parameter record + hRecKey = ::MsiCreateRecord(1); + ExitOnNull(hRecKey, hr, E_OUTOFMEMORY, "Failed to create record"); + hr = WcaSetRecordString(hRecKey, 1, pwzKey); + ExitOnFailure(hr, "Failed to set record string"); + + // open view + hr = WcaOpenView(pwzQuery, &hView); + ExitOnFailure(hr, "Failed to open view on role assignment table"); + hr = WcaExecuteView(hView, hRecKey); + ExitOnFailure(hr, "Failed to execute view on role assignment table"); + + while (S_OK == (hr = WcaFetchRecord(hView, &hRec))) + { + // get component + hr = WcaGetRecordString(hRec, raqComponent, &pwzData); + ExitOnFailure(hr, "Failed to get assembly component"); + + // check if the component is our processor architecture + hr = CpiVerifyComponentArchitecure(pwzData, &fMatchingArchitecture); + ExitOnFailure(hr, "Failed to get component architecture."); + + if (!fMatchingArchitecture) + { + continue; // not the same architecture, ignore + } + + // create entry + pItm = (CPI_ROLE_ASSIGNMENT*)::HeapAlloc(::GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(CPI_ROLE_ASSIGNMENT)); + if (!pItm) + ExitFunction1(hr = E_OUTOFMEMORY); + + // get component install state + er = ::MsiGetComponentStateW(WcaGetInstallHandle(), pwzData, &pItm->isInstalled, &pItm->isAction); + ExitOnFailure(hr = HRESULT_FROM_WIN32(er), "Failed to get component state"); + + // get key + hr = WcaGetRecordString(hRec, raqKey, &pwzData); + ExitOnFailure(hr, "Failed to get key"); + StringCchCopyW(pItm->wzKey, countof(pItm->wzKey), pwzData); + + // get application role + hr = WcaGetRecordString(hRec, raqApplicationRole, &pwzData); + ExitOnFailure(hr, "Failed to get application role"); + + hr = CpiApplicationRoleFindByKey(pAppRoleList, pwzData, &pItm->pApplicationRole); + if (S_FALSE == hr) + hr = HRESULT_FROM_WIN32(ERROR_NOT_FOUND); + ExitOnFailure(hr, "Failed to find application, key: %S", pwzData); + + // set references & increment counters + if (WcaIsInstalling(pItm->isInstalled, pItm->isAction)) + { + CpiApplicationRoleAddReferenceInstall(pItm->pApplicationRole); + ++*piInstallCount; + } + if (WcaIsUninstalling(pItm->isInstalled, pItm->isAction)) + { + CpiApplicationRoleAddReferenceUninstall(pItm->pApplicationRole); + ++*piUninstallCount; + } + + // add entry + if (*ppRoleList) + pItm->pNext = *ppRoleList; + *ppRoleList = pItm; + pItm = NULL; + } + + if (E_NOMOREITEMS == hr) + hr = S_OK; + +LExit: + // clean up + if (pItm) + RoleAssignmentsFreeList(pItm); + + ReleaseStr(pwzData); + + return hr; +} + +static HRESULT AddAssemblyToActionData( + CPI_ASSEMBLY* pItm, + BOOL fInstall, + int iActionType, + int iActionCost, + LPWSTR* ppwzActionData + ) +{ + HRESULT hr = S_OK; + + // add action information to custom action data + hr = WcaWriteIntegerToCaData(iActionType, ppwzActionData); + ExitOnFailure(hr, "Failed to add action type to custom action data"); + hr = WcaWriteIntegerToCaData(iActionCost, ppwzActionData); + ExitOnFailure(hr, "Failed to add action cost to custom action data"); + + // add assembly information to custom action data + hr = WcaWriteStringToCaData(pItm->wzKey, ppwzActionData); + ExitOnFailure(hr, "Failed to add assembly key to custom action data"); + hr = WcaWriteStringToCaData(pItm->pwzAssemblyName, ppwzActionData); + ExitOnFailure(hr, "Failed to add assembly name to custom action data"); + hr = WcaWriteStringToCaData(pItm->pwzDllPath, ppwzActionData); + ExitOnFailure(hr, "Failed to add assembly dll path to custom action data"); + hr = WcaWriteStringToCaData(atCreate == iActionType ? pItm->pwzTlbPath : L"", ppwzActionData); + ExitOnFailure(hr, "Failed to add assembly tlb path to custom action data"); + hr = WcaWriteStringToCaData(atCreate == iActionType ? pItm->pwzPSDllPath : L"", ppwzActionData); + ExitOnFailure(hr, "Failed to add assembly proxy-stub dll path to custom action data"); + hr = WcaWriteIntegerToCaData(pItm->iAttributes, ppwzActionData); + ExitOnFailure(hr, "Failed to add assembly attributes to custom action data"); + + // add application information to custom action data + hr = WcaWriteStringToCaData(pItm->pApplication ? pItm->pApplication->wzID : L"", ppwzActionData); + ExitOnFailure(hr, "Failed to add application id to custom action data"); + + // add partition information to custom action data + LPCWSTR pwzPartID = pItm->pApplication && pItm->pApplication->pPartition ? pItm->pApplication->pPartition->wzID : L""; + hr = WcaWriteStringToCaData(pwzPartID, ppwzActionData); + ExitOnFailure(hr, "Failed to add partition id to custom action data"); + + // add components to custom action data + // + // components are needed acording to the following table: + // + // Native .NET + // -------------------------------------------- + // NoOp | No | No + // Create | Yes | Yes + // Remove | Yes | No + // + int iCompCount = (atCreate == iActionType || (atRemove == iActionType && 0 == (pItm->iAttributes & aaDotNetAssembly))) ? pItm->iComponentCount : 0; + hr = WcaWriteIntegerToCaData(iCompCount, ppwzActionData); + ExitOnFailure(hr, "Failed to add component count to custom action data, key: %S", pItm->wzKey); + + if (iCompCount) + { + for (CPI_COMPONENT* pComp = pItm->pComponents; pComp; pComp = pComp->pNext) + { + hr = AddComponentToActionData(pComp, fInstall, atCreate == iActionType, FALSE, ppwzActionData); + ExitOnFailure(hr, "Failed to add component to custom action data, component: %S", pComp->wzKey); + } + } + + hr = S_OK; + +LExit: + return hr; +} + +static HRESULT AddRoleAssignmentsToActionData( + CPI_ASSEMBLY* pItm, + BOOL fInstall, + int iActionType, + int iActionCost, + LPWSTR* ppwzActionData + ) +{ + HRESULT hr = S_OK; + + // add action information to custom action data + hr = WcaWriteIntegerToCaData(iActionType, ppwzActionData); + ExitOnFailure(hr, "Failed to add action type to custom action data"); + hr = WcaWriteIntegerToCaData(iActionCost, ppwzActionData); + ExitOnFailure(hr, "Failed to add action cost to custom action data"); + + // add assembly information to custom action data + hr = WcaWriteStringToCaData(pItm->wzKey, ppwzActionData); + ExitOnFailure(hr, "Failed to add assembly key to custom action data"); + hr = WcaWriteIntegerToCaData(fInstall ? pItm->iRoleAssignmentsInstallCount : pItm->iRoleAssignmentsUninstallCount, ppwzActionData); + ExitOnFailure(hr, "Failed to add role assignments count to custom action data"); + + // add application information to custom action data + hr = WcaWriteStringToCaData(pItm->pApplication ? pItm->pApplication->wzID : L"", ppwzActionData); + ExitOnFailure(hr, "Failed to add application id to custom action data"); + + // add partition information to custom action data + LPCWSTR pwzPartID = pItm->pApplication && pItm->pApplication->pPartition ? pItm->pApplication->pPartition->wzID : L""; + hr = WcaWriteStringToCaData(pwzPartID, ppwzActionData); + ExitOnFailure(hr, "Failed to add partition id to custom action data"); + + // add components to custom action data + hr = WcaWriteIntegerToCaData(pItm->iComponentCount, ppwzActionData); + ExitOnFailure(hr, "Failed to add component count to custom action data"); + + for (CPI_COMPONENT* pComp = pItm->pComponents; pComp; pComp = pComp->pNext) + { + hr = AddComponentToActionData(pComp, fInstall, FALSE, TRUE, ppwzActionData); + ExitOnFailure(hr, "Failed to add component to custom action data, component: %S", pComp->wzKey); + } + + hr = S_OK; + +LExit: + return hr; +} + +static HRESULT AddComponentToActionData( + CPI_COMPONENT* pItm, + BOOL fInstall, + BOOL fProps, + BOOL fRoles, + LPWSTR* ppwzActionData + ) +{ + HRESULT hr = S_OK; + + // add component information to custom action data + hr = WcaWriteStringToCaData(pItm->wzCLSID, ppwzActionData); + ExitOnFailure(hr, "Failed to add component CLSID to custom action data"); + + // add properties to custom action data + hr = CpiAddPropertiesToActionData(fProps ? pItm->iPropertyCount : 0, pItm->pProperties, ppwzActionData); + ExitOnFailure(hr, "Failed to add properties to custom action data"); + + // add roles to custom action data + hr = AddRolesToActionData(pItm->iRoleInstallCount, pItm->iRoleUninstallCount, pItm->pRoles, fInstall, fRoles, ppwzActionData); + ExitOnFailure(hr, "Failed to add roles to custom action data"); + + // add interfaces to custom action data + int iIntfCount = (fProps || fRoles) ? pItm->iInterfaceCount : 0; + hr = WcaWriteIntegerToCaData(iIntfCount, ppwzActionData); + ExitOnFailure(hr, "Failed to add interface count to custom action data"); + + if (iIntfCount) + { + for (CPI_INTERFACE* pIntf = pItm->pInterfaces; pIntf; pIntf = pIntf->pNext) + { + hr = AddInterfaceToActionData(pIntf, fInstall, fProps, fRoles, ppwzActionData); + ExitOnFailure(hr, "Failed to add interface custom action data, interface: %S", pIntf->wzKey); + } + } + + hr = S_OK; + +LExit: + return hr; +} + +static HRESULT AddInterfaceToActionData( + CPI_INTERFACE* pItm, + BOOL fInstall, + BOOL fProps, + BOOL fRoles, + LPWSTR* ppwzActionData + ) +{ + HRESULT hr = S_OK; + + // add interface information to custom action data + hr = WcaWriteStringToCaData(pItm->wzIID, ppwzActionData); + ExitOnFailure(hr, "Failed to add interface IID to custom action data"); + + // add properties to custom action data + hr = CpiAddPropertiesToActionData(fProps ? pItm->iPropertyCount : 0, pItm->pProperties, ppwzActionData); + ExitOnFailure(hr, "Failed to add properties to custom action data"); + + // add roles to custom action data + hr = AddRolesToActionData(pItm->iRoleInstallCount, pItm->iRoleUninstallCount, pItm->pRoles, fInstall, fRoles, ppwzActionData); + ExitOnFailure(hr, "Failed to add roles to custom action data"); + + // add methods to custom action data + hr = WcaWriteIntegerToCaData(pItm->iMethodCount, ppwzActionData); + ExitOnFailure(hr, "Failed to add method count to custom action data"); + + for (CPI_METHOD* pMeth = pItm->pMethods; pMeth; pMeth = pMeth->pNext) + { + hr = AddMethodToActionData(pMeth, fInstall, fProps, fRoles, ppwzActionData); + ExitOnFailure(hr, "Failed to add method custom action data, method: %S", pMeth->wzKey); + } + + hr = S_OK; + +LExit: + return hr; +} + +static HRESULT AddMethodToActionData( + CPI_METHOD* pItm, + BOOL fInstall, + BOOL fProps, + BOOL fRoles, + LPWSTR* ppwzActionData + ) +{ + HRESULT hr = S_OK; + + // add interface information to custom action data + hr = WcaWriteStringToCaData(pItm->wzIndex, ppwzActionData); + ExitOnFailure(hr, "Failed to add method index to custom action data"); + + hr = WcaWriteStringToCaData(pItm->wzName, ppwzActionData); + ExitOnFailure(hr, "Failed to add method name to custom action data"); + + // add properties to custom action data + hr = CpiAddPropertiesToActionData(fProps ? pItm->iPropertyCount : 0, pItm->pProperties, ppwzActionData); + ExitOnFailure(hr, "Failed to add properties to custom action data"); + + // add roles to custom action data + hr = AddRolesToActionData(pItm->iRoleInstallCount, pItm->iRoleUninstallCount, pItm->pRoles, fInstall, fRoles, ppwzActionData); + ExitOnFailure(hr, "Failed to add roles to custom action data"); + + hr = S_OK; + +LExit: + return hr; +} + +static HRESULT AddRolesToActionData( + int iRoleInstallCount, + int iRoleUninstallCount, + CPI_ROLE_ASSIGNMENT* pRoleList, + BOOL fInstall, + BOOL fRoles, + LPWSTR* ppwzActionData + ) +{ + HRESULT hr = S_OK; + + int iRoleCount = fRoles ? (fInstall ? iRoleInstallCount : iRoleUninstallCount) : 0; + hr = WcaWriteIntegerToCaData(iRoleCount, ppwzActionData); + ExitOnFailure(hr, "Failed to add role count to custom action data"); + + if (iRoleCount) + { + for (CPI_ROLE_ASSIGNMENT* pRole = pRoleList; pRole; pRole = pRole->pNext) + { + // make sure the install state matches the create flag + if (fInstall ? !WcaIsInstalling(pRole->isInstalled, pRole->isAction) : !WcaIsUninstalling(pRole->isInstalled, pRole->isAction)) + continue; + + hr = WcaWriteStringToCaData(pRole->pApplicationRole->wzKey, ppwzActionData); + ExitOnFailure(hr, "Failed to add key to custom action data, role: %S", pRole->wzKey); + + hr = WcaWriteStringToCaData(pRole->pApplicationRole->wzName, ppwzActionData); + ExitOnFailure(hr, "Failed to add role name to custom action data, role: %S", pRole->wzKey); + } + } + + hr = S_OK; + +LExit: + return hr; +} + +static HRESULT KeyPairFindByFirstKey( + CPI_KEY_PAIR* pList, + LPCWSTR pwzKey, + CPI_KEY_PAIR** ppItm + ) +{ + for (; pList; pList = pList->pNext) + { + if (0 == lstrcmpW(pList->wzFirstKey, pwzKey)) + { + *ppItm = pList; + return S_OK; + } + } + + return S_FALSE; +} + +static void AssemblyFree( + CPI_ASSEMBLY* pItm + ) +{ + ReleaseStr(pItm->pwzAssemblyName); + ReleaseStr(pItm->pwzDllPath); + ReleaseStr(pItm->pwzTlbPath); + ReleaseStr(pItm->pwzPSDllPath); + + if (pItm->pComponents) + ComponentsFreeList(pItm->pComponents); + + ::HeapFree(::GetProcessHeap(), 0, pItm); +} + +static void KeyPairsFreeList( + CPI_KEY_PAIR* pList + ) +{ + while (pList) + { + CPI_KEY_PAIR* pDelete = pList; + pList = pList->pNext; + ::HeapFree(::GetProcessHeap(), 0, pDelete); + } +} + +void ModuleListFree( + CPI_MODULE_LIST* pList + ) +{ + CPI_MODULE* pItm = pList->pFirst; + + while (pItm) + { + CPI_MODULE* pDelete = pItm; + pItm = pItm->pNext; + ModuleFree(pDelete); + } +} + +static void ModuleFree( + CPI_MODULE* pItm + ) +{ + ::HeapFree(::GetProcessHeap(), 0, pItm); +} + +static void ComponentsFreeList( + CPI_COMPONENT* pList + ) +{ + while (pList) + { + if (pList->pProperties) + CpiPropertiesFreeList(pList->pProperties); + + if (pList->pRoles) + RoleAssignmentsFreeList(pList->pRoles); + + if (pList->pInterfaces) + InterfacesFreeList(pList->pInterfaces); + + ReleaseObject(pList->piSubsColl); + + CPI_COMPONENT* pDelete = pList; + pList = pList->pNext; + ::HeapFree(::GetProcessHeap(), 0, pDelete); + } +} + +static void InterfacesFreeList( + CPI_INTERFACE* pList + ) +{ + while (pList) + { + if (pList->pProperties) + CpiPropertiesFreeList(pList->pProperties); + + if (pList->pRoles) + RoleAssignmentsFreeList(pList->pRoles); + + if (pList->pMethods) + MethodsFreeList(pList->pMethods); + + CPI_INTERFACE* pDelete = pList; + pList = pList->pNext; + ::HeapFree(::GetProcessHeap(), 0, pDelete); + } +} + +static void MethodsFreeList( + CPI_METHOD* pList + ) +{ + while (pList) + { + if (pList->pProperties) + CpiPropertiesFreeList(pList->pProperties); + + if (pList->pRoles) + RoleAssignmentsFreeList(pList->pRoles); + + CPI_METHOD* pDelete = pList; + pList = pList->pNext; + ::HeapFree(::GetProcessHeap(), 0, pDelete); + } +} + +static void RoleAssignmentsFreeList( + CPI_ROLE_ASSIGNMENT* pList + ) +{ + while (pList) + { + CPI_ROLE_ASSIGNMENT* pDelete = pList; + pList = pList->pNext; + ::HeapFree(::GetProcessHeap(), 0, pDelete); + } +} diff --git a/src/ca/cpasmsched.h b/src/ca/cpasmsched.h new file mode 100644 index 00000000..b5a68d7e --- /dev/null +++ b/src/ca/cpasmsched.h @@ -0,0 +1,168 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + + +enum eAssemblyAttributes +{ + aaEventClass = (1 << 0), + aaDotNetAssembly = (1 << 1), + aaPathFromGAC = (1 << 2), + aaRunInCommit = (1 << 3) +}; + + +// structs + +struct CPI_ROLE_ASSIGNMENT +{ + WCHAR wzKey[MAX_DARWIN_KEY + 1]; + + INSTALLSTATE isInstalled, isAction; + + CPI_APPLICATION_ROLE* pApplicationRole; + + CPI_ROLE_ASSIGNMENT* pNext; +}; + +struct CPI_METHOD +{ + WCHAR wzKey[MAX_DARWIN_KEY + 1]; + WCHAR wzIndex[11 + 1]; + WCHAR wzName[MAX_DARWIN_COLUMN + 1]; + + int iPropertyCount; + CPI_PROPERTY* pProperties; + + int iRoleInstallCount; + int iRoleUninstallCount; + CPI_ROLE_ASSIGNMENT* pRoles; + + CPI_METHOD* pNext; +}; + +struct CPI_INTERFACE +{ + WCHAR wzKey[MAX_DARWIN_KEY + 1]; + WCHAR wzIID[CPI_MAX_GUID + 1]; + + int iPropertyCount; + CPI_PROPERTY* pProperties; + + int iRoleInstallCount; + int iRoleUninstallCount; + CPI_ROLE_ASSIGNMENT* pRoles; + + int iMethodCount; + CPI_METHOD* pMethods; + + CPI_INTERFACE* pNext; +}; + +struct CPI_COMPONENT +{ + WCHAR wzKey[MAX_DARWIN_KEY + 1]; + WCHAR wzCLSID[CPI_MAX_GUID + 1]; + + int iPropertyCount; + CPI_PROPERTY* pProperties; + + int iRoleInstallCount; + int iRoleUninstallCount; + CPI_ROLE_ASSIGNMENT* pRoles; + + int iInterfaceCount; + CPI_INTERFACE* pInterfaces; + + ICatalogCollection* piSubsColl; + + CPI_COMPONENT* pNext; +}; + +struct CPI_ASSEMBLY +{ + WCHAR wzKey[MAX_DARWIN_KEY + 1]; + WCHAR wzModule[MAX_DARWIN_KEY + 1]; + LPWSTR pwzAssemblyName; + LPWSTR pwzDllPath; + LPWSTR pwzTlbPath; + LPWSTR pwzPSDllPath; + int iAttributes; + + int iComponentCount; + CPI_COMPONENT* pComponents; + + BOOL fReferencedForInstall; + BOOL fReferencedForUninstall; + BOOL fIgnore; + + int iRoleAssignmentsInstallCount; + int iRoleAssignmentsUninstallCount; + + INSTALLSTATE isInstalled, isAction; + + CPI_APPLICATION* pApplication; + + CPI_ASSEMBLY* pPrev; + CPI_ASSEMBLY* pNext; +}; + +struct CPI_ASSEMBLY_LIST +{ + CPI_ASSEMBLY* pFirst; + CPI_ASSEMBLY* pLast; + + int iInstallCount; + int iCommitCount; + int iUninstallCount; + + int iRoleInstallCount; + int iRoleCommitCount; + int iRoleUninstallCount; +}; + + +// function prototypes + +void CpiAssemblyListFree( + CPI_ASSEMBLY_LIST* pList + ); +HRESULT CpiAssembliesRead( + CPI_APPLICATION_LIST* pAppList, + CPI_APPLICATION_ROLE_LIST* pAppRoleList, + CPI_ASSEMBLY_LIST* pAsmList + ); +HRESULT CpiAssembliesVerifyInstall( + CPI_ASSEMBLY_LIST* pList + ); +HRESULT CpiAssembliesVerifyUninstall( + CPI_ASSEMBLY_LIST* pList + ); +HRESULT CpiAssembliesInstall( + CPI_ASSEMBLY_LIST* pList, + int iRunMode, + LPWSTR* ppwzActionData, + int* piProgress + ); +HRESULT CpiAssembliesUninstall( + CPI_ASSEMBLY_LIST* pList, + int iRunMode, + LPWSTR* ppwzActionData, + int* piProgress + ); +HRESULT CpiRoleAssignmentsInstall( + CPI_ASSEMBLY_LIST* pList, + int iRunMode, + LPWSTR* ppwzActionData, + int* piProgress + ); +HRESULT CpiRoleAssignmentsUninstall( + CPI_ASSEMBLY_LIST* pList, + int iRunMode, + LPWSTR* ppwzActionData, + int* piProgress + ); +HRESULT CpiGetSubscriptionsCollForComponent( + CPI_ASSEMBLY* pAsm, + CPI_COMPONENT* pComp, + ICatalogCollection** ppiSubsColl + ); diff --git a/src/ca/cpcost.h b/src/ca/cpcost.h new file mode 100644 index 00000000..7a23e03b --- /dev/null +++ b/src/ca/cpcost.h @@ -0,0 +1,30 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + + +#define COST_PARTITION_CREATE 10000 +#define COST_PARTITION_DELETE 10000 + +#define COST_PARTITION_USER_CREATE 10000 +#define COST_PARTITION_USER_DELETE 10000 + +#define COST_USER_IN_PARTITION_ROLE_CREATE 10000 +#define COST_USER_IN_PARTITION_ROLE_DELETE 10000 + +#define COST_APPLICATION_CREATE 10000 +#define COST_APPLICATION_DELETE 10000 + +#define COST_APPLICATION_ROLE_CREATE 10000 +#define COST_APPLICATION_ROLE_DELETE 10000 + +#define COST_USER_IN_APPLICATION_ROLE_CREATE 10000 +#define COST_USER_IN_APPLICATION_ROLE_DELETE 10000 + +#define COST_ASSEMBLY_REGISTER 50000 +#define COST_ASSEMBLY_UNREGISTER 10000 + +#define COST_ROLLASSIGNMENT_CREATE 10000 +#define COST_ROLLASSIGNMENT_DELETE 10000 + +#define COST_SUBSCRIPTION_CREATE 10000 +#define COST_SUBSCRIPTION_DELETE 10000 diff --git a/src/ca/cpexec.cpp b/src/ca/cpexec.cpp new file mode 100644 index 00000000..fa2446d8 --- /dev/null +++ b/src/ca/cpexec.cpp @@ -0,0 +1,704 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + +/******************************************************************** + DllMain - standard entry point for all WiX CustomActions + +********************************************************************/ +extern "C" BOOL WINAPI DllMain( + IN HINSTANCE hInst, + IN ULONG ulReason, + IN LPVOID) +{ + switch(ulReason) + { + case DLL_PROCESS_ATTACH: + WcaGlobalInitialize(hInst); + break; + + case DLL_PROCESS_DETACH: + WcaGlobalFinalize(); + break; + } + + return TRUE; +} + +/******************************************************************** + ComPlusPrepare - CUSTOM ACTION ENTRY POINT + + Input: deferred CustomActionData - ComPlusPrepare +********************************************************************/ +extern "C" UINT __stdcall ComPlusPrepare(MSIHANDLE hInstall) +{ + HRESULT hr = S_OK; + UINT er = ERROR_SUCCESS; + + LPWSTR pwzCustomActionData = NULL; + LPWSTR pwzData = NULL; + + HANDLE hRollbackFile = INVALID_HANDLE_VALUE; + + // initialize + hr = WcaInitialize(hInstall, "ComPlusPrepare"); + ExitOnFailure(hr, "Failed to initialize ComPlusPrepare"); + + // get custom action data + hr = WcaGetProperty(L"CustomActionData", &pwzCustomActionData); + ExitOnFailure(hr, "Failed to get CustomActionData"); + pwzData = pwzCustomActionData; + + // create rollback file + hRollbackFile = ::CreateFileW(pwzData, GENERIC_WRITE, 0, NULL, CREATE_NEW, FILE_ATTRIBUTE_TEMPORARY, NULL); + if (INVALID_HANDLE_VALUE == hRollbackFile) + ExitOnFailure(hr = HRESULT_FROM_WIN32(::GetLastError()), "Failed to create rollback file, name: %S", pwzData); + + hr = S_OK; + +LExit: + // clean up + ReleaseStr(pwzCustomActionData); + + if (INVALID_HANDLE_VALUE != hRollbackFile) + ::CloseHandle(hRollbackFile); + + er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE; + return WcaFinalize(er); +} + +/******************************************************************** + ComPlusCleanup - CUSTOM ACTION ENTRY POINT + + Input: deferred CustomActionData - ComPlusCleanup +********************************************************************/ +extern "C" UINT __stdcall ComPlusCleanup(MSIHANDLE hInstall) +{ + HRESULT hr = S_OK; + UINT er = ERROR_SUCCESS; + + LPWSTR pwzCustomActionData = NULL; + LPWSTR pwzData = NULL; + + // initialize + hr = WcaInitialize(hInstall, "ComPlusCleanup"); + ExitOnFailure(hr, "Failed to initialize ComPlusCleanup"); + + // get custom action data + hr = WcaGetProperty(L"CustomActionData", &pwzCustomActionData); + ExitOnFailure(hr, "Failed to get CustomActionData"); + pwzData = pwzCustomActionData; + + // delete rollback file + if (!::DeleteFileW(pwzData)) + { + // error, but not a showstopper + hr = HRESULT_FROM_WIN32(::GetLastError()); + WcaLog(LOGMSG_STANDARD, "Failed to delete rollback file, hr: 0x%x, name: %S", hr, pwzData); + } + + hr = S_OK; + +LExit: + // clean up + ReleaseStr(pwzCustomActionData); + + er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE; + return WcaFinalize(er); +} + +/******************************************************************** + ComPlusInstallExecute - CUSTOM ACTION ENTRY POINT + + Input: deferred CustomActionData - ComPlusInstallExecute +********************************************************************/ +extern "C" UINT __stdcall ComPlusInstallExecute(MSIHANDLE hInstall) +{ + HRESULT hr = S_OK; + UINT er = ERROR_SUCCESS; + + LPWSTR pwzCustomActionData = NULL; + LPWSTR pwzData = NULL; + LPWSTR pwzRollbackFileName = NULL; + + HANDLE hRollbackFile = INVALID_HANDLE_VALUE; + + BOOL fInitializedCom = FALSE; + + // initialize + hr = WcaInitialize(hInstall, "ComPlusInstallExecute"); + ExitOnFailure(hr, "Failed to initialize ComPlusInstallExecute"); + + hr = ::CoInitialize(NULL); + ExitOnFailure(hr, "Failed to initialize COM"); + fInitializedCom = TRUE; + + CpiInitialize(); + + // get custom action data + hr = WcaGetProperty(L"CustomActionData", &pwzCustomActionData); + ExitOnFailure(hr, "Failed to get CustomActionData"); + pwzData = pwzCustomActionData; + + // open rollback file + hr = WcaReadStringFromCaData(&pwzData, &pwzRollbackFileName); + ExitOnFailure(hr, "Failed to read rollback file name"); + + hRollbackFile = ::CreateFileW(pwzRollbackFileName, GENERIC_WRITE, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_TEMPORARY, NULL); + if (INVALID_HANDLE_VALUE == hRollbackFile) + ExitOnFailure(hr = HRESULT_FROM_WIN32(::GetLastError()), "Failed to open rollback file, name: %S", pwzRollbackFileName); + + // create partitions + hr = CpiConfigurePartitions(&pwzData, hRollbackFile); + ExitOnFailure(hr, "Failed to create partitions"); + if (S_FALSE == hr) ExitFunction(); + + // create users in partition roles + hr = CpiConfigureUsersInPartitionRoles(&pwzData, hRollbackFile); + ExitOnFailure(hr, "Failed to create users in partition roles"); + if (S_FALSE == hr) ExitFunction(); + + // create partition users + hr = CpiConfigurePartitionUsers(&pwzData, hRollbackFile); + ExitOnFailure(hr, "Failed to add partition users"); + if (S_FALSE == hr) ExitFunction(); + + // create applications + hr = CpiConfigureApplications(&pwzData, hRollbackFile); + ExitOnFailure(hr, "Failed to create applications"); + if (S_FALSE == hr) ExitFunction(); + + // create application roles + hr = CpiConfigureApplicationRoles(&pwzData, hRollbackFile); + ExitOnFailure(hr, "Failed to create application roles"); + if (S_FALSE == hr) ExitFunction(); + + // create users in application roles + hr = CpiConfigureUsersInApplicationRoles(&pwzData, hRollbackFile); + ExitOnFailure(hr, "Failed to create users in application roles"); + if (S_FALSE == hr) ExitFunction(); + + // register assemblies + hr = CpiConfigureAssemblies(&pwzData, hRollbackFile); + ExitOnFailure(hr, "Failed to register assemblies"); + if (S_FALSE == hr) ExitFunction(); + + // create role assignments + hr = CpiConfigureRoleAssignments(&pwzData, hRollbackFile); + ExitOnFailure(hr, "Failed to create role assignments"); + if (S_FALSE == hr) ExitFunction(); + + // create subscriptions + hr = CpiConfigureSubscriptions(&pwzData, hRollbackFile); + ExitOnFailure(hr, "Failed to create subscriptions"); + if (S_FALSE == hr) ExitFunction(); + + hr = S_OK; + +LExit: + // clean up + ReleaseStr(pwzCustomActionData); + ReleaseStr(pwzRollbackFileName); + + if (INVALID_HANDLE_VALUE != hRollbackFile) + ::CloseHandle(hRollbackFile); + + // unitialize + CpiFinalize(); + + if (fInitializedCom) + ::CoUninitialize(); + + er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE; + return WcaFinalize(er); +} + +/******************************************************************** + ComPlusInstallExecuteCommit - CUSTOM ACTION ENTRY POINT + + Input: deferred CustomActionData - ComPlusInstallExecuteCommit +********************************************************************/ +extern "C" UINT __stdcall ComPlusInstallExecuteCommit(MSIHANDLE hInstall) +{ + HRESULT hr = S_OK; + UINT er = ERROR_SUCCESS; + + LPWSTR pwzCustomActionData = NULL; + LPWSTR pwzData = NULL; + LPWSTR pwzRollbackFileName = NULL; + + HANDLE hRollbackFile = INVALID_HANDLE_VALUE; + + BOOL fInitializedCom = FALSE; + + // initialize + hr = WcaInitialize(hInstall, "ComPlusInstallExecuteCommit"); + ExitOnFailure(hr, "Failed to initialize ComPlusInstallExecuteCommit"); + + hr = ::CoInitialize(NULL); + ExitOnFailure(hr, "Failed to initialize COM"); + fInitializedCom = TRUE; + + CpiInitialize(); + + // get custom action data + hr = WcaGetProperty(L"CustomActionData", &pwzCustomActionData); + ExitOnFailure(hr, "Failed to get CustomActionData"); + pwzData = pwzCustomActionData; + + // open rollback file + hr = WcaReadStringFromCaData(&pwzData, &pwzRollbackFileName); + ExitOnFailure(hr, "Failed to read rollback file name"); + + hRollbackFile = ::CreateFileW(pwzRollbackFileName, GENERIC_WRITE, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_TEMPORARY, NULL); + if (INVALID_HANDLE_VALUE == hRollbackFile) + ExitOnFailure(hr = HRESULT_FROM_WIN32(::GetLastError()), "Failed to open rollback file, name: %S", pwzRollbackFileName); + + if (INVALID_SET_FILE_POINTER == ::SetFilePointer(hRollbackFile, 0, NULL, FILE_END)) + ExitOnFailure(hr = HRESULT_FROM_WIN32(::GetLastError()), "Failed to set file pointer"); + + // register assemblies + hr = CpiConfigureAssemblies(&pwzData, hRollbackFile); + ExitOnFailure(hr, "Failed to register assemblies"); + if (S_FALSE == hr) ExitFunction(); + + // create role assignments + hr = CpiConfigureRoleAssignments(&pwzData, hRollbackFile); + ExitOnFailure(hr, "Failed to create role assignments"); + if (S_FALSE == hr) ExitFunction(); + + // create subscriptions + hr = CpiConfigureSubscriptions(&pwzData, hRollbackFile); + ExitOnFailure(hr, "Failed to create subscriptions"); + if (S_FALSE == hr) ExitFunction(); + + hr = S_OK; + +LExit: + // clean up + ReleaseStr(pwzCustomActionData); + + if (INVALID_HANDLE_VALUE != hRollbackFile) + ::CloseHandle(hRollbackFile); + + // unitialize + CpiFinalize(); + + if (fInitializedCom) + ::CoUninitialize(); + + er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE; + return WcaFinalize(er); +} + +/******************************************************************** + ComPlusRollbackInstallExecute - CUSTOM ACTION ENTRY POINT + + Input: deferred CustomActionData - ComPlusRollbackInstallExecute +********************************************************************/ +extern "C" UINT __stdcall ComPlusRollbackInstallExecute(MSIHANDLE hInstall) +{ + HRESULT hr = S_OK; + UINT er = ERROR_SUCCESS; + + LPWSTR pwzCustomActionData = NULL; + LPWSTR pwzData = NULL; + LPWSTR pwzRollbackFileName = NULL; + + HANDLE hRollbackFile = INVALID_HANDLE_VALUE; + + CPI_ROLLBACK_DATA* prdPartitions = NULL; + CPI_ROLLBACK_DATA* prdUsersInPartitionRoles = NULL; + CPI_ROLLBACK_DATA* prdPartitionUsers = NULL; + CPI_ROLLBACK_DATA* prdApplications = NULL; + CPI_ROLLBACK_DATA* prdApplicationRoles = NULL; + CPI_ROLLBACK_DATA* prdUsersApplicationRoles = NULL; + CPI_ROLLBACK_DATA* prdAssemblies = NULL; + CPI_ROLLBACK_DATA* prdRoleAssignments = NULL; + CPI_ROLLBACK_DATA* prdSubscriptions = NULL; + + BOOL fInitializedCom = FALSE; + + // initialize + hr = WcaInitialize(hInstall, "ComPlusRollbackInstallExecute"); + ExitOnFailure(hr, "Failed to initialize ComPlusRollbackInstallExecute"); + + hr = ::CoInitialize(NULL); + ExitOnFailure(hr, "Failed to initialize COM"); + fInitializedCom = TRUE; + + CpiInitialize(); + + // get custom action data + hr = WcaGetProperty(L"CustomActionData", &pwzCustomActionData); + ExitOnFailure(hr, "Failed to get CustomActionData"); + pwzData = pwzCustomActionData; + + // open rollback file + hr = WcaReadStringFromCaData(&pwzData, &pwzRollbackFileName); + ExitOnFailure(hr, "Failed to read rollback file name"); + + hRollbackFile = ::CreateFileW(pwzRollbackFileName, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_TEMPORARY, NULL); + if (INVALID_HANDLE_VALUE == hRollbackFile) + ExitOnFailure(hr = HRESULT_FROM_WIN32(::GetLastError()), "Failed to open rollback file, name: %S", pwzRollbackFileName); + + // read rollback data (execute) + hr = CpiReadRollbackDataList(hRollbackFile, &prdPartitions); + ExitOnFailure(hr, "Failed to read partitions rollback data"); + hr = CpiReadRollbackDataList(hRollbackFile, &prdUsersInPartitionRoles); + ExitOnFailure(hr, "Failed to read users in partition roles rollback data"); + hr = CpiReadRollbackDataList(hRollbackFile, &prdPartitionUsers); + ExitOnFailure(hr, "Failed to read partition users rollback data"); + hr = CpiReadRollbackDataList(hRollbackFile, &prdApplications); + ExitOnFailure(hr, "Failed to read applications rollback data"); + hr = CpiReadRollbackDataList(hRollbackFile, &prdApplicationRoles); + ExitOnFailure(hr, "Failed to read application roles rollback data"); + hr = CpiReadRollbackDataList(hRollbackFile, &prdUsersApplicationRoles); + ExitOnFailure(hr, "Failed to read users in application roles rollback data"); + hr = CpiReadRollbackDataList(hRollbackFile, &prdAssemblies); + ExitOnFailure(hr, "Failed to read assemblies rollback data"); + hr = CpiReadRollbackDataList(hRollbackFile, &prdRoleAssignments); + ExitOnFailure(hr, "Failed to read role assignments rollback data"); + hr = CpiReadRollbackDataList(hRollbackFile, &prdSubscriptions); + ExitOnFailure(hr, "Failed to read subscription rollback data"); + + // read rollback data (commit) + hr = CpiReadRollbackDataList(hRollbackFile, &prdAssemblies); + ExitOnFailure(hr, "Failed to read assemblies rollback data (commit)"); + hr = CpiReadRollbackDataList(hRollbackFile, &prdRoleAssignments); + ExitOnFailure(hr, "Failed to read role assignments rollback data"); + hr = CpiReadRollbackDataList(hRollbackFile, &prdSubscriptions); + ExitOnFailure(hr, "Failed to read subscription rollback data (commit)"); + + ::CloseHandle(hRollbackFile); + hRollbackFile = INVALID_HANDLE_VALUE; + + // rollback create subscriptions + hr = CpiRollbackConfigureSubscriptions(&pwzData, prdSubscriptions); + ExitOnFailure(hr, "Failed to rollback create subscriptions"); + + // rollback create role assignments + hr = CpiRollbackConfigureRoleAssignments(&pwzData, prdRoleAssignments); + ExitOnFailure(hr, "Failed to rollback create role assignments"); + + // rollback register assemblies + hr = CpiRollbackConfigureAssemblies(&pwzData, prdAssemblies); + ExitOnFailure(hr, "Failed to rollback register assemblies"); + + // rollback create users in application roles + hr = CpiRollbackConfigureUsersInApplicationRoles(&pwzData, prdUsersApplicationRoles); + ExitOnFailure(hr, "Failed to rollback create users in application roles"); + + // rollback create application roles + hr = CpiRollbackConfigureApplicationRoles(&pwzData, prdApplicationRoles); + ExitOnFailure(hr, "Failed to rollback create application roles"); + + // rollback create applications + hr = CpiRollbackConfigureApplications(&pwzData, prdApplications); + ExitOnFailure(hr, "Failed to rollback create applications"); + + // rollback create partition users + hr = CpiRollbackConfigurePartitionUsers(&pwzData, prdPartitionUsers); + ExitOnFailure(hr, "Failed to rollback create partition users"); + + // rollback create users in partition roles + hr = CpiRollbackConfigureUsersInPartitionRoles(&pwzData, prdUsersInPartitionRoles); + ExitOnFailure(hr, "Failed to rollback create users in partition roles"); + + // rollback create partitions + hr = CpiRollbackConfigurePartitions(&pwzData, prdPartitions); + ExitOnFailure(hr, "Failed to rollback create partitions"); + + hr = S_OK; + +LExit: + // clean up + ReleaseStr(pwzCustomActionData); + ReleaseStr(pwzRollbackFileName); + + if (INVALID_HANDLE_VALUE != hRollbackFile) + ::CloseHandle(hRollbackFile); + + if (prdPartitions) + CpiFreeRollbackDataList(prdPartitions); + if (prdUsersInPartitionRoles) + CpiFreeRollbackDataList(prdUsersInPartitionRoles); + if (prdPartitionUsers) + CpiFreeRollbackDataList(prdPartitionUsers); + if (prdApplications) + CpiFreeRollbackDataList(prdApplications); + if (prdApplicationRoles) + CpiFreeRollbackDataList(prdApplicationRoles); + if (prdUsersApplicationRoles) + CpiFreeRollbackDataList(prdUsersApplicationRoles); + if (prdAssemblies) + CpiFreeRollbackDataList(prdAssemblies); + if (prdRoleAssignments) + CpiFreeRollbackDataList(prdRoleAssignments); + if (prdSubscriptions) + CpiFreeRollbackDataList(prdSubscriptions); + + // unitialize + CpiFinalize(); + + if (fInitializedCom) + ::CoUninitialize(); + + er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE; + return WcaFinalize(er); +} + +/******************************************************************** + ComPlusUninstallExecute - CUSTOM ACTION ENTRY POINT + + Input: deferred CustomActionData - ComPlusUninstallExecute +********************************************************************/ +extern "C" UINT __stdcall ComPlusUninstallExecute(MSIHANDLE hInstall) +{ + HRESULT hr = S_OK; + UINT er = ERROR_SUCCESS; + + LPWSTR pwzCustomActionData = NULL; + LPWSTR pwzData = NULL; + LPWSTR pwzRollbackFileName = NULL; + + HANDLE hRollbackFile = INVALID_HANDLE_VALUE; + + BOOL fInitializedCom = FALSE; + + // initialize + hr = WcaInitialize(hInstall, "ComPlusUninstallExecute"); + ExitOnFailure(hr, "Failed to initialize ComPlusUninstallExecute"); + + hr = ::CoInitialize(NULL); + ExitOnFailure(hr, "Failed to initialize COM"); + fInitializedCom = TRUE; + + CpiInitialize(); + + // get custom action data + hr = WcaGetProperty(L"CustomActionData", &pwzCustomActionData); + ExitOnFailure(hr, "Failed to get CustomActionData"); + pwzData = pwzCustomActionData; + + // open rollback file + hr = WcaReadStringFromCaData(&pwzData, &pwzRollbackFileName); + ExitOnFailure(hr, "Failed to read rollback file name"); + + hRollbackFile = ::CreateFileW(pwzRollbackFileName, GENERIC_WRITE, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_TEMPORARY, NULL); + if (INVALID_HANDLE_VALUE == hRollbackFile) + ExitOnFailure(hr = HRESULT_FROM_WIN32(::GetLastError()), "Failed to open rollback file, name: %S", pwzRollbackFileName); + + // delete subscriptions + hr = CpiConfigureSubscriptions(&pwzData, hRollbackFile); + ExitOnFailure(hr, "Failed to delete subscriptions"); + if (S_FALSE == hr) ExitFunction(); + + // delete role assignments + hr = CpiConfigureRoleAssignments(&pwzData, hRollbackFile); + ExitOnFailure(hr, "Failed to delete role assignments"); + if (S_FALSE == hr) ExitFunction(); + + // unregister assemblies + hr = CpiConfigureAssemblies(&pwzData, hRollbackFile); + ExitOnFailure(hr, "Failed to unregister assemblies"); + if (S_FALSE == hr) ExitFunction(); + + // remove users in application roles + hr = CpiConfigureUsersInApplicationRoles(&pwzData, hRollbackFile); + ExitOnFailure(hr, "Failed to delete users in application roles"); + if (S_FALSE == hr) ExitFunction(); + + // remove application roles + hr = CpiConfigureApplicationRoles(&pwzData, hRollbackFile); + ExitOnFailure(hr, "Failed to delete application roles"); + if (S_FALSE == hr) ExitFunction(); + + // remove applications + hr = CpiConfigureApplications(&pwzData, hRollbackFile); + ExitOnFailure(hr, "Failed to remove applications"); + if (S_FALSE == hr) ExitFunction(); + + // remove partition users + hr = CpiConfigurePartitionUsers(&pwzData, hRollbackFile); + ExitOnFailure(hr, "Failed to remove partition users"); + if (S_FALSE == hr) ExitFunction(); + + // remove users in partition roles + hr = CpiConfigureUsersInPartitionRoles(&pwzData, hRollbackFile); + ExitOnFailure(hr, "Failed to delete users in partition roles"); + if (S_FALSE == hr) ExitFunction(); + + // remove partitions + hr = CpiConfigurePartitions(&pwzData, hRollbackFile); + ExitOnFailure(hr, "Failed to delete partitions"); + if (S_FALSE == hr) ExitFunction(); + + hr = S_OK; + +LExit: + // clean up + ReleaseStr(pwzCustomActionData); + ReleaseStr(pwzRollbackFileName); + + if (INVALID_HANDLE_VALUE != hRollbackFile) + ::CloseHandle(hRollbackFile); + + // unitialize + CpiFinalize(); + + if (fInitializedCom) + ::CoUninitialize(); + + er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE; + return WcaFinalize(er); +} + +/******************************************************************** + ComPlusRollbackUninstallExecute - CUSTOM ACTION ENTRY POINT + + Input: deferred CustomActionData - ComPlusRollbackUninstallExecute +********************************************************************/ +extern "C" UINT __stdcall ComPlusRollbackUninstallExecute(MSIHANDLE hInstall) +{ + HRESULT hr = S_OK; + UINT er = ERROR_SUCCESS; + + LPWSTR pwzCustomActionData = NULL; + LPWSTR pwzData = NULL; + LPWSTR pwzRollbackFileName = NULL; + + HANDLE hRollbackFile = INVALID_HANDLE_VALUE; + + CPI_ROLLBACK_DATA* prdPartitions = NULL; + CPI_ROLLBACK_DATA* prdUsersInPartitionRoles = NULL; + CPI_ROLLBACK_DATA* prdPartitionUsers = NULL; + CPI_ROLLBACK_DATA* prdApplications = NULL; + CPI_ROLLBACK_DATA* prdApplicationRoles = NULL; + CPI_ROLLBACK_DATA* prdUsersApplicationRoles = NULL; + CPI_ROLLBACK_DATA* prdAssemblies = NULL; + CPI_ROLLBACK_DATA* prdRoleAssignments = NULL; + CPI_ROLLBACK_DATA* prdSubscriptions = NULL; + + BOOL fInitializedCom = FALSE; + + // initialize + hr = WcaInitialize(hInstall, "ComPlusRollbackUninstallExecute"); + ExitOnFailure(hr, "Failed to initialize ComPlusRollbackUninstallExecute"); + + hr = ::CoInitialize(NULL); + ExitOnFailure(hr, "Failed to initialize COM"); + fInitializedCom = TRUE; + + CpiInitialize(); + + // get custom action data + hr = WcaGetProperty(L"CustomActionData", &pwzCustomActionData); + ExitOnFailure(hr, "Failed to get CustomActionData"); + pwzData = pwzCustomActionData; + + // open rollback file + hr = WcaReadStringFromCaData(&pwzData, &pwzRollbackFileName); + ExitOnFailure(hr, "Failed to read rollback file name"); + + hRollbackFile = ::CreateFileW(pwzRollbackFileName, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_TEMPORARY, NULL); + if (INVALID_HANDLE_VALUE == hRollbackFile) + ExitOnFailure(hr = HRESULT_FROM_WIN32(::GetLastError()), "Failed to open rollback file, name: %S", pwzRollbackFileName); + + // read rollback data + hr = CpiReadRollbackDataList(hRollbackFile, &prdSubscriptions); + ExitOnFailure(hr, "Failed to read subscription rollback data"); + hr = CpiReadRollbackDataList(hRollbackFile, &prdRoleAssignments); + ExitOnFailure(hr, "Failed to read role assignments rollback data"); + hr = CpiReadRollbackDataList(hRollbackFile, &prdAssemblies); + ExitOnFailure(hr, "Failed to read assemblies rollback data"); + hr = CpiReadRollbackDataList(hRollbackFile, &prdUsersApplicationRoles); + ExitOnFailure(hr, "Failed to read users in application roles rollback data"); + hr = CpiReadRollbackDataList(hRollbackFile, &prdApplicationRoles); + ExitOnFailure(hr, "Failed to read application roles rollback data"); + hr = CpiReadRollbackDataList(hRollbackFile, &prdApplications); + ExitOnFailure(hr, "Failed to read applications rollback data"); + hr = CpiReadRollbackDataList(hRollbackFile, &prdPartitionUsers); + ExitOnFailure(hr, "Failed to read partition users rollback data"); + hr = CpiReadRollbackDataList(hRollbackFile, &prdUsersInPartitionRoles); + ExitOnFailure(hr, "Failed to read users in partition roles rollback data"); + hr = CpiReadRollbackDataList(hRollbackFile, &prdPartitions); + ExitOnFailure(hr, "Failed to read partitions rollback data"); + + ::CloseHandle(hRollbackFile); + hRollbackFile = INVALID_HANDLE_VALUE; + + // rollback remove partitions + hr = CpiRollbackConfigurePartitions(&pwzData, prdPartitions); + ExitOnFailure(hr, "Failed to rollback delete partitions"); + + // rollback remove users in partition roles + hr = CpiRollbackConfigureUsersInPartitionRoles(&pwzData, prdUsersInPartitionRoles); + ExitOnFailure(hr, "Failed to rollback delete users in partition roles"); + + // rollback remove partition users + hr = CpiRollbackConfigurePartitionUsers(&pwzData, prdPartitionUsers); + ExitOnFailure(hr, "Failed to rollback delete partition users"); + + // rollback remove applications + hr = CpiRollbackConfigureApplications(&pwzData, prdApplications); + ExitOnFailure(hr, "Failed to rollback delete applications"); + + // rollback remove application roles + hr = CpiRollbackConfigureApplicationRoles(&pwzData, prdApplicationRoles); + ExitOnFailure(hr, "Failed to rollback delete application roles"); + + // rollback remove users in application roles + hr = CpiRollbackConfigureUsersInApplicationRoles(&pwzData, prdUsersApplicationRoles); + ExitOnFailure(hr, "Failed to rollback delete users in application roles"); + + // rollback unregister assemblies + hr = CpiRollbackConfigureAssemblies(&pwzData, prdAssemblies); + ExitOnFailure(hr, "Failed to rollback unregister assemblies"); + + // rollback delete role assignments + hr = CpiRollbackConfigureRoleAssignments(&pwzData, prdAssemblies); + ExitOnFailure(hr, "Failed to rollback delete role assignments"); + + // rollback delete subscriptions + hr = CpiRollbackConfigureSubscriptions(&pwzData, prdSubscriptions); + ExitOnFailure(hr, "Failed to rollback delete subscriptions"); + + hr = S_OK; + +LExit: + // clean up + ReleaseStr(pwzCustomActionData); + ReleaseStr(pwzRollbackFileName); + + if (INVALID_HANDLE_VALUE != hRollbackFile) + ::CloseHandle(hRollbackFile); + + if (prdPartitions) + CpiFreeRollbackDataList(prdPartitions); + if (prdUsersInPartitionRoles) + CpiFreeRollbackDataList(prdUsersInPartitionRoles); + if (prdPartitionUsers) + CpiFreeRollbackDataList(prdPartitionUsers); + if (prdApplications) + CpiFreeRollbackDataList(prdApplications); + if (prdApplicationRoles) + CpiFreeRollbackDataList(prdApplicationRoles); + if (prdUsersApplicationRoles) + CpiFreeRollbackDataList(prdUsersApplicationRoles); + if (prdAssemblies) + CpiFreeRollbackDataList(prdAssemblies); + if (prdRoleAssignments) + CpiFreeRollbackDataList(prdRoleAssignments); + if (prdSubscriptions) + CpiFreeRollbackDataList(prdSubscriptions); + + // unitialize + CpiFinalize(); + + if (fInitializedCom) + ::CoUninitialize(); + + er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE; + return WcaFinalize(er); +} diff --git a/src/ca/cpexec.def b/src/ca/cpexec.def new file mode 100644 index 00000000..1dad15c2 --- /dev/null +++ b/src/ca/cpexec.def @@ -0,0 +1,11 @@ +; Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + + +EXPORTS + ComPlusPrepare + ComPlusCleanup + ComPlusInstallExecute + ComPlusInstallExecuteCommit + ComPlusRollbackInstallExecute + ComPlusUninstallExecute + ComPlusRollbackUninstallExecute diff --git a/src/ca/cppartexec.cpp b/src/ca/cppartexec.cpp new file mode 100644 index 00000000..d8c30c6a --- /dev/null +++ b/src/ca/cppartexec.cpp @@ -0,0 +1,690 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + + +// private structs + +struct CPI_PARTITION_ATTRIBUTES +{ + int iActionType; + int iActionCost; + LPWSTR pwzKey; + LPWSTR pwzID; + LPWSTR pwzName; + CPI_PROPERTY* pPropList; +}; + +struct CPI_PARTITION_USER_ATTRIBUTES +{ + int iActionType; + int iActionCost; + LPWSTR pwzKey; + LPWSTR pwzAccount; + LPWSTR pwzPartID; +}; + + +// prototypes for private helper functions + +static HRESULT ReadPartitionAttributes( + LPWSTR* ppwzData, + CPI_PARTITION_ATTRIBUTES* pAttrs + ); +static void FreePartitionAttributes( + CPI_PARTITION_ATTRIBUTES* pAttrs + ); +static HRESULT CreatePartition( + CPI_PARTITION_ATTRIBUTES* pAttrs + ); +static HRESULT RemovePartition( + CPI_PARTITION_ATTRIBUTES* pAttrs + ); +static HRESULT ReadPartitionUserAttributes( + LPWSTR* ppwzData, + CPI_PARTITION_USER_ATTRIBUTES* pAttrs + ); +static void FreePartitionUserAttributes( + CPI_PARTITION_USER_ATTRIBUTES* pAttrs + ); +static HRESULT CreatePartitionUser( + CPI_PARTITION_USER_ATTRIBUTES* pAttrs + ); +static HRESULT RemovePartitionUser( + CPI_PARTITION_USER_ATTRIBUTES* pAttrs + ); + + +// function definitions + +HRESULT CpiConfigurePartitions( + LPWSTR* ppwzData, + HANDLE hRollbackFile + ) +{ + HRESULT hr = S_OK; + + CPI_PARTITION_ATTRIBUTES attrs; + ::ZeroMemory(&attrs, sizeof(attrs)); + + // read action text + hr = CpiActionStartMessage(ppwzData, FALSE); + ExitOnFailure(hr, "Failed to send action start message"); + + // ger partition count + int iCnt = 0; + hr = WcaReadIntegerFromCaData(ppwzData, &iCnt); + ExitOnFailure(hr, "Failed to read count"); + + // write count to rollback file + hr = CpiWriteIntegerToRollbackFile(hRollbackFile, iCnt); + ExitOnFailure(hr, "Failed to write count to rollback file"); + + for (int i = 0; i < iCnt; i++) + { + // read partition attributes from CustomActionData + hr = ReadPartitionAttributes(ppwzData, &attrs); + ExitOnFailure(hr, "Failed to read attributes"); + + // progress message + hr = CpiActionDataMessage(1, attrs.pwzName); + ExitOnFailure(hr, "Failed to send progress messages"); + + if (S_FALSE == hr) + ExitFunction(); + + // write key to rollback file + hr = CpiWriteKeyToRollbackFile(hRollbackFile, attrs.pwzKey); + ExitOnFailure(hr, "Failed to write key to rollback file"); + + // action + switch (attrs.iActionType) + { + case atCreate: + hr = CreatePartition(&attrs); + ExitOnFailure(hr, "Failed to create partition, key: %S", attrs.pwzKey); + break; + case atRemove: + hr = RemovePartition(&attrs); + ExitOnFailure(hr, "Failed to remove partition, key: %S", attrs.pwzKey); + break; + } + + // write completion status to rollback file + hr = CpiWriteIntegerToRollbackFile(hRollbackFile, 1); + ExitOnFailure(hr, "Failed to write completion status to rollback file"); + + // progress + hr = WcaProgressMessage(attrs.iActionCost, FALSE); + ExitOnFailure(hr, "Failed to update progress"); + } + + hr = S_OK; + +LExit: + // clean up + FreePartitionAttributes(&attrs); + + return hr; +} + +HRESULT CpiRollbackConfigurePartitions( + LPWSTR* ppwzData, + CPI_ROLLBACK_DATA* pRollbackDataList + ) +{ + HRESULT hr = S_OK; + + int iRollbackStatus; + + CPI_PARTITION_ATTRIBUTES attrs; + ::ZeroMemory(&attrs, sizeof(attrs)); + + // read action text + hr = CpiActionStartMessage(ppwzData, NULL == pRollbackDataList); + ExitOnFailure(hr, "Failed to send action start message"); + + // get count + int iCnt = 0; + hr = WcaReadIntegerFromCaData(ppwzData, &iCnt); + ExitOnFailure(hr, "Failed to read count"); + + for (int i = 0; i < iCnt; i++) + { + // read partition attributes from CustomActionData + hr = ReadPartitionAttributes(ppwzData, &attrs); + ExitOnFailure(hr, "Failed to read attributes"); + + // rollback status + hr = CpiFindRollbackStatus(pRollbackDataList, attrs.pwzKey, &iRollbackStatus); + + if (S_FALSE == hr) + continue; // not found, nothing to rollback + + // progress message + hr = CpiActionDataMessage(1, attrs.pwzName); + ExitOnFailure(hr, "Failed to send progress messages"); + + if (S_FALSE == hr) + ExitFunction(); + + // action + switch (attrs.iActionType) + { + case atCreate: + hr = CreatePartition(&attrs); + if (FAILED(hr)) + WcaLog(LOGMSG_STANDARD, "Failed to create partition, hr: 0x%x, key: %S", hr, attrs.pwzKey); + break; + case atRemove: + hr = RemovePartition(&attrs); + if (FAILED(hr)) + WcaLog(LOGMSG_STANDARD, "Failed to remove partition, hr: 0x%x, key: %S", hr, attrs.pwzKey); + break; + } + + // check rollback status + if (0 == iRollbackStatus) + continue; // operation did not complete, skip progress + + // progress + hr = WcaProgressMessage(attrs.iActionCost, FALSE); + ExitOnFailure(hr, "Failed to update progress"); + } + + hr = S_OK; + +LExit: + // clean up + FreePartitionAttributes(&attrs); + + return hr; +} + +HRESULT CpiConfigurePartitionUsers( + LPWSTR* ppwzData, + HANDLE hRollbackFile + ) +{ + HRESULT hr = S_OK; + + CPI_PARTITION_USER_ATTRIBUTES attrs; + ::ZeroMemory(&attrs, sizeof(attrs)); + + // read action text + hr = CpiActionStartMessage(ppwzData, FALSE); + ExitOnFailure(hr, "Failed to send action start message"); + + // ger partition count + int iCnt = 0; + hr = WcaReadIntegerFromCaData(ppwzData, &iCnt); + ExitOnFailure(hr, "Failed to read count"); + + // write count to rollback file + hr = CpiWriteIntegerToRollbackFile(hRollbackFile, iCnt); + ExitOnFailure(hr, "Failed to write count to rollback file"); + + for (int i = 0; i < iCnt; i++) + { + // read partition attributes from CustomActionData + hr = ReadPartitionUserAttributes(ppwzData, &attrs); + ExitOnFailure(hr, "Failed to read attributes"); + + // progress message + hr = CpiActionDataMessage(1, attrs.pwzAccount); + ExitOnFailure(hr, "Failed to send progress messages"); + + if (S_FALSE == hr) + ExitFunction(); + + // write key to rollback file + hr = CpiWriteKeyToRollbackFile(hRollbackFile, attrs.pwzKey); + ExitOnFailure(hr, "Failed to write key to rollback file"); + + // action + switch (attrs.iActionType) + { + case atCreate: + hr = CreatePartitionUser(&attrs); + ExitOnFailure(hr, "Failed to create partition user, key: %S", attrs.pwzKey); + break; + case atRemove: + hr = RemovePartitionUser(&attrs); + ExitOnFailure(hr, "Failed to remove partition user, key: %S", attrs.pwzKey); + break; + } + + // write completion status to rollback file + hr = CpiWriteIntegerToRollbackFile(hRollbackFile, 1); + ExitOnFailure(hr, "Failed to write completion status to rollback file"); + + // progress + hr = WcaProgressMessage(attrs.iActionCost, FALSE); + ExitOnFailure(hr, "Failed to update progress"); + } + + hr = S_OK; + +LExit: + // clean up + FreePartitionUserAttributes(&attrs); + + return hr; +} + +HRESULT CpiRollbackConfigurePartitionUsers( + LPWSTR* ppwzData, + CPI_ROLLBACK_DATA* pRollbackDataList + ) +{ + HRESULT hr = S_OK; + + int iRollbackStatus; + + CPI_PARTITION_USER_ATTRIBUTES attrs; + ::ZeroMemory(&attrs, sizeof(attrs)); + + // read action text + hr = CpiActionStartMessage(ppwzData, NULL == pRollbackDataList); + ExitOnFailure(hr, "Failed to send action start message"); + + // get count + int iCnt = 0; + hr = WcaReadIntegerFromCaData(ppwzData, &iCnt); + ExitOnFailure(hr, "Failed to read count"); + + for (int i = 0; i < iCnt; i++) + { + // read partition attributes from CustomActionData + hr = ReadPartitionUserAttributes(ppwzData, &attrs); + ExitOnFailure(hr, "Failed to read attributes"); + + // rollback status + hr = CpiFindRollbackStatus(pRollbackDataList, attrs.pwzKey, &iRollbackStatus); + + if (S_FALSE == hr) + continue; // not found, nothing to rollback + + // progress message + hr = CpiActionDataMessage(1, attrs.pwzAccount); + ExitOnFailure(hr, "Failed to send progress messages"); + + if (S_FALSE == hr) + ExitFunction(); + + // action + switch (attrs.iActionType) + { + case atCreate: + hr = CreatePartitionUser(&attrs); + ExitOnFailure(hr, "Failed to create partition user, key: %S", attrs.pwzKey); + break; + case atRemove: + hr = RemovePartitionUser(&attrs); + ExitOnFailure(hr, "Failed to remove partition user, key: %S", attrs.pwzKey); + break; + } + + // check rollback status + if (0 == iRollbackStatus) + continue; // operation did not complete, skip progress + + // progress + hr = WcaProgressMessage(attrs.iActionCost, FALSE); + ExitOnFailure(hr, "Failed to update progress"); + } + + hr = S_OK; + +LExit: + // clean up + FreePartitionUserAttributes(&attrs); + + return hr; +} + + +// helper function definitions + +static HRESULT ReadPartitionAttributes( + LPWSTR* ppwzData, + CPI_PARTITION_ATTRIBUTES* pAttrs + ) +{ + HRESULT hr = S_OK; + + hr = WcaReadIntegerFromCaData(ppwzData, &pAttrs->iActionType); + ExitOnFailure(hr, "Failed to read action type"); + hr = WcaReadIntegerFromCaData(ppwzData, &pAttrs->iActionCost); + ExitOnFailure(hr, "Failed to read action cost"); + hr = WcaReadStringFromCaData(ppwzData, &pAttrs->pwzKey); + ExitOnFailure(hr, "Failed to read key"); + hr = WcaReadStringFromCaData(ppwzData, &pAttrs->pwzID); + ExitOnFailure(hr, "Failed to read id"); + hr = WcaReadStringFromCaData(ppwzData, &pAttrs->pwzName); + ExitOnFailure(hr, "Failed to read name"); + hr = CpiReadPropertyList(ppwzData, &pAttrs->pPropList); + ExitOnFailure(hr, "Failed to read properties"); + + hr = S_OK; + +LExit: + return hr; +} + +static void FreePartitionAttributes( + CPI_PARTITION_ATTRIBUTES* pAttrs + ) +{ + ReleaseStr(pAttrs->pwzKey); + ReleaseStr(pAttrs->pwzID); + ReleaseStr(pAttrs->pwzName); + + if (pAttrs->pPropList) + CpiFreePropertyList(pAttrs->pPropList); +} + +static HRESULT CreatePartition( + CPI_PARTITION_ATTRIBUTES* pAttrs + ) +{ + HRESULT hr = S_OK; + + ICatalogCollection* piPartColl = NULL; + ICatalogObject* piPartObj = NULL; + + long lChanges = 0; + + // log + WcaLog(LOGMSG_VERBOSE, "Creating partition, key: %S", pAttrs->pwzKey); + + // get partitions collection + hr = CpiGetPartitionsCollection(&piPartColl); + ExitOnFailure(hr, "Failed to get partitions collection"); + + // check if partition exists + hr = CpiFindCollectionObjectByStringKey(piPartColl, pAttrs->pwzID, &piPartObj); + ExitOnFailure(hr, "Failed to find partition"); + + if (S_FALSE == hr) + { + // create partition + hr = CpiAddCollectionObject(piPartColl, &piPartObj); + ExitOnFailure(hr, "Failed to add partition to collection"); + + hr = CpiPutCollectionObjectValue(piPartObj, L"ID", pAttrs->pwzID); + ExitOnFailure(hr, "Failed to set partition id property"); + + hr = CpiPutCollectionObjectValue(piPartObj, L"Name", pAttrs->pwzName); + ExitOnFailure(hr, "Failed to set partition name property"); + } + + // properties + hr = CpiPutCollectionObjectValues(piPartObj, pAttrs->pPropList); + ExitOnFailure(hr, "Failed to write properties"); + + // save changes + hr = piPartColl->SaveChanges(&lChanges); + if (COMADMIN_E_OBJECTERRORS == hr) + CpiLogCatalogErrorInfo(); + ExitOnFailure(hr, "Failed to save changes"); + + // log + WcaLog(LOGMSG_VERBOSE, "%d changes saved to catalog, key: %S", lChanges, pAttrs->pwzKey); + + hr = S_OK; + +LExit: + // clean up + ReleaseObject(piPartColl); + ReleaseObject(piPartObj); + + return hr; +} + +static HRESULT RemovePartition( + CPI_PARTITION_ATTRIBUTES* pAttrs + ) +{ + HRESULT hr = S_OK; + + ICatalogCollection* piPartColl = NULL; + + long lChanges = 0; + + // log + WcaLog(LOGMSG_VERBOSE, "Removing partition, key: %S", pAttrs->pwzKey); + + // get partitions collection + hr = CpiGetPartitionsCollection(&piPartColl); + ExitOnFailure(hr, "Failed to get partitions collection"); + + // remove + hr = CpiRemoveCollectionObject(piPartColl, pAttrs->pwzID, NULL, TRUE); + ExitOnFailure(hr, "Failed to remove partition"); + + if (S_FALSE == hr) + { + // partition not found + WcaLog(LOGMSG_VERBOSE, "Partition not found, nothing to delete, key: %S", pAttrs->pwzKey); + ExitFunction1(hr = S_OK); + } + + // save changes + hr = piPartColl->SaveChanges(&lChanges); + if (COMADMIN_E_OBJECTERRORS == hr) + CpiLogCatalogErrorInfo(); + ExitOnFailure(hr, "Failed to save changes"); + + // log + WcaLog(LOGMSG_VERBOSE, "%d changes saved to catalog, key: %S", lChanges, pAttrs->pwzKey); + + hr = S_OK; + +LExit: + // clean up + ReleaseObject(piPartColl); + + return hr; +} + +static HRESULT ReadPartitionUserAttributes( + LPWSTR* ppwzData, + CPI_PARTITION_USER_ATTRIBUTES* pAttrs + ) +{ + HRESULT hr = S_OK; + + hr = WcaReadIntegerFromCaData(ppwzData, &pAttrs->iActionType); + ExitOnFailure(hr, "Failed to read action type"); + hr = WcaReadIntegerFromCaData(ppwzData, &pAttrs->iActionCost); + ExitOnFailure(hr, "Failed to read action cost"); + hr = WcaReadStringFromCaData(ppwzData, &pAttrs->pwzKey); + ExitOnFailure(hr, "Failed to read key"); + hr = WcaReadStringFromCaData(ppwzData, &pAttrs->pwzAccount); + ExitOnFailure(hr, "Failed to read account name"); + hr = WcaReadStringFromCaData(ppwzData, &pAttrs->pwzPartID); + ExitOnFailure(hr, "Failed to read partition id"); + + hr = S_OK; + +LExit: + return hr; +} + +static void FreePartitionUserAttributes( + CPI_PARTITION_USER_ATTRIBUTES* pAttrs + ) +{ + ReleaseStr(pAttrs->pwzKey); + ReleaseStr(pAttrs->pwzAccount); + ReleaseStr(pAttrs->pwzPartID); +} + +static HRESULT CreatePartitionUser( + CPI_PARTITION_USER_ATTRIBUTES* pAttrs + ) +{ + HRESULT hr = S_OK; + UINT er = ERROR_SUCCESS; + + ICatalogCollection* piUserColl = NULL; + ICatalogObject* piUserObj = NULL; + + PSID pSid = NULL; + long lChanges = 0; + + // log + WcaLog(LOGMSG_VERBOSE, "Setting default partition for user, key: %S", pAttrs->pwzKey); + + // get partition users collection + hr = CpiGetPartitionUsersCollection(&piUserColl); + ExitOnFailure(hr, "Failed to get partition users collection"); + + // get SID for account + do { + er = ERROR_SUCCESS; + hr = CpiAccountNameToSid(pAttrs->pwzAccount, &pSid); + if (HRESULT_FROM_WIN32(ERROR_NONE_MAPPED) == hr && !::MsiGetMode(WcaGetInstallHandle(), MSIRUNMODE_ROLLBACK)) + { + WcaLog(LOGMSG_STANDARD, "Failed to lookup account name, hr: 0x%x, account: '%S'", hr, pAttrs->pwzAccount); + er = WcaErrorMessage(msierrComPlusFailedLookupNames, hr, INSTALLMESSAGE_ERROR | MB_ABORTRETRYIGNORE, 0); + switch (er) + { + case IDABORT: + ExitFunction(); // exit with error code from CpiAccountNameToSid() + case IDRETRY: + break; + case IDIGNORE: + default: + ExitFunction1(hr = S_OK); + } + } + else + ExitOnFailure(hr, "Failed to get SID for account"); + } while (IDRETRY == er); + + // remove any existing entry + hr = CpiRemoveUserCollectionObject(piUserColl, pSid); + if (HRESULT_FROM_WIN32(ERROR_NONE_MAPPED) == hr || HRESULT_FROM_WIN32(ERROR_SOME_NOT_MAPPED) == hr) + { + WcaLog(LOGMSG_STANDARD, "Failed to lookup account names, hr: 0x%x", hr); + hr = S_FALSE; + } + else + ExitOnFailure(hr, "Failed to remove user"); + + if (S_OK == hr) + WcaLog(LOGMSG_VERBOSE, "Existing default partition for user was removed, key: %S", pAttrs->pwzKey); + + // add partition user + hr = CpiAddCollectionObject(piUserColl, &piUserObj); + ExitOnFailure(hr, "Failed to add partition to collection"); + + hr = CpiPutCollectionObjectValue(piUserObj, L"AccountName", pAttrs->pwzAccount); + ExitOnFailure(hr, "Failed to set account name property"); + + hr = CpiPutCollectionObjectValue(piUserObj, L"DefaultPartitionID", pAttrs->pwzPartID); + ExitOnFailure(hr, "Failed to set default partition id property"); + + // save changes + hr = piUserColl->SaveChanges(&lChanges); + if (COMADMIN_E_OBJECTERRORS == hr) + CpiLogCatalogErrorInfo(); + ExitOnFailure(hr, "Failed to save changes"); + + // log + WcaLog(LOGMSG_VERBOSE, "%d changes saved to catalog, key: %S", lChanges, pAttrs->pwzKey); + + hr = S_OK; + +LExit: + // clean up + ReleaseObject(piUserColl); + ReleaseObject(piUserObj); + + if (pSid) + ::HeapFree(::GetProcessHeap(), 0, pSid); + + return hr; +} + +static HRESULT RemovePartitionUser( + CPI_PARTITION_USER_ATTRIBUTES* pAttrs + ) +{ + HRESULT hr = S_OK; + UINT er = ERROR_SUCCESS; + + ICatalogCollection* piUserColl = NULL; + + PSID pSid = NULL; + long lChanges = 0; + + // log + WcaLog(LOGMSG_VERBOSE, "Removing default partition for user, key: %S", pAttrs->pwzKey); + + // get partition users collection + hr = CpiGetPartitionUsersCollection(&piUserColl); + ExitOnFailure(hr, "Failed to get partition users collection"); + + // get SID for account + do { + er = ERROR_SUCCESS; + hr = CpiAccountNameToSid(pAttrs->pwzAccount, &pSid); + if (HRESULT_FROM_WIN32(ERROR_NONE_MAPPED) == hr && !::MsiGetMode(WcaGetInstallHandle(), MSIRUNMODE_ROLLBACK)) + { + WcaLog(LOGMSG_STANDARD, "Failed to lookup account name, hr: 0x%x, account: '%S'", hr, pAttrs->pwzAccount); + er = WcaErrorMessage(msierrComPlusFailedLookupNames, hr, INSTALLMESSAGE_ERROR | MB_ABORTRETRYIGNORE, 0); + switch (er) + { + case IDABORT: + ExitFunction(); // exit with error code from CpiAccountNameToSid() + case IDRETRY: + break; + case IDIGNORE: + default: + ExitFunction1(hr = S_OK); + } + } + else + ExitOnFailure(hr, "Failed to get SID for account"); + } while (IDRETRY == er); + + // remove + hr = CpiRemoveUserCollectionObject(piUserColl, pSid); + if (HRESULT_FROM_WIN32(ERROR_NONE_MAPPED) == hr || HRESULT_FROM_WIN32(ERROR_SOME_NOT_MAPPED) == hr) + { + WcaLog(LOGMSG_STANDARD, "Failed to lookup account names, hr: 0x%x", hr); + hr = S_FALSE; + } + else + ExitOnFailure(hr, "Failed to remove user"); + + if (S_FALSE == hr) + { + // user not found + WcaLog(LOGMSG_VERBOSE, "Default partition for user not found, nothing to delete, key: %S", pAttrs->pwzKey); + ExitFunction1(hr = S_OK); + } + + // save changes + hr = piUserColl->SaveChanges(&lChanges); + if (COMADMIN_E_OBJECTERRORS == hr) + CpiLogCatalogErrorInfo(); + ExitOnFailure(hr, "Failed to save changes"); + + // log + WcaLog(LOGMSG_VERBOSE, "%d changes saved to catalog, key: %S", lChanges, pAttrs->pwzKey); + + hr = S_OK; + +LExit: + // clean up + ReleaseObject(piUserColl); + + if (pSid) + ::HeapFree(::GetProcessHeap(), 0, pSid); + + return hr; +} diff --git a/src/ca/cppartexec.h b/src/ca/cppartexec.h new file mode 100644 index 00000000..132a9f5a --- /dev/null +++ b/src/ca/cppartexec.h @@ -0,0 +1,20 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + + +HRESULT CpiConfigurePartitions( + LPWSTR* ppwzData, + HANDLE hRollbackFile + ); +HRESULT CpiRollbackConfigurePartitions( + LPWSTR* ppwzData, + CPI_ROLLBACK_DATA* pRollbackDataList + ); +HRESULT CpiConfigurePartitionUsers( + LPWSTR* ppwzData, + HANDLE hRollbackFile + ); +HRESULT CpiRollbackConfigurePartitionUsers( + LPWSTR* ppwzData, + CPI_ROLLBACK_DATA* pRollbackDataList + ); diff --git a/src/ca/cppartroleexec.cpp b/src/ca/cppartroleexec.cpp new file mode 100644 index 00000000..4a503c79 --- /dev/null +++ b/src/ca/cppartroleexec.cpp @@ -0,0 +1,397 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + + +// private structs + +struct CPI_USER_IN_PARTITION_ROLE_ATTRIBUTES +{ + int iActionType; + int iActionCost; + LPWSTR pwzKey; + LPWSTR pwzRoleName; + LPWSTR pwzAccount; + LPWSTR pwzPartID; +}; + + +// prototypes for private helper functions + +static HRESULT ReadUserInPartitionRoleAttributes( + LPWSTR* ppwzData, + CPI_USER_IN_PARTITION_ROLE_ATTRIBUTES* pAttrs + ); +static void FreeUserInPartitionRoleAttributes( + CPI_USER_IN_PARTITION_ROLE_ATTRIBUTES* pAttrs + ); +static HRESULT CreateUserInPartitionRole( + CPI_USER_IN_PARTITION_ROLE_ATTRIBUTES* pAttrs + ); +static HRESULT RemoveUserInPartitionRole( + CPI_USER_IN_PARTITION_ROLE_ATTRIBUTES* pAttrs + ); + + +// function definitions + +HRESULT CpiConfigureUsersInPartitionRoles( + LPWSTR* ppwzData, + HANDLE hRollbackFile + ) +{ + HRESULT hr = S_OK; + + CPI_USER_IN_PARTITION_ROLE_ATTRIBUTES attrs; + ::ZeroMemory(&attrs, sizeof(attrs)); + + // read action text + hr = CpiActionStartMessage(ppwzData, FALSE); + ExitOnFailure(hr, "Failed to send action start message"); + + // ger count + int iCnt = 0; + hr = WcaReadIntegerFromCaData(ppwzData, &iCnt); + ExitOnFailure(hr, "Failed to read count"); + + // write count to rollback file + hr = CpiWriteIntegerToRollbackFile(hRollbackFile, iCnt); + ExitOnFailure(hr, "Failed to write count to rollback file"); + + for (int i = 0; i < iCnt; i++) + { + // read attributes from CustomActionData + hr = ReadUserInPartitionRoleAttributes(ppwzData, &attrs); + ExitOnFailure(hr, "Failed to read attributes"); + + // progress message + hr = CpiActionDataMessage(1, attrs.pwzRoleName); + ExitOnFailure(hr, "Failed to send progress messages"); + + if (S_FALSE == hr) + ExitFunction(); + + // write key to rollback file + hr = CpiWriteKeyToRollbackFile(hRollbackFile, attrs.pwzKey); + ExitOnFailure(hr, "Failed to write key to rollback file"); + + // action + switch (attrs.iActionType) + { + case atCreate: + hr = CreateUserInPartitionRole(&attrs); + ExitOnFailure(hr, "Failed to add user to partition role, key: %S", attrs.pwzKey); + break; + case atRemove: + hr = RemoveUserInPartitionRole(&attrs); + ExitOnFailure(hr, "Failed to remove user from partition role, key: %S", attrs.pwzKey); + break; + } + + // write completion status to rollback file + hr = CpiWriteIntegerToRollbackFile(hRollbackFile, 1); + ExitOnFailure(hr, "Failed to write completion status to rollback file"); + + // progress + hr = WcaProgressMessage(attrs.iActionCost, FALSE); + ExitOnFailure(hr, "Failed to update progress"); + } + + hr = S_OK; + +LExit: + // clean up + FreeUserInPartitionRoleAttributes(&attrs); + + return hr; +} + +HRESULT CpiRollbackConfigureUsersInPartitionRoles( + LPWSTR* ppwzData, + CPI_ROLLBACK_DATA* pRollbackDataList + ) +{ + HRESULT hr = S_OK; + + int iRollbackStatus; + + CPI_USER_IN_PARTITION_ROLE_ATTRIBUTES attrs; + ::ZeroMemory(&attrs, sizeof(attrs)); + + // read action text + hr = CpiActionStartMessage(ppwzData, NULL == pRollbackDataList); + ExitOnFailure(hr, "Failed to send action start message"); + + // get count + int iCnt = 0; + hr = WcaReadIntegerFromCaData(ppwzData, &iCnt); + ExitOnFailure(hr, "Failed to read count"); + + for (int i = 0; i < iCnt; i++) + { + // read attributes from CustomActionData + hr = ReadUserInPartitionRoleAttributes(ppwzData, &attrs); + ExitOnFailure(hr, "Failed to read attributes"); + + // rollback status + hr = CpiFindRollbackStatus(pRollbackDataList, attrs.pwzKey, &iRollbackStatus); + + if (S_FALSE == hr) + continue; // not found, nothing to rollback + + // progress message + hr = CpiActionDataMessage(1, attrs.pwzRoleName); + ExitOnFailure(hr, "Failed to send progress messages"); + + if (S_FALSE == hr) + ExitFunction(); + + // action + switch (attrs.iActionType) + { + case atCreate: + hr = CreateUserInPartitionRole(&attrs); + if (FAILED(hr)) + WcaLog(LOGMSG_STANDARD, "Failed to add user to partition role, hr: 0x%x, key: %S", hr, attrs.pwzKey); + break; + case atRemove: + hr = RemoveUserInPartitionRole(&attrs); + if (FAILED(hr)) + WcaLog(LOGMSG_STANDARD, "Failed to remove user from partition role, hr: 0x%x, key: %S", hr, attrs.pwzKey); + break; + } + + // check rollback status + if (0 == iRollbackStatus) + continue; // operation did not complete, skip progress + + // progress + hr = WcaProgressMessage(attrs.iActionCost, FALSE); + ExitOnFailure(hr, "Failed to update progress"); + } + + hr = S_OK; + +LExit: + // clean up + FreeUserInPartitionRoleAttributes(&attrs); + + return hr; +} + + +// helper function definitions + +static HRESULT ReadUserInPartitionRoleAttributes( + LPWSTR* ppwzData, + CPI_USER_IN_PARTITION_ROLE_ATTRIBUTES* pAttrs + ) +{ + HRESULT hr = S_OK; + + hr = WcaReadIntegerFromCaData(ppwzData, &pAttrs->iActionType); + ExitOnFailure(hr, "Failed to read action type"); + hr = WcaReadIntegerFromCaData(ppwzData, &pAttrs->iActionCost); + ExitOnFailure(hr, "Failed to read action cost"); + hr = WcaReadStringFromCaData(ppwzData, &pAttrs->pwzKey); + ExitOnFailure(hr, "Failed to read key"); + hr = WcaReadStringFromCaData(ppwzData, &pAttrs->pwzRoleName); + ExitOnFailure(hr, "Failed to read role name"); + hr = WcaReadStringFromCaData(ppwzData, &pAttrs->pwzAccount); + ExitOnFailure(hr, "Failed to read account name"); + hr = WcaReadStringFromCaData(ppwzData, &pAttrs->pwzPartID); + ExitOnFailure(hr, "Failed to read partition id"); + + hr = S_OK; + +LExit: + return hr; +} + +static void FreeUserInPartitionRoleAttributes( + CPI_USER_IN_PARTITION_ROLE_ATTRIBUTES* pAttrs + ) +{ + ReleaseStr(pAttrs->pwzKey); + ReleaseStr(pAttrs->pwzRoleName); + ReleaseStr(pAttrs->pwzAccount); + ReleaseStr(pAttrs->pwzPartID); +} + +static HRESULT CreateUserInPartitionRole( + CPI_USER_IN_PARTITION_ROLE_ATTRIBUTES* pAttrs + ) +{ + HRESULT hr = S_OK; + UINT er = ERROR_SUCCESS; + + ICatalogCollection* piUsrInRoleColl = NULL; + ICatalogObject* piUsrInRoleObj = NULL; + + PSID pSid = NULL; + long lChanges = 0; + + // log + WcaLog(LOGMSG_VERBOSE, "Adding user to partition role, key: %S", pAttrs->pwzKey); + + // get users in partition role collection + hr = CpiGetUsersInPartitionRoleCollection(pAttrs->pwzPartID, pAttrs->pwzRoleName, &piUsrInRoleColl); + if (S_FALSE == hr) + hr = HRESULT_FROM_WIN32(ERROR_NOT_FOUND); + ExitOnFailure(hr, "Failed to get users in partition role collection"); + + // get SID for account + do { + er = ERROR_SUCCESS; + hr = CpiAccountNameToSid(pAttrs->pwzAccount, &pSid); + if (HRESULT_FROM_WIN32(ERROR_NONE_MAPPED) == hr && !::MsiGetMode(WcaGetInstallHandle(), MSIRUNMODE_ROLLBACK)) + { + WcaLog(LOGMSG_STANDARD, "Failed to lookup account name, hr: 0x%x, account: '%S'", hr, pAttrs->pwzAccount); + er = WcaErrorMessage(msierrComPlusFailedLookupNames, hr, INSTALLMESSAGE_ERROR | MB_ABORTRETRYIGNORE, 0); + switch (er) + { + case IDABORT: + ExitFunction(); // exit with error code from CpiAccountNameToSid() + case IDRETRY: + break; + case IDIGNORE: + default: + ExitFunction1(hr = S_OK); + } + } + else + ExitOnFailure(hr, "Failed to get SID for account"); + } while (IDRETRY == er); + + // find any existing entry + hr = CpiFindUserCollectionObject(piUsrInRoleColl, pSid, NULL); + if (HRESULT_FROM_WIN32(ERROR_NONE_MAPPED) == hr || HRESULT_FROM_WIN32(ERROR_SOME_NOT_MAPPED) == hr) + WcaLog(LOGMSG_STANDARD, "Failed to lookup account names, hr: 0x%x", hr); + else + ExitOnFailure(hr, "Failed to find user in partition role"); + + if (S_OK == hr) + { + WcaLog(LOGMSG_VERBOSE, "User already assigned to partition role, key: %S", pAttrs->pwzKey); + ExitFunction(); // exit with hr = S_OK + } + + // convert SID back to account name + hr = CpiSidToAccountName(pSid, &pAttrs->pwzAccount); + ExitOnFailure(hr, "Failed to convert SID to account name"); + + // add user + hr = CpiAddCollectionObject(piUsrInRoleColl, &piUsrInRoleObj); + ExitOnFailure(hr, "Failed to add user in role to collection"); + + hr = CpiPutCollectionObjectValue(piUsrInRoleObj, L"User", pAttrs->pwzAccount); + ExitOnFailure(hr, "Failed to set role name property"); + + // save changes + hr = piUsrInRoleColl->SaveChanges(&lChanges); + if (COMADMIN_E_OBJECTERRORS == hr) + CpiLogCatalogErrorInfo(); + ExitOnFailure(hr, "Failed to save changes"); + + // log + WcaLog(LOGMSG_VERBOSE, "%d changes saved to catalog, key: %S", lChanges, pAttrs->pwzKey); + + hr = S_OK; + +LExit: + // clean up + ReleaseObject(piUsrInRoleColl); + ReleaseObject(piUsrInRoleObj); + + if (pSid) + ::HeapFree(::GetProcessHeap(), 0, pSid); + + return hr; +} + +static HRESULT RemoveUserInPartitionRole( + CPI_USER_IN_PARTITION_ROLE_ATTRIBUTES* pAttrs + ) +{ + HRESULT hr = S_OK; + UINT er = ERROR_SUCCESS; + + ICatalogCollection* piUsrInRoleColl = NULL; + + PSID pSid = NULL; + long lChanges = 0; + + // log + WcaLog(LOGMSG_VERBOSE, "Removing user from partition role, key: %S", pAttrs->pwzKey); + + // get users in partition role collection + hr = CpiGetUsersInPartitionRoleCollection(pAttrs->pwzPartID, pAttrs->pwzRoleName, &piUsrInRoleColl); + ExitOnFailure(hr, "Failed to get users in partition role collection"); + + if (S_FALSE == hr) + { + // users in role collection not found + WcaLog(LOGMSG_VERBOSE, "Unable to retrieve users in partition role collection, nothing to delete, key: %S", pAttrs->pwzKey); + ExitFunction1(hr = S_OK); + } + + // get SID for account + do { + er = ERROR_SUCCESS; + hr = CpiAccountNameToSid(pAttrs->pwzAccount, &pSid); + if (HRESULT_FROM_WIN32(ERROR_NONE_MAPPED) == hr && !::MsiGetMode(WcaGetInstallHandle(), MSIRUNMODE_ROLLBACK)) + { + WcaLog(LOGMSG_STANDARD, "Failed to lookup account name, hr: 0x%x, account: '%S'", hr, pAttrs->pwzAccount); + er = WcaErrorMessage(msierrComPlusFailedLookupNames, hr, INSTALLMESSAGE_ERROR | MB_ABORTRETRYIGNORE, 0); + switch (er) + { + case IDABORT: + ExitFunction(); // exit with error code from CpiAccountNameToSid() + case IDRETRY: + break; + case IDIGNORE: + default: + ExitFunction1(hr = S_OK); + } + } + else + ExitOnFailure(hr, "Failed to get SID for account"); + } while (IDRETRY == er); + + // remove + hr = CpiRemoveUserCollectionObject(piUsrInRoleColl, pSid); + if (HRESULT_FROM_WIN32(ERROR_NONE_MAPPED) == hr || HRESULT_FROM_WIN32(ERROR_SOME_NOT_MAPPED) == hr) + { + WcaLog(LOGMSG_STANDARD, "Failed to lookup account names, hr: 0x%x", hr); + hr = S_FALSE; + } + else + ExitOnFailure(hr, "Failed to remove user"); + + if (S_FALSE == hr) + { + // role not found + WcaLog(LOGMSG_VERBOSE, "User not found for partition role, nothing to delete, key: %S", pAttrs->pwzKey); + ExitFunction1(hr = S_OK); + } + + // save changes + hr = piUsrInRoleColl->SaveChanges(&lChanges); + if (COMADMIN_E_OBJECTERRORS == hr) + CpiLogCatalogErrorInfo(); + ExitOnFailure(hr, "Failed to save changes"); + + // log + WcaLog(LOGMSG_VERBOSE, "%d changes saved to catalog, key: %S", lChanges, pAttrs->pwzKey); + + hr = S_OK; + +LExit: + // clean up + ReleaseObject(piUsrInRoleColl); + + if (pSid) + ::HeapFree(::GetProcessHeap(), 0, pSid); + + return hr; +} diff --git a/src/ca/cppartroleexec.h b/src/ca/cppartroleexec.h new file mode 100644 index 00000000..0ec47dad --- /dev/null +++ b/src/ca/cppartroleexec.h @@ -0,0 +1,12 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + + +HRESULT CpiConfigureUsersInPartitionRoles( + LPWSTR* ppwzData, + HANDLE hRollbackFile + ); +HRESULT CpiRollbackConfigureUsersInPartitionRoles( + LPWSTR* ppwzData, + CPI_ROLLBACK_DATA* pRollbackDataList + ); diff --git a/src/ca/cppartrolesched.cpp b/src/ca/cppartrolesched.cpp new file mode 100644 index 00000000..a988f8e3 --- /dev/null +++ b/src/ca/cppartrolesched.cpp @@ -0,0 +1,421 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + + +// sql queries + +LPCWSTR vcsPartitionRoleQuery = + L"SELECT `PartitionRole`, `Partition_`, `Component_`, `Name` FROM `ComPlusPartitionRole`"; +enum ePartitionRoleQuery { prqPartitionRole = 1, prqPartition, prqComponent, prqName }; + +LPCWSTR vcsUserInPartitionRoleQuery = + L"SELECT `UserInPartitionRole`, `PartitionRole_`, `ComPlusUserInPartitionRole`.`Component_`, `Domain`, `Name` FROM `ComPlusUserInPartitionRole`, `User` WHERE `User_` = `User`"; +LPCWSTR vcsGroupInPartitionRoleQuery = + L"SELECT `GroupInPartitionRole`, `PartitionRole_`, `ComPlusGroupInPartitionRole`.`Component_`, `Domain`, `Name` FROM `ComPlusGroupInPartitionRole`, `Group` WHERE `Group_` = `Group`"; +enum eTrusteeInPartitionRoleQuery { tiprqUserInPartitionRole = 1, tiprqPartitionRole, tiprqComponent, tiprqDomain, tiprqName }; + + +// prototypes for private helper functions + +static HRESULT TrusteesInPartitionRolesRead( + LPCWSTR pwzQuery, + CPI_PARTITION_ROLE_LIST* pPartRoleList, + CPI_USER_IN_PARTITION_ROLE_LIST* pUsrInPartRoleList + ); +static void FreePartitionRole( + CPI_PARTITION_ROLE* pItm + ); +static void FreeUserInPartitionRole( + CPI_USER_IN_PARTITION_ROLE* pItm + ); +static HRESULT AddUserInPartitionRoleToActionData( + CPI_USER_IN_PARTITION_ROLE* pItm, + int iActionType, + int iActionCost, + LPWSTR* ppwzActionData + ); + + +// function definitions + +void CpiPartitionRoleListFree( + CPI_PARTITION_ROLE_LIST* pList + ) +{ + CPI_PARTITION_ROLE* pItm = pList->pFirst; + + while (pItm) + { + CPI_PARTITION_ROLE* pDelete = pItm; + pItm = pItm->pNext; + FreePartitionRole(pDelete); + } +} + +HRESULT CpiPartitionRolesRead( + CPI_PARTITION_LIST* pPartList, + CPI_PARTITION_ROLE_LIST* pPartRoleList + ) +{ + HRESULT hr = S_OK; + PMSIHANDLE hView, hRec; + CPI_PARTITION_ROLE* pItm = NULL; + LPWSTR pwzData = NULL; + + // loop through all application roles + hr = WcaOpenExecuteView(vcsPartitionRoleQuery, &hView); + ExitOnFailure(hr, "Failed to execute view on ComPlusPartitionRole table"); + + while (S_OK == (hr = WcaFetchRecord(hView, &hRec))) + { + // create entry + pItm = (CPI_PARTITION_ROLE*)::HeapAlloc(::GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(CPI_PARTITION_ROLE)); + if (!pItm) + ExitFunction1(hr = E_OUTOFMEMORY); + + // get key + hr = WcaGetRecordString(hRec, prqPartitionRole, &pwzData); + ExitOnFailure(hr, "Failed to get key"); + StringCchCopyW(pItm->wzKey, countof(pItm->wzKey), pwzData); + + // get partition + hr = WcaGetRecordString(hRec, prqPartition, &pwzData); + ExitOnFailure(hr, "Failed to get application"); + + hr = CpiPartitionFindByKey(pPartList, pwzData, &pItm->pPartition); + if (S_FALSE == hr) + hr = HRESULT_FROM_WIN32(ERROR_NOT_FOUND); + ExitOnFailure(hr, "Failed to find partition, key: %S", pwzData); + + // get name + hr = WcaGetRecordFormattedString(hRec, prqName, &pwzData); + ExitOnFailure(hr, "Failed to get name"); + StringCchCopyW(pItm->wzName, countof(pItm->wzName), pwzData); + + // add entry + if (pPartRoleList->pFirst) + pItm->pNext = pPartRoleList->pFirst; + pPartRoleList->pFirst = pItm; + pItm = NULL; + } + + if (E_NOMOREITEMS == hr) + hr = S_OK; + +LExit: + // clean up + if (pItm) + FreePartitionRole(pItm); + + ReleaseStr(pwzData); + + return hr; +} + +HRESULT CpiPartitionRoleFindByKey( + CPI_PARTITION_ROLE_LIST* pList, + LPCWSTR pwzKey, + CPI_PARTITION_ROLE** ppPartRole + ) +{ + for (CPI_PARTITION_ROLE* pItm = pList->pFirst; pItm; pItm = pItm->pNext) + { + if (0 == lstrcmpW(pItm->wzKey, pwzKey)) + { + *ppPartRole = pItm; + return S_OK; + } + } + + return E_FAIL; +} + +void CpiUserInPartitionRoleListFree( + CPI_USER_IN_PARTITION_ROLE_LIST* pList + ) +{ + CPI_USER_IN_PARTITION_ROLE* pItm = pList->pFirst; + + while (pItm) + { + CPI_USER_IN_PARTITION_ROLE* pDelete = pItm; + pItm = pItm->pNext; + FreeUserInPartitionRole(pDelete); + } +} + +HRESULT CpiUsersInPartitionRolesRead( + CPI_PARTITION_ROLE_LIST* pPartRoleList, + CPI_USER_IN_PARTITION_ROLE_LIST* pUsrInPartRoleList + ) +{ + HRESULT hr = S_OK; + + // read users in partition roles + if (CpiTableExists(cptComPlusUserInPartitionRole)) + { + hr = TrusteesInPartitionRolesRead(vcsUserInPartitionRoleQuery, pPartRoleList, pUsrInPartRoleList); + ExitOnFailure(hr, "Failed to read users in partition roles"); + } + + // read groups in partition roles + if (CpiTableExists(cptComPlusGroupInPartitionRole)) + { + hr = TrusteesInPartitionRolesRead(vcsGroupInPartitionRoleQuery, pPartRoleList, pUsrInPartRoleList); + ExitOnFailure(hr, "Failed to read groups in partition roles"); + } + + hr = S_OK; + +LExit: + return hr; +} + +HRESULT CpiUsersInPartitionRolesInstall( + CPI_USER_IN_PARTITION_ROLE_LIST* pList, + int iRunMode, + LPWSTR* ppwzActionData, + int* piProgress + ) +{ + HRESULT hr = S_OK; + + int iActionType; + + // add action text + hr = CpiAddActionTextToActionData(L"AddUsersToComPlusPartitionRoles", ppwzActionData); + ExitOnFailure(hr, "Failed to add action text to custom action data"); + + // add count to action data + hr = WcaWriteIntegerToCaData(pList->iInstallCount, ppwzActionData); + ExitOnFailure(hr, "Failed to add count to custom action data"); + + // add roles to custom action data + for (CPI_USER_IN_PARTITION_ROLE* pItm = pList->pFirst; pItm; pItm = pItm->pNext) + { + // roles that are being installed only + if (!WcaIsInstalling(pItm->isInstalled, pItm->isAction)) + continue; + + // action type + if (rmRollback == iRunMode) + { + if (CpiIsInstalled(pItm->isInstalled)) + iActionType = atNoOp; + else + iActionType = atRemove; + } + else + iActionType = atCreate; + + // add to action data + hr = AddUserInPartitionRoleToActionData(pItm, iActionType, COST_USER_IN_APPLICATION_ROLE_CREATE, ppwzActionData); + ExitOnFailure(hr, "Failed to add user in partition role to custom action data, key: %S", pItm->wzKey); + } + + // add progress tics + if (piProgress) + *piProgress += COST_USER_IN_APPLICATION_ROLE_CREATE * pList->iInstallCount; + + hr = S_OK; + +LExit: + return hr; +} + +HRESULT CpiUsersInPartitionRolesUninstall( + CPI_USER_IN_PARTITION_ROLE_LIST* pList, + int iRunMode, + LPWSTR* ppwzActionData, + int* piProgress + ) +{ + HRESULT hr = S_OK; + + int iActionType; + + // add action text + hr = CpiAddActionTextToActionData(L"RemoveUsersFromComPlusPartRoles", ppwzActionData); + ExitOnFailure(hr, "Failed to add action text to custom action data"); + + // add count to action data + hr = WcaWriteIntegerToCaData(pList->iUninstallCount, ppwzActionData); + ExitOnFailure(hr, "Failed to add count to custom action data"); + + // add roles to custom action data + for (CPI_USER_IN_PARTITION_ROLE* pItm = pList->pFirst; pItm; pItm = pItm->pNext) + { + // roles that are being uninstalled only + if (!WcaIsUninstalling(pItm->isInstalled, pItm->isAction)) + continue; + + // action type + if (rmRollback == iRunMode) + iActionType = atCreate; + else + iActionType = atRemove; + + // add to action data + hr = AddUserInPartitionRoleToActionData(pItm, iActionType, COST_USER_IN_APPLICATION_ROLE_DELETE, ppwzActionData); + ExitOnFailure(hr, "Failed to add user in partition role to custom action data, key: %S", pItm->wzKey); + } + + // add progress tics + if (piProgress) + *piProgress += COST_USER_IN_APPLICATION_ROLE_DELETE * pList->iUninstallCount; + + hr = S_OK; + +LExit: + return hr; +} + + +// helper function definitions + +static HRESULT TrusteesInPartitionRolesRead( + LPCWSTR pwzQuery, + CPI_PARTITION_ROLE_LIST* pPartRoleList, + CPI_USER_IN_PARTITION_ROLE_LIST* pUsrInPartRoleList + ) +{ + HRESULT hr = S_OK; + UINT er = ERROR_SUCCESS; + + PMSIHANDLE hView, hRec; + + CPI_USER_IN_PARTITION_ROLE* pItm = NULL; + LPWSTR pwzData = NULL; + LPWSTR pwzDomain = NULL; + LPWSTR pwzName = NULL; + BOOL fMatchingArchitecture = FALSE; + + // loop through all application roles + hr = WcaOpenExecuteView(pwzQuery, &hView); + ExitOnFailure(hr, "Failed to execute view on table"); + + while (S_OK == (hr = WcaFetchRecord(hView, &hRec))) + { + // get component + hr = WcaGetRecordString(hRec, tiprqComponent, &pwzData); + ExitOnFailure(hr, "Failed to get component"); + + // check if the component is our processor architecture + hr = CpiVerifyComponentArchitecure(pwzData, &fMatchingArchitecture); + ExitOnFailure(hr, "Failed to get component architecture."); + + if (!fMatchingArchitecture) + { + continue; // not the same architecture, ignore + } + + // create entry + pItm = (CPI_USER_IN_PARTITION_ROLE*)::HeapAlloc(::GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(CPI_USER_IN_PARTITION_ROLE)); + if (!pItm) + ExitFunction1(hr = E_OUTOFMEMORY); + + // get component install state + er = ::MsiGetComponentStateW(WcaGetInstallHandle(), pwzData, &pItm->isInstalled, &pItm->isAction); + ExitOnFailure(hr = HRESULT_FROM_WIN32(er), "Failed to get component state"); + + // get key + hr = WcaGetRecordString(hRec, tiprqUserInPartitionRole, &pwzData); + ExitOnFailure(hr, "Failed to get key"); + StringCchCopyW(pItm->wzKey, countof(pItm->wzKey), pwzData); + + // get partition role + hr = WcaGetRecordString(hRec, tiprqPartitionRole, &pwzData); + ExitOnFailure(hr, "Failed to get partition role"); + + hr = CpiPartitionRoleFindByKey(pPartRoleList, pwzData, &pItm->pPartitionRole); + ExitOnFailure(hr, "Failed to find partition role, key: %S", pwzData); + + // get user domain + hr = WcaGetRecordFormattedString(hRec, tiprqDomain, &pwzDomain); + ExitOnFailure(hr, "Failed to get domain"); + + // get user name + hr = WcaGetRecordFormattedString(hRec, tiprqName, &pwzName); + ExitOnFailure(hr, "Failed to get name"); + + // build account name + hr = CpiBuildAccountName(pwzDomain, pwzName, &pItm->pwzAccount); + ExitOnFailure(hr, "Failed to build account name"); + + // increment counters + if (WcaIsInstalling(pItm->isInstalled, pItm->isAction)) + pUsrInPartRoleList->iInstallCount++; + if (WcaIsUninstalling(pItm->isInstalled, pItm->isAction)) + pUsrInPartRoleList->iUninstallCount++; + + // add entry + if (pUsrInPartRoleList->pFirst) + pItm->pNext = pUsrInPartRoleList->pFirst; + pUsrInPartRoleList->pFirst = pItm; + pItm = NULL; + } + + if (E_NOMOREITEMS == hr) + hr = S_OK; + +LExit: + // clean up + if (pItm) + FreeUserInPartitionRole(pItm); + + ReleaseStr(pwzData); + ReleaseStr(pwzDomain); + ReleaseStr(pwzName); + + return hr; +} + +static void FreePartitionRole( + CPI_PARTITION_ROLE* pItm + ) +{ + ::HeapFree(::GetProcessHeap(), 0, pItm); +} + +static void FreeUserInPartitionRole( + CPI_USER_IN_PARTITION_ROLE* pItm + ) +{ + ReleaseStr(pItm->pwzAccount); + + ::HeapFree(::GetProcessHeap(), 0, pItm); +} + +static HRESULT AddUserInPartitionRoleToActionData( + CPI_USER_IN_PARTITION_ROLE* pItm, + int iActionType, + int iActionCost, + LPWSTR* ppwzActionData + ) +{ + HRESULT hr = S_OK; + + // add action information to custom action data + hr = WcaWriteIntegerToCaData(iActionType, ppwzActionData); + ExitOnFailure(hr, "Failed to add action type to custom action data"); + hr = WcaWriteIntegerToCaData(iActionCost, ppwzActionData); + ExitOnFailure(hr, "Failed to add action cost to custom action data"); + + // add application role information to custom action data + hr = WcaWriteStringToCaData(pItm->wzKey, ppwzActionData); + ExitOnFailure(hr, "Failed to add key to custom action data"); + hr = WcaWriteStringToCaData(pItm->pPartitionRole->wzName, ppwzActionData); + ExitOnFailure(hr, "Failed to add role name to custom action data"); + hr = WcaWriteStringToCaData(pItm->pwzAccount, ppwzActionData); + ExitOnFailure(hr, "Failed to add user account to custom action data"); + + // add partition information to custom action data + hr = WcaWriteStringToCaData(pItm->pPartitionRole->pPartition->wzID, ppwzActionData); + ExitOnFailure(hr, "Failed to add partition id to custom action data"); + + hr = S_OK; + +LExit: + return hr; +} diff --git a/src/ca/cppartrolesched.h b/src/ca/cppartrolesched.h new file mode 100644 index 00000000..ff1275d9 --- /dev/null +++ b/src/ca/cppartrolesched.h @@ -0,0 +1,76 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + + +struct CPI_PARTITION_ROLE +{ + WCHAR wzKey[MAX_DARWIN_KEY + 1]; + WCHAR wzName[MAX_DARWIN_COLUMN + 1]; + + CPI_PARTITION* pPartition; + + ICatalogCollection* piUsersColl; + + CPI_PARTITION_ROLE* pNext; +}; + +struct CPI_PARTITION_ROLE_LIST +{ + CPI_PARTITION_ROLE* pFirst; +}; + +struct CPI_USER_IN_PARTITION_ROLE +{ + WCHAR wzKey[MAX_DARWIN_KEY + 1]; + LPWSTR pwzAccount; + + INSTALLSTATE isInstalled, isAction; + + CPI_PARTITION_ROLE* pPartitionRole; + + CPI_USER_IN_PARTITION_ROLE* pNext; +}; + +struct CPI_USER_IN_PARTITION_ROLE_LIST +{ + CPI_USER_IN_PARTITION_ROLE* pFirst; + + int iInstallCount; + int iUninstallCount; +}; + + +// function prototypes + +void CpiPartitionRoleListFree( + CPI_PARTITION_ROLE_LIST* pList + ); +HRESULT CpiPartitionRolesRead( + CPI_PARTITION_LIST* pPartList, + CPI_PARTITION_ROLE_LIST* pPartRoleList + ); +HRESULT CpiPartitionRoleFindByKey( + CPI_PARTITION_ROLE_LIST* pList, + LPCWSTR pwzKey, + CPI_PARTITION_ROLE** ppPartRole + ); + +void CpiUserInPartitionRoleListFree( + CPI_USER_IN_PARTITION_ROLE_LIST* pList + ); +HRESULT CpiUsersInPartitionRolesRead( + CPI_PARTITION_ROLE_LIST* pPartRoleList, + CPI_USER_IN_PARTITION_ROLE_LIST* pUsrInPartRoleList + ); +HRESULT CpiUsersInPartitionRolesInstall( + CPI_USER_IN_PARTITION_ROLE_LIST* pList, + int iRunMode, + LPWSTR* ppwzActionData, + int* piProgress + ); +HRESULT CpiUsersInPartitionRolesUninstall( + CPI_USER_IN_PARTITION_ROLE_LIST* pList, + int iRunMode, + LPWSTR* ppwzActionData, + int* piProgress + ); diff --git a/src/ca/cppartsched.cpp b/src/ca/cppartsched.cpp new file mode 100644 index 00000000..6643a50b --- /dev/null +++ b/src/ca/cppartsched.cpp @@ -0,0 +1,912 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + + +// sql queries + +LPCWSTR vcsPartitionQuery = + L"SELECT `Partition`, `Component_`, `Id`, `Name` FROM `ComPlusPartition`"; +enum ePartitionQuery { pqPartition = 1, pqComponent, pqID, pqName }; + +LPCWSTR vcsPartitionPropertyQuery = + L"SELECT `Name`, `Value` FROM `ComPlusPartitionProperty` WHERE `Partition_` = ?"; + +LPCWSTR vcsPartitionUserQuery = + L"SELECT `PartitionUser`, `Partition_`, `ComPlusPartitionUser`.`Component_`, `Domain`, `Name` FROM `ComPlusPartitionUser`, `User` WHERE `User_` = `User`"; +enum ePartitionUserQuery { puqPartitionUser = 1, puqPartition, puqComponent, puqDomain, puqName }; + + +// property definitions + +CPI_PROPERTY_DEFINITION pdlPartitionProperties[] = +{ + {L"Changeable", cpptBoolean, 502}, + {L"Deleteable", cpptBoolean, 502}, + {L"Description", cpptString, 502}, + {NULL, cpptNone, 0} +}; + + +// prototypes for private helper functions + +static void FreePartition( + CPI_PARTITION* pItm + ); +static void FreePartitionUser( + CPI_PARTITION_USER* pItm + ); +static HRESULT AddPartitionToActionData( + CPI_PARTITION* pItm, + int iActionType, + int iActionCost, + LPWSTR* ppwzActionData + ); +static HRESULT AddPartitionUserToActionData( + CPI_PARTITION_USER* pItm, + int iActionType, + int iActionCost, + LPWSTR* ppwzActionData + ); + + +// function definitions + +void CpiPartitionListFree( + CPI_PARTITION_LIST* pList + ) +{ + CPI_PARTITION* pItm = pList->pFirst; + + while (pItm) + { + CPI_PARTITION* pDelete = pItm; + pItm = pItm->pNext; + FreePartition(pDelete); + } +} + +HRESULT CpiPartitionsRead( + CPI_PARTITION_LIST* pPartList + ) +{ + HRESULT hr = S_OK; + UINT er = ERROR_SUCCESS; + + PMSIHANDLE hView, hRec; + + CPI_PARTITION* pItm = NULL; + LPWSTR pwzData = NULL; + BOOL fMatchingArchitecture = FALSE; + + // loop through all partitions + hr = WcaOpenExecuteView(vcsPartitionQuery, &hView); + ExitOnFailure(hr, "Failed to execute view on ComPlusPartition table"); + + while (S_OK == (hr = WcaFetchRecord(hView, &hRec))) + { + // get component + hr = WcaGetRecordString(hRec, pqComponent, &pwzData); + ExitOnFailure(hr, "Failed to get component"); + + // check if the component is our processor architecture + if (pwzData && *pwzData) + { + hr = CpiVerifyComponentArchitecure(pwzData, &fMatchingArchitecture); + ExitOnFailure(hr, "Failed to get component architecture."); + + if (!fMatchingArchitecture) + { + continue; // not the same architecture, ignore + } + } + + // create entry + pItm = (CPI_PARTITION*)::HeapAlloc(::GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(CPI_PARTITION)); + if (!pItm) + ExitFunction1(hr = E_OUTOFMEMORY); + + // get component install state + if (pwzData && *pwzData) + { + pItm->fHasComponent = TRUE; + + er = ::MsiGetComponentStateW(WcaGetInstallHandle(), pwzData, &pItm->isInstalled, &pItm->isAction); + ExitOnFailure(hr = HRESULT_FROM_WIN32(er), "Failed to get component state"); + } + + // get key + hr = WcaGetRecordString(hRec, pqPartition, &pwzData); + ExitOnFailure(hr, "Failed to get key"); + StringCchCopyW(pItm->wzKey, countof(pItm->wzKey), pwzData); + + // get id + hr = WcaGetRecordFormattedString(hRec, pqID, &pwzData); + ExitOnFailure(hr, "Failed to get id"); + + if (pwzData && *pwzData) + { + hr = PcaGuidToRegFormat(pwzData, pItm->wzID, countof(pItm->wzID)); + ExitOnFailure(hr, "Failed to parse id guid value, key: %S, value: '%S'", pItm->wzKey, pwzData); + } + + // get name + hr = WcaGetRecordFormattedString(hRec, pqName, &pwzData); + ExitOnFailure(hr, "Failed to get name"); + StringCchCopyW(pItm->wzName, countof(pItm->wzName), pwzData); + + // if partition is a locater, either an id or a name must be provided + if (!pItm->fHasComponent && !*pItm->wzID && !*pItm->wzName) + ExitOnFailure(hr = E_FAIL, "A partition locater must have either an id or a name associated, key: %S", pItm->wzKey); + + // if partition is not a locater, an name must be provided + if (pItm->fHasComponent && !*pItm->wzName) + ExitOnFailure(hr = E_FAIL, "A partition must have a name associated, key: %S", pItm->wzKey); + + // get properties + if (CpiTableExists(cptComPlusPartitionProperty) && pItm->fHasComponent) + { + hr = CpiPropertiesRead(vcsPartitionPropertyQuery, pItm->wzKey, pdlPartitionProperties, &pItm->pProperties, &pItm->iPropertyCount); + ExitOnFailure(hr, "Failed to get properties"); + } + + // increment counters + if (pItm->fHasComponent && WcaIsInstalling(pItm->isInstalled, pItm->isAction)) + pPartList->iInstallCount++; + if (pItm->fHasComponent && WcaIsUninstalling(pItm->isInstalled, pItm->isAction)) + pPartList->iUninstallCount++; + + // add entry + if (pPartList->pFirst) + pItm->pNext = pPartList->pFirst; + pPartList->pFirst = pItm; + pItm = NULL; + } + + if (E_NOMOREITEMS == hr) + hr = S_OK; + +LExit: + // clean up + if (pItm) + FreePartition(pItm); + + ReleaseStr(pwzData); + + return hr; +} + +HRESULT CpiPartitionsVerifyInstall( + CPI_PARTITION_LIST* pList + ) +{ + HRESULT hr = S_OK; + UINT er = ERROR_SUCCESS; + + ICatalogCollection* piPartColl = NULL; + ICatalogObject* piPartObj = NULL; + + for (CPI_PARTITION* pItm = pList->pFirst; pItm; pItm = pItm->pNext) + { + // referenced locaters or partitions that are being installed + if (!pItm->fReferencedForInstall && !(pItm->fHasComponent && WcaIsInstalling(pItm->isInstalled, pItm->isAction))) + continue; + + // if the partition is referensed and is not a locater, it must be installed + if (pItm->fReferencedForInstall && pItm->fHasComponent && !CpiWillBeInstalled(pItm->isInstalled, pItm->isAction)) + MessageExitOnFailure(hr = E_FAIL, msierrComPlusPartitionDependency, "A partition is used by another entity being installed, but is not installed itself, key: %S", pItm->wzKey); + + // get partitions collection + if (!piPartColl) + { + hr = CpiGetPartitionsCollection(&piPartColl); + ExitOnFailure(hr, "Failed to get partitions collection"); + } + + // partition is supposed to exist + if (!pItm->fHasComponent || CpiIsInstalled(pItm->isInstalled)) + { + // get collection object for partition + hr = CpiFindCollectionObject(piPartColl, pItm->wzID, *pItm->wzID ? NULL : pItm->wzName, &piPartObj); + ExitOnFailure(hr, "Failed to find collection object for partition"); + + // if the partition was found + if (S_OK == hr) + { + // if we don't have an id, copy id from object + if (!*pItm->wzID) + { + hr = CpiGetKeyForObject(piPartObj, pItm->wzID, countof(pItm->wzID)); + ExitOnFailure(hr, "Failed to get id"); + } + } + + // if the partition was not found + else + { + // if the application is a locater, this is an error + if (!pItm->fHasComponent) + MessageExitOnFailure(hr = HRESULT_FROM_WIN32(ERROR_NOT_FOUND), msierrComPlusPartitionNotFound, "A partition required by this installation was not found, key: %S", pItm->wzKey); + + // create a new id if one is missing + if (!*pItm->wzID) + { + hr = CpiCreateId(pItm->wzID, countof(pItm->wzID)); + ExitOnFailure(hr, "Failed to create id"); + } + } + } + + // partition is supposed to be created + else + { + // check for conflicts + do { + if (*pItm->wzID) + { + // find partitions with conflicting id + hr = CpiFindCollectionObject(piPartColl, pItm->wzID, NULL, &piPartObj); + ExitOnFailure(hr, "Failed to find collection object for partition"); + + if (S_FALSE == hr) + { + // find partitions with conflicting name + hr = CpiFindCollectionObject(piPartColl, NULL, pItm->wzName, &piPartObj); + ExitOnFailure(hr, "Failed to find collection object for partition"); + + if (S_OK == hr) + // "A partition with a conflictiong name exists. retry cancel" + er = WcaErrorMessage(msierrComPlusPartitionNameConflict, hr, INSTALLMESSAGE_ERROR | MB_RETRYCANCEL, 0); + else + break; // no conflicting entry found, break loop + } + else + // "A partition with a conflicting id exists. abort retry ignore" + er = WcaErrorMessage(msierrComPlusPartitionIdConflict, hr, INSTALLMESSAGE_ERROR | MB_ABORTRETRYIGNORE, 0); + } + else + { + // find partitions with conflicting name + hr = CpiFindCollectionObject(piPartColl, NULL, pItm->wzName, &piPartObj); + ExitOnFailure(hr, "Failed to find collection object for partition"); + + if (S_OK == hr) + // "A partition with a conflictiong name exists. abort retry ignore" + er = WcaErrorMessage(msierrComPlusPartitionNameConflict, hr, INSTALLMESSAGE_ERROR | MB_ABORTRETRYIGNORE, 0); + else + break; // no conflicting entry found, break loop + } + + switch (er) + { + case IDCANCEL: + case IDABORT: + ExitOnFailure(hr = E_FAIL, "A partition with a conflictiong name or id exists, key: %S", pItm->wzKey); + break; + case IDRETRY: + break; + case IDIGNORE: + default: + // if we don't have an id, copy id from object + if (!*pItm->wzID) + { + hr = CpiGetKeyForObject(piPartObj, pItm->wzID, countof(pItm->wzID)); + ExitOnFailure(hr, "Failed to get id"); + } + hr = S_FALSE; // indicate that this is not a conflict + } + } while (S_OK == hr); // hr = S_FALSE if we don't have any conflicts + + // create a new id if one is missing + if (!*pItm->wzID) + { + hr = CpiCreateId(pItm->wzID, countof(pItm->wzID)); + ExitOnFailure(hr, "Failed to create id"); + } + } + + // clean up + ReleaseNullObject(piPartObj); + } + + hr = S_OK; + +LExit: + // clean up + ReleaseObject(piPartColl); + ReleaseObject(piPartObj); + + return hr; +} + +HRESULT CpiPartitionsVerifyUninstall( + CPI_PARTITION_LIST* pList + ) +{ + HRESULT hr = S_OK; + ICatalogCollection* piPartColl = NULL; + ICatalogObject* piPartObj = NULL; + + for (CPI_PARTITION* pItm = pList->pFirst; pItm; pItm = pItm->pNext) + { + // referenced locaters or partitions that are being uninstalled + if (!pItm->fReferencedForUninstall && !(pItm->fHasComponent && WcaIsUninstalling(pItm->isInstalled, pItm->isAction))) + continue; + + // get partitions collection + if (!piPartColl) + { + hr = CpiGetPartitionsCollection(&piPartColl); + ExitOnFailure(hr, "Failed to get partitions collection"); + } + + // get collection object for partition + hr = CpiFindCollectionObject(piPartColl, pItm->wzID, *pItm->wzID ? NULL : pItm->wzName, &piPartObj); + ExitOnFailure(hr, "Failed to find collection object for partition"); + + // if the partition was found + if (S_OK == hr) + { + // if we don't have an id, copy id from object + if (!*pItm->wzID) + { + hr = CpiGetKeyForObject(piPartObj, pItm->wzID, countof(pItm->wzID)); + ExitOnFailure(hr, "Failed to get id"); + } + } + + // if the partition was not found + else + { + pItm->fObjectNotFound = TRUE; + if (pItm->fHasComponent) + pList->iUninstallCount--; // elements with the fObjectNotFound flag set will not be scheduled for uninstall + } + + // clean up + ReleaseNullObject(piPartObj); + } + + hr = S_OK; + +LExit: + // clean up + ReleaseObject(piPartColl); + ReleaseObject(piPartObj); + + return hr; +} + +void CpiPartitionAddReferenceInstall( + CPI_PARTITION* pItm + ) +{ + pItm->fReferencedForInstall = TRUE; +} + +void CpiPartitionAddReferenceUninstall( + CPI_PARTITION* pItm + ) +{ + pItm->fReferencedForUninstall = TRUE; +} + +HRESULT CpiPartitionsInstall( + CPI_PARTITION_LIST* pList, + int iRunMode, + LPWSTR* ppwzActionData, + int* piProgress + ) +{ + HRESULT hr = S_OK; + + int iActionType; + + // add action text + hr = CpiAddActionTextToActionData(L"CreateComPlusPartitions", ppwzActionData); + ExitOnFailure(hr, "Failed to add action text to custom action data"); + + // add partition count to action data + hr = WcaWriteIntegerToCaData(pList->iInstallCount, ppwzActionData); + ExitOnFailure(hr, "Failed to add count to custom action data"); + + // add applications to custom action data + for (CPI_PARTITION* pItm = pList->pFirst; pItm; pItm = pItm->pNext) + { + // partitions that are being installed only + if (!pItm->fHasComponent || !WcaIsInstalling(pItm->isInstalled, pItm->isAction)) + continue; + + // action type + if (rmRollback == iRunMode) + { + if (CpiIsInstalled(pItm->isInstalled)) + iActionType = atNoOp; + else + iActionType = atRemove; + } + else + iActionType = atCreate; + + // add to action data + hr = AddPartitionToActionData(pItm, iActionType, COST_PARTITION_CREATE, ppwzActionData); + ExitOnFailure(hr, "Failed to add partition to custom action data, key: %S", pItm->wzKey); + } + + // add progress tics + if (piProgress) + *piProgress += COST_PARTITION_CREATE * pList->iInstallCount; + + hr = S_OK; + +LExit: + return hr; +} + +HRESULT CpiPartitionsUninstall( + CPI_PARTITION_LIST* pList, + int iRunMode, + LPWSTR* ppwzActionData, + int* piProgress + ) +{ + HRESULT hr = S_OK; + + int iActionType; + + // add action text + hr = CpiAddActionTextToActionData(L"RemoveComPlusPartitions", ppwzActionData); + ExitOnFailure(hr, "Failed to add action text to custom action data"); + + // add partition count to action data + hr = WcaWriteIntegerToCaData(pList->iUninstallCount, ppwzActionData); + ExitOnFailure(hr, "Failed to add count to custom action data"); + + // add partitions to custom action data + for (CPI_PARTITION* pItm = pList->pFirst; pItm; pItm = pItm->pNext) + { + // partitions that are being uninstalled only + if (!pItm->fHasComponent || pItm->fObjectNotFound || !WcaIsUninstalling(pItm->isInstalled, pItm->isAction)) + continue; + + // action type + if (rmRollback == iRunMode) + iActionType = atCreate; + else + iActionType = atRemove; + + // add to action data + hr = AddPartitionToActionData(pItm, iActionType, COST_PARTITION_DELETE, ppwzActionData); + ExitOnFailure(hr, "Failed to add partition to custom action data, key:", pItm->wzKey); + } + + // add progress tics + if (piProgress) + *piProgress += COST_PARTITION_DELETE * pList->iUninstallCount; + + hr = S_OK; + +LExit: + return hr; +} + +HRESULT CpiPartitionFindByKey( + CPI_PARTITION_LIST* pList, + LPCWSTR wzKey, + CPI_PARTITION** ppItm + ) +{ + for (CPI_PARTITION* pItm = pList->pFirst; pItm; pItm = pItm->pNext) + { + if (0 == lstrcmpW(pItm->wzKey, wzKey)) + { + *ppItm = pItm; + return S_OK; + } + } + + return S_FALSE; +} + +HRESULT CpiGetApplicationsCollForPartition( + CPI_PARTITION* pPart, + ICatalogCollection** ppiAppColl + ) +{ + HRESULT hr = S_OK; + + ICatalogCollection* piPartColl = NULL; + ICatalogObject* piPartObj = NULL; + + // if a previous attempt to locate the collection object failed + if (pPart->fObjectNotFound) + ExitFunction1(hr = S_FALSE); + + // get applications collection + if (!pPart->piApplicationsColl) + { + // get partitions collection from catalog + hr = CpiGetPartitionsCollection(&piPartColl); + ExitOnFailure(hr, "Failed to get partitions collection"); + + // find application object + hr = CpiFindCollectionObject(piPartColl, pPart->wzID, *pPart->wzID ? NULL : pPart->wzName, &piPartObj); + ExitOnFailure(hr, "Failed to find partition object"); + + if (S_FALSE == hr) + { + pPart->fObjectNotFound = TRUE; + ExitFunction(); // exit with hr = S_FALSE + } + + // get roles collection + hr = CpiGetCatalogCollection(piPartColl, piPartObj, L"Applications", &pPart->piApplicationsColl); + ExitOnFailure(hr, "Failed to get applications collection"); + } + + // return value + *ppiAppColl = pPart->piApplicationsColl; + (*ppiAppColl)->AddRef(); + + hr = S_OK; + +LExit: + // clean up + ReleaseObject(piPartColl); + ReleaseObject(piPartObj); + + return hr; +} + +HRESULT CpiGetRolesCollForPartition( + CPI_PARTITION* pPart, + ICatalogCollection** ppiRolesColl + ) +{ + HRESULT hr = S_OK; + + ICatalogCollection* piPartColl = NULL; + ICatalogObject* piPartObj = NULL; + + // if a previous attempt to locate the collection object failed + if (pPart->fObjectNotFound) + ExitFunction1(hr = S_FALSE); + + // get applications collection + if (!pPart->piRolesColl) + { + // get partitions collection from catalog + hr = CpiGetPartitionsCollection(&piPartColl); + ExitOnFailure(hr, "Failed to get partitions collection"); + + // find partition object + hr = CpiFindCollectionObject(piPartColl, pPart->wzID, *pPart->wzID ? NULL : pPart->wzName, &piPartObj); + ExitOnFailure(hr, "Failed to find partition object"); + + if (S_FALSE == hr) + ExitFunction(); // exit with hr = S_FALSE + + // get roles collection + hr = CpiGetCatalogCollection(piPartColl, piPartObj, L"RolesForPartition", &pPart->piRolesColl); + ExitOnFailure(hr, "Failed to get roles collection"); + } + + // return value + *ppiRolesColl = pPart->piRolesColl; + (*ppiRolesColl)->AddRef(); + + hr = S_OK; + +LExit: + // clean up + ReleaseObject(piPartColl); + ReleaseObject(piPartObj); + + return hr; +} + +void CpiPartitionUserListFree( + CPI_PARTITION_USER_LIST* pList + ) +{ + CPI_PARTITION_USER* pItm = pList->pFirst; + + while (pItm) + { + CPI_PARTITION_USER* pDelete = pItm; + pItm = pItm->pNext; + FreePartitionUser(pDelete); + } +} + +HRESULT CpiPartitionUsersRead( + CPI_PARTITION_LIST* pPartList, + CPI_PARTITION_USER_LIST* pPartUsrList + ) +{ + HRESULT hr = S_OK; + UINT er = ERROR_SUCCESS; + + PMSIHANDLE hView, hRec; + + CPI_PARTITION_USER* pItm = NULL; + LPWSTR pwzData = NULL; + LPWSTR pwzDomain = NULL; + LPWSTR pwzName = NULL; + BOOL fMatchingArchitecture = FALSE; + + // loop through all partition users + hr = WcaOpenExecuteView(vcsPartitionUserQuery, &hView); + ExitOnFailure(hr, "Failed to execute view on ComPlusPartitionUser table"); + + while (S_OK == (hr = WcaFetchRecord(hView, &hRec))) + { + // get component + hr = WcaGetRecordString(hRec, puqComponent, &pwzData); + ExitOnFailure(hr, "Failed to get component"); + + // check if the component is our processor architecture + hr = CpiVerifyComponentArchitecure(pwzData, &fMatchingArchitecture); + ExitOnFailure(hr, "Failed to get component architecture."); + + if (!fMatchingArchitecture) + { + continue; // not the same architecture, ignore + } + + // create entry + pItm = (CPI_PARTITION_USER*)::HeapAlloc(::GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(CPI_PARTITION_USER)); + if (!pItm) + ExitFunction1(hr = E_OUTOFMEMORY); + + // get component install state + er = ::MsiGetComponentStateW(WcaGetInstallHandle(), pwzData, &pItm->isInstalled, &pItm->isAction); + ExitOnFailure(hr = HRESULT_FROM_WIN32(er), "Failed to get component state"); + + // get key + hr = WcaGetRecordString(hRec, puqPartitionUser, &pwzData); + ExitOnFailure(hr, "Failed to get key"); + StringCchCopyW(pItm->wzKey, countof(pItm->wzKey), pwzData); + + // get partition + hr = WcaGetRecordString(hRec, puqPartition, &pwzData); + ExitOnFailure(hr, "Failed to get partition"); + + hr = CpiPartitionFindByKey(pPartList, pwzData, &pItm->pPartition); + if (S_FALSE == hr) + hr = HRESULT_FROM_WIN32(ERROR_NOT_FOUND); + ExitOnFailure(hr, "Failed to find partition, key: %S", pwzData); + + // get user domain + hr = WcaGetRecordFormattedString(hRec, puqDomain, &pwzDomain); + ExitOnFailure(hr, "Failed to get user domain"); + + // get user name + hr = WcaGetRecordFormattedString(hRec, puqName, &pwzName); + ExitOnFailure(hr, "Failed to get user name"); + + // build account name + hr = CpiBuildAccountName(pwzDomain, pwzName, &pItm->pwzAccount); + ExitOnFailure(hr, "Failed to build account name"); + + // set references & increment counters + if (WcaIsInstalling(pItm->isInstalled, pItm->isAction)) + { + pItm->pPartition->fReferencedForInstall = TRUE; + pPartUsrList->iInstallCount++; + } + if (WcaIsUninstalling(pItm->isInstalled, pItm->isAction)) + { + pItm->pPartition->fReferencedForUninstall = TRUE; + pPartUsrList->iUninstallCount++; + } + + // add entry + if (pPartUsrList->pFirst) + pItm->pNext = pPartUsrList->pFirst; + pPartUsrList->pFirst = pItm; + pItm = NULL; + } + + if (E_NOMOREITEMS == hr) + hr = S_OK; + +LExit: + // clean up + if (pItm) + FreePartitionUser(pItm); + + ReleaseStr(pwzData); + ReleaseStr(pwzDomain); + ReleaseStr(pwzName); + + return hr; +} + +HRESULT CpiPartitionUsersInstall( + CPI_PARTITION_USER_LIST* pList, + int iRunMode, + LPWSTR* ppwzActionData, + int* piProgress + ) +{ + HRESULT hr = S_OK; + + int iActionType; + + // add action text + hr = CpiAddActionTextToActionData(L"AddComPlusPartitionUsers", ppwzActionData); + ExitOnFailure(hr, "Failed to add action text to custom action data"); + + // add partition count to action data + hr = WcaWriteIntegerToCaData(pList->iInstallCount, ppwzActionData); + ExitOnFailure(hr, "Failed to add count to custom action data"); + + // add applications to custom action data + for (CPI_PARTITION_USER* pItm = pList->pFirst; pItm; pItm = pItm->pNext) + { + // partitions that are being installed only + if (!WcaIsInstalling(pItm->isInstalled, pItm->isAction)) + continue; + + // action type + if (rmRollback == iRunMode) + { + if (CpiIsInstalled(pItm->isInstalled)) + iActionType = atNoOp; + else + iActionType = atRemove; + } + else + iActionType = atCreate; + + // add to action data + hr = AddPartitionUserToActionData(pItm, iActionType, COST_PARTITION_USER_CREATE, ppwzActionData); + ExitOnFailure(hr, "Failed to add partition user to custom action data, key: %S", pItm->wzKey); + } + + // add progress tics + if (piProgress) + *piProgress += COST_PARTITION_USER_CREATE * pList->iInstallCount; + + hr = S_OK; + +LExit: + return hr; +} + +HRESULT CpiPartitionUsersUninstall( + CPI_PARTITION_USER_LIST* pList, + int iRunMode, + LPWSTR* ppwzActionData, + int* piProgress + ) +{ + HRESULT hr = S_OK; + + int iActionType; + + // add action text + hr = CpiAddActionTextToActionData(L"RemoveComPlusPartitionUsers", ppwzActionData); + ExitOnFailure(hr, "Failed to add action text to custom action data"); + + // add partition count to action data + hr = WcaWriteIntegerToCaData(pList->iUninstallCount, ppwzActionData); + ExitOnFailure(hr, "Failed to add count to custom action data"); + + // add partitions to custom action data + for (CPI_PARTITION_USER* pItm = pList->pFirst; pItm; pItm = pItm->pNext) + { + // partitions that are being uninstalled only + if (!WcaIsUninstalling(pItm->isInstalled, pItm->isAction)) + continue; + + // action type + if (rmRollback == iRunMode) + iActionType = atCreate; + else + iActionType = atRemove; + + // add to action data + hr = AddPartitionUserToActionData(pItm, iActionType, COST_PARTITION_USER_DELETE, ppwzActionData); + ExitOnFailure(hr, "Failed to add partition user to custom action data, key: %S", pItm->wzKey); + } + + // add progress tics + if (piProgress) + *piProgress += COST_PARTITION_USER_DELETE * pList->iUninstallCount; + + hr = S_OK; + +LExit: + return hr; +} + + +// helper function definitions + +static void FreePartition( + CPI_PARTITION* pItm + ) +{ + if (pItm->pProperties) + CpiPropertiesFreeList(pItm->pProperties); + + ReleaseObject(pItm->piApplicationsColl); + ReleaseObject(pItm->piRolesColl); + + ::HeapFree(::GetProcessHeap(), 0, pItm); +} + +static void FreePartitionUser( + CPI_PARTITION_USER* pItm + ) +{ + ReleaseStr(pItm->pwzAccount); + + ::HeapFree(::GetProcessHeap(), 0, pItm); +} + +static HRESULT AddPartitionToActionData( + CPI_PARTITION* pItm, + int iActionType, + int iActionCost, + LPWSTR* ppwzActionData + ) +{ + HRESULT hr = S_OK; + + // add action information to custom action data + hr = WcaWriteIntegerToCaData(iActionType, ppwzActionData); + ExitOnFailure(hr, "Failed to add action type to custom action data"); + hr = WcaWriteIntegerToCaData(iActionCost, ppwzActionData); + ExitOnFailure(hr, "Failed to add action cost to custom action data"); + + // add partition information to custom action data + hr = WcaWriteStringToCaData(pItm->wzKey, ppwzActionData); + ExitOnFailure(hr, "Failed to add partition key to custom action data"); + hr = WcaWriteStringToCaData(pItm->wzID, ppwzActionData); + ExitOnFailure(hr, "Failed to add partition id to custom action data"); + hr = WcaWriteStringToCaData(pItm->wzName, ppwzActionData); + ExitOnFailure(hr, "Failed to add partition name to custom action data"); + + // add properties to custom action data + hr = CpiAddPropertiesToActionData(atCreate == iActionType ? pItm->iPropertyCount : 0, pItm->pProperties, ppwzActionData); + ExitOnFailure(hr, "Failed to add properties to custom action data"); + + hr = S_OK; + +LExit: + return hr; +} + +static HRESULT AddPartitionUserToActionData( + CPI_PARTITION_USER* pItm, + int iActionType, + int iActionCost, + LPWSTR* ppwzActionData + ) +{ + HRESULT hr = S_OK; + + // add action information to custom action data + hr = WcaWriteIntegerToCaData(iActionType, ppwzActionData); + ExitOnFailure(hr, "Failed to add action type to custom action data"); + hr = WcaWriteIntegerToCaData(iActionCost, ppwzActionData); + ExitOnFailure(hr, "Failed to add action cost to custom action data"); + + // add partition user information to custom action data + hr = WcaWriteStringToCaData(pItm->wzKey, ppwzActionData); + ExitOnFailure(hr, "Failed to add partition user key to custom action data"); + hr = WcaWriteStringToCaData(pItm->pwzAccount, ppwzActionData); + ExitOnFailure(hr, "Failed to add user account to custom action data"); + + // add partition information to custom action data + hr = WcaWriteStringToCaData(atCreate == iActionType ? pItm->pPartition->wzID : L"", ppwzActionData); + ExitOnFailure(hr, "Failed to add partition id to custom action data"); + + hr = S_OK; + +LExit: + return hr; +} diff --git a/src/ca/cppartsched.h b/src/ca/cppartsched.h new file mode 100644 index 00000000..55085912 --- /dev/null +++ b/src/ca/cppartsched.h @@ -0,0 +1,125 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + + +struct CPI_PARTITION +{ + WCHAR wzKey[MAX_DARWIN_KEY + 1]; + WCHAR wzID[CPI_MAX_GUID + 1]; + WCHAR wzName[MAX_DARWIN_COLUMN + 1]; + + int iPropertyCount; + CPI_PROPERTY* pProperties; + + BOOL fHasComponent; + BOOL fReferencedForInstall; + BOOL fReferencedForUninstall; + BOOL fObjectNotFound; + + INSTALLSTATE isInstalled, isAction; + + ICatalogCollection* piApplicationsColl; + ICatalogCollection* piRolesColl; + + CPI_PARTITION* pNext; +}; + +struct CPI_PARTITION_LIST +{ + CPI_PARTITION* pFirst; + + int iInstallCount; + int iUninstallCount; +}; + +struct CPI_PARTITION_USER +{ + WCHAR wzKey[MAX_DARWIN_KEY + 1]; + LPWSTR pwzAccount; + + BOOL fNoFind; + + INSTALLSTATE isInstalled, isAction; + + CPI_PARTITION* pPartition; + + CPI_PARTITION_USER* pNext; +}; + +struct CPI_PARTITION_USER_LIST +{ + CPI_PARTITION_USER* pFirst; + + int iInstallCount; + int iUninstallCount; +}; + + +// function prototypes + +void CpiPartitionListFree( + CPI_PARTITION_LIST* pList + ); +HRESULT CpiPartitionsRead( + CPI_PARTITION_LIST* pPartList + ); +HRESULT CpiPartitionsVerifyInstall( + CPI_PARTITION_LIST* pList + ); +HRESULT CpiPartitionsVerifyUninstall( + CPI_PARTITION_LIST* pList + ); +void CpiPartitionAddReferenceInstall( + CPI_PARTITION* pItm + ); +void CpiPartitionAddReferenceUninstall( + CPI_PARTITION* pItm + ); +HRESULT CpiPartitionsInstall( + CPI_PARTITION_LIST* pList, + int iRunMode, + LPWSTR* ppwzActionData, + int* piProgress + ); +HRESULT CpiPartitionsUninstall( + CPI_PARTITION_LIST* pList, + int iRunMode, + LPWSTR* ppwzActionData, + int* piProgress + ); +HRESULT CpiPartitionFindByKey( + CPI_PARTITION_LIST* pList, + LPCWSTR wzKey, + CPI_PARTITION** ppItm + ); +HRESULT CpiGetApplicationsCollForPartition( + CPI_PARTITION* pPart, + ICatalogCollection** ppiAppColl + ); +HRESULT CpiGetPartitionUsersCollection( + CPI_PARTITION* pPart, + ICatalogCollection** ppiPartUsrColl + ); +HRESULT CpiGetRolesCollForPartition( + CPI_PARTITION* pPart, + ICatalogCollection** ppiRolesColl + ); +void CpiPartitionUserListFree( + CPI_PARTITION_USER_LIST* pList + ); +HRESULT CpiPartitionUsersRead( + CPI_PARTITION_LIST* pPartList, + CPI_PARTITION_USER_LIST* pPartUsrList + ); +HRESULT CpiPartitionUsersInstall( + CPI_PARTITION_USER_LIST* pList, + int iRunMode, + LPWSTR* ppwzActionData, + int* piProgress + ); +HRESULT CpiPartitionUsersUninstall( + CPI_PARTITION_USER_LIST* pList, + int iRunMode, + LPWSTR* ppwzActionData, + int* piProgress + ); diff --git a/src/ca/cpsched.cpp b/src/ca/cpsched.cpp new file mode 100644 index 00000000..ac0dda59 --- /dev/null +++ b/src/ca/cpsched.cpp @@ -0,0 +1,590 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + + +#ifdef _WIN64 +#define CP_COMPLUSROLLBACKINSTALLPREPARE L"ComPlusRollbackInstallPrepare_64" +#define CP_COMPLUSINSTALLPREPARE L"ComPlusInstallPrepare_64" +#define CP_COMPLUSROLLBACKINSTALLEXECUTE L"ComPlusRollbackInstallExecute_64" +#define CP_COMPLUSINSTALLEXECUTE L"ComPlusInstallExecute_64" +#define CP_COMPLUSINSTALLEXECUTECOMMIT L"ComPlusInstallExecuteCommit_64" +#define CP_COMPLUSINSTALLCOMMIT L"ComPlusInstallCommit_64" +#define CP_COMPLUSROLLBACKINSTALLPREPARE L"ComPlusRollbackInstallPrepare_64" +#define CP_COMPLUSINSTALLPREPARE L"ComPlusInstallPrepare_64" +#define CP_COMPLUSROLLBACKUNINSTALLEXECUTE L"ComPlusRollbackUninstallExecute_64" +#define CP_COMPLUSUNINSTALLEXECUTE L"ComPlusUninstallExecute_64" +#define CP_COMPLUSINSTALLCOMMIT L"ComPlusInstallCommit_64" +#else +#define CP_COMPLUSROLLBACKINSTALLPREPARE L"ComPlusRollbackInstallPrepare" +#define CP_COMPLUSINSTALLPREPARE L"ComPlusInstallPrepare" +#define CP_COMPLUSROLLBACKINSTALLEXECUTE L"ComPlusRollbackInstallExecute" +#define CP_COMPLUSINSTALLEXECUTE L"ComPlusInstallExecute" +#define CP_COMPLUSINSTALLEXECUTECOMMIT L"ComPlusInstallExecuteCommit" +#define CP_COMPLUSINSTALLCOMMIT L"ComPlusInstallCommit" +#define CP_COMPLUSROLLBACKINSTALLPREPARE L"ComPlusRollbackInstallPrepare" +#define CP_COMPLUSINSTALLPREPARE L"ComPlusInstallPrepare" +#define CP_COMPLUSROLLBACKUNINSTALLEXECUTE L"ComPlusRollbackUninstallExecute" +#define CP_COMPLUSUNINSTALLEXECUTE L"ComPlusUninstallExecute" +#define CP_COMPLUSINSTALLCOMMIT L"ComPlusInstallCommit" +#endif + + +/******************************************************************** + DllMain - standard entry point for all WiX CustomActions + +********************************************************************/ +extern "C" BOOL WINAPI DllMain( + IN HINSTANCE hInst, + IN ULONG ulReason, + IN LPVOID) +{ + switch(ulReason) + { + case DLL_PROCESS_ATTACH: + WcaGlobalInitialize(hInst); + break; + + case DLL_PROCESS_DETACH: + WcaGlobalFinalize(); + break; + } + + return TRUE; +} + +/******************************************************************** + ConfigureComPlusInstall - CUSTOM ACTION ENTRY POINT for installing COM+ components + +********************************************************************/ +extern "C" UINT __stdcall ConfigureComPlusInstall(MSIHANDLE hInstall) +{ + HRESULT hr = S_OK; + UINT er = ERROR_SUCCESS; + + BOOL fInitializedCom = FALSE; + + ICOMAdminCatalog* piCatalog = NULL; + + CPI_PARTITION_LIST partList; + CPI_PARTITION_ROLE_LIST partRoleList; + CPI_USER_IN_PARTITION_ROLE_LIST usrInPartRoleList; + CPI_PARTITION_USER_LIST partUsrList; + CPI_APPLICATION_LIST appList; + CPI_APPLICATION_ROLE_LIST appRoleList; + CPI_USER_IN_APPLICATION_ROLE_LIST usrInAppRoleList; + CPI_ASSEMBLY_LIST asmList; + CPI_SUBSCRIPTION_LIST subList; + + LPWSTR pwzRollbackFileName = NULL; + LPWSTR pwzActionData = NULL; + LPWSTR pwzRollbackActionData = NULL; + LPWSTR pwzCommitActionData = NULL; + + int iVersionNT = 0; + int iProgress = 0; + int iCommitProgress = 0; + + ::ZeroMemory(&partList, sizeof(CPI_PARTITION_LIST)); + ::ZeroMemory(&partRoleList, sizeof(CPI_PARTITION_ROLE_LIST)); + ::ZeroMemory(&usrInPartRoleList, sizeof(CPI_USER_IN_PARTITION_ROLE_LIST)); + ::ZeroMemory(&partUsrList, sizeof(CPI_PARTITION_USER_LIST)); + ::ZeroMemory(&appList, sizeof(CPI_APPLICATION_LIST)); + ::ZeroMemory(&appRoleList, sizeof(CPI_APPLICATION_ROLE_LIST)); + ::ZeroMemory(&usrInAppRoleList, sizeof(CPI_USER_IN_APPLICATION_ROLE_LIST)); + ::ZeroMemory(&asmList, sizeof(CPI_ASSEMBLY_LIST)); + ::ZeroMemory(&subList, sizeof(CPI_SUBSCRIPTION_LIST)); + + // initialize + hr = WcaInitialize(hInstall, "ConfigureComPlusInstall"); + ExitOnFailure(hr, "Failed to initialize"); + + hr = ::CoInitialize(NULL); + ExitOnFailure(hr, "Failed to initialize COM"); + fInitializedCom = TRUE; + + CpiInitialize(); + + // check for the prerequsite tables + if (!CpiTableExists(cptComPlusPartition) && !CpiTableExists(cptComPlusApplication) && !CpiTableExists(cptComPlusAssembly)) + { + WcaLog(LOGMSG_VERBOSE, "skipping install COM+ CustomAction, no ComPlusPartition, ComPlusApplication or ComPlusAssembly table present"); + ExitFunction1(hr = S_FALSE); + } + + // make sure we can access the COM+ admin catalog + do { + hr = CpiGetAdminCatalog(&piCatalog); + if (FAILED(hr)) + { + WcaLog(LOGMSG_STANDARD, "Failed to get COM+ admin catalog"); + er = WcaErrorMessage(msierrComPlusCannotConnect, hr, INSTALLMESSAGE_ERROR | MB_ABORTRETRYIGNORE, 0); + switch (er) + { + case IDABORT: + ExitFunction(); // exit with hr from CpiGetAdminCatalog() to kick off a rollback + case IDRETRY: + hr = S_FALSE; + break; + case IDIGNORE: + default: + ExitFunction1(hr = S_OK); // pretend everything is okay and bail + } + } + } while (S_FALSE == hr); + + // get NT version + hr = WcaGetIntProperty(L"VersionNT", &iVersionNT); + ExitOnFailure(hr, "Failed to get VersionNT property"); + + // read elements + if (502 <= iVersionNT && CpiTableExists(cptComPlusPartition)) + { + hr = CpiPartitionsRead(&partList); + MessageExitOnFailure(hr, msierrComPlusPartitionReadFailed, "Failed to read ComPlusPartitions table"); + } + + if (502 <= iVersionNT && CpiTableExists(cptComPlusPartitionRole)) + { + hr = CpiPartitionRolesRead(&partList, &partRoleList); + MessageExitOnFailure(hr, msierrComPlusPartitionRoleReadFailed, "Failed to read ComPlusPartitionRole table"); + } + + if (502 <= iVersionNT && (CpiTableExists(cptComPlusUserInPartitionRole) || CpiTableExists(cptComPlusGroupInPartitionRole))) + { + hr = CpiUsersInPartitionRolesRead(&partRoleList, &usrInPartRoleList); + MessageExitOnFailure(hr, msierrComPlusUserInPartitionRoleReadFailed, "Failed to read ComPlusUserInPartitionRole table"); + } + + if (502 <= iVersionNT && CpiTableExists(cptComPlusPartitionUser)) + { + hr = CpiPartitionUsersRead(&partList, &partUsrList); + MessageExitOnFailure(hr, msierrComPlusPartitionUserReadFailed, "Failed to read ComPlusPartitionUser table"); + } + + if (CpiTableExists(cptComPlusApplication)) + { + hr = CpiApplicationsRead(&partList, &appList); + MessageExitOnFailure(hr, msierrComPlusApplicationReadFailed, "Failed to read ComPlusApplication table"); + } + + if (CpiTableExists(cptComPlusApplicationRole)) + { + hr = CpiApplicationRolesRead(&appList, &appRoleList); + MessageExitOnFailure(hr, msierrComPlusApplicationRoleReadFailed, "Failed to read ComPlusApplicationRole table"); + } + + if (CpiTableExists(cptComPlusUserInApplicationRole) || CpiTableExists(cptComPlusGroupInApplicationRole)) + { + hr = CpiUsersInApplicationRolesRead(&appRoleList, &usrInAppRoleList); + MessageExitOnFailure(hr, msierrComPlusUserInApplicationRoleReadFailed, "Failed to read ComPlusUserInApplicationRole table"); + } + + if (CpiTableExists(cptComPlusAssembly)) + { + hr = CpiAssembliesRead(&appList, &appRoleList, &asmList); + MessageExitOnFailure(hr, msierrComPlusAssembliesReadFailed, "Failed to read ComPlusAssembly table"); + } + + if (CpiTableExists(cptComPlusSubscription)) + { + hr = CpiSubscriptionsRead(&asmList, &subList); + MessageExitOnFailure(hr, msierrComPlusSubscriptionReadFailed, "Failed to read ComPlusSubscription table"); + } + + // verify elements + hr = CpiPartitionsVerifyInstall(&partList); + ExitOnFailure(hr, "Failed to verify partitions"); + + hr = CpiApplicationsVerifyInstall(&appList); + ExitOnFailure(hr, "Failed to verify applications"); + + hr = CpiApplicationRolesVerifyInstall(&appRoleList); + ExitOnFailure(hr, "Failed to verify application roles"); + + hr = CpiAssembliesVerifyInstall(&asmList); + ExitOnFailure(hr, "Failed to verify assemblies"); + + if (subList.iInstallCount) + { + hr = CpiSubscriptionsVerifyInstall(&subList); + ExitOnFailure(hr, "Failed to verify subscriptions"); + } + + // schedule + if (partList.iInstallCount || appList.iInstallCount || usrInAppRoleList.iInstallCount || + appRoleList.iInstallCount || asmList.iInstallCount || asmList.iRoleInstallCount || subList.iInstallCount) + { + // create rollback file name + hr = CpiGetTempFileName(&pwzRollbackFileName); + ExitOnFailure(hr, "Failed to get rollback file name"); + + // schedule rollback prepare custom action + hr = WcaDoDeferredAction(CP_COMPLUSROLLBACKINSTALLPREPARE, pwzRollbackFileName, 0); + ExitOnFailure(hr, "Failed to schedule ComPlusRollbackInstallPrepare"); + + // schedule prepare custom action + hr = WcaDoDeferredAction(CP_COMPLUSINSTALLPREPARE, pwzRollbackFileName, 0); + ExitOnFailure(hr, "Failed to schedule ComPlusInstallPrepare"); + + // schedule rollback custom action + hr = WcaWriteStringToCaData(pwzRollbackFileName, &pwzRollbackActionData); + ExitOnFailure(hr, "Failed to add rollback file name to rollback custom action data"); + + hr = CpiSubscriptionsInstall(&subList, rmRollback, &pwzRollbackActionData, NULL); + ExitOnFailure(hr, "Failed to install subscriptions"); + hr = CpiRoleAssignmentsInstall(&asmList, rmRollback, &pwzRollbackActionData, NULL); + ExitOnFailure(hr, "Failed to install assemblies"); + hr = CpiAssembliesInstall(&asmList, rmRollback, &pwzRollbackActionData, NULL); + ExitOnFailure(hr, "Failed to install assemblies"); + hr = CpiUsersInApplicationRolesInstall(&usrInAppRoleList, rmRollback, &pwzRollbackActionData, NULL); + ExitOnFailure(hr, "Failed to install users in application roles"); + hr = CpiApplicationRolesInstall(&appRoleList, rmRollback, &pwzRollbackActionData, NULL); + ExitOnFailure(hr, "Failed to install application roles"); + hr = CpiApplicationsInstall(&appList, rmRollback, &pwzRollbackActionData, NULL); + ExitOnFailure(hr, "Failed to install applications"); + hr = CpiPartitionUsersInstall(&partUsrList, rmRollback, &pwzRollbackActionData, NULL); + ExitOnFailure(hr, "Failed to install partition users"); + hr = CpiUsersInPartitionRolesInstall(&usrInPartRoleList, rmRollback, &pwzRollbackActionData, NULL); + ExitOnFailure(hr, "Failed to install users in partition roles"); + hr = CpiPartitionsInstall(&partList, rmRollback, &pwzRollbackActionData, NULL); + ExitOnFailure(hr, "Failed to install partitions"); + + hr = WcaDoDeferredAction(CP_COMPLUSROLLBACKINSTALLEXECUTE, pwzRollbackActionData, 0); + ExitOnFailure(hr, "Failed to schedule ComPlusRollbackInstallExecute"); + + // schedule install custom action + hr = WcaWriteStringToCaData(pwzRollbackFileName, &pwzActionData); + ExitOnFailure(hr, "Failed to add rollback file name to custom action data"); + + hr = CpiPartitionsInstall(&partList, rmDeferred, &pwzActionData, &iProgress); + ExitOnFailure(hr, "Failed to install partitions"); + hr = CpiUsersInPartitionRolesInstall(&usrInPartRoleList, rmDeferred, &pwzActionData, &iProgress); + ExitOnFailure(hr, "Failed to install users in partition roles"); + hr = CpiPartitionUsersInstall(&partUsrList, rmDeferred, &pwzActionData, &iProgress); + ExitOnFailure(hr, "Failed to install partition users"); + hr = CpiApplicationsInstall(&appList, rmDeferred, &pwzActionData, &iProgress); + ExitOnFailure(hr, "Failed to install applications"); + hr = CpiApplicationRolesInstall(&appRoleList, rmDeferred, &pwzActionData, &iProgress); + ExitOnFailure(hr, "Failed to install application roles"); + hr = CpiUsersInApplicationRolesInstall(&usrInAppRoleList, rmDeferred, &pwzActionData, &iProgress); + ExitOnFailure(hr, "Failed to install users in application roles"); + hr = CpiAssembliesInstall(&asmList, rmDeferred, &pwzActionData, &iProgress); + ExitOnFailure(hr, "Failed to install assemblies"); + hr = CpiRoleAssignmentsInstall(&asmList, rmDeferred, &pwzActionData, &iProgress); + ExitOnFailure(hr, "Failed to install assemblies"); + hr = CpiSubscriptionsInstall(&subList, rmDeferred, &pwzActionData, &iProgress); + ExitOnFailure(hr, "Failed to install subscriptions"); + + hr = WcaDoDeferredAction(CP_COMPLUSINSTALLEXECUTE, pwzActionData, iProgress); + ExitOnFailure(hr, "Failed to schedule ComPlusInstallExecute"); + + // schedule install commit custom action + hr = WcaWriteStringToCaData(pwzRollbackFileName, &pwzCommitActionData); + ExitOnFailure(hr, "Failed to add rollback file name to commit custom action data"); + + hr = CpiAssembliesInstall(&asmList, rmCommit, &pwzCommitActionData, &iCommitProgress); + ExitOnFailure(hr, "Failed to install assemblies"); + hr = CpiRoleAssignmentsInstall(&asmList, rmCommit, &pwzCommitActionData, &iCommitProgress); + ExitOnFailure(hr, "Failed to install assemblies"); + hr = CpiSubscriptionsInstall(&subList, rmCommit, &pwzCommitActionData, &iCommitProgress); + ExitOnFailure(hr, "Failed to install subscriptions"); + + hr = WcaDoDeferredAction(CP_COMPLUSINSTALLEXECUTECOMMIT, pwzCommitActionData, iCommitProgress); + ExitOnFailure(hr, "Failed to schedule ComPlusInstallExecuteCommit"); + + // schedule commit custom action + hr = WcaDoDeferredAction(CP_COMPLUSINSTALLCOMMIT, pwzRollbackFileName, 0); + ExitOnFailure(hr, "Failed to schedule ComPlusInstallCommit"); + } + + hr = S_OK; + +LExit: + // clean up + ReleaseObject(piCatalog); + + ReleaseStr(pwzRollbackFileName); + ReleaseStr(pwzActionData); + ReleaseStr(pwzRollbackActionData); + ReleaseStr(pwzCommitActionData); + + CpiPartitionListFree(&partList); + CpiPartitionRoleListFree(&partRoleList); + CpiUserInPartitionRoleListFree(&usrInPartRoleList); + CpiPartitionUserListFree(&partUsrList); + CpiApplicationListFree(&appList); + CpiApplicationRoleListFree(&appRoleList); + CpiUserInApplicationRoleListFree(&usrInAppRoleList); + CpiAssemblyListFree(&asmList); + CpiSubscriptionListFree(&subList); + + // unitialize + CpiFinalize(); + + if (fInitializedCom) + ::CoUninitialize(); + + er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE; + return WcaFinalize(er); +} + + +/******************************************************************** + ConfigureComPlusUninstall - CUSTOM ACTION ENTRY POINT for uninstalling COM+ components + +********************************************************************/ +extern "C" UINT __stdcall ConfigureComPlusUninstall(MSIHANDLE hInstall) +{ + HRESULT hr = S_OK; + UINT er = ERROR_SUCCESS; + + BOOL fInitializedCom = FALSE; + + ICOMAdminCatalog* piCatalog = NULL; + + CPI_PARTITION_LIST partList; + CPI_PARTITION_ROLE_LIST partRoleList; + CPI_USER_IN_PARTITION_ROLE_LIST usrInPartRoleList; + CPI_PARTITION_USER_LIST partUsrList; + CPI_APPLICATION_LIST appList; + CPI_APPLICATION_ROLE_LIST appRoleList; + CPI_USER_IN_APPLICATION_ROLE_LIST usrInAppRoleList; + CPI_ASSEMBLY_LIST asmList; + CPI_SUBSCRIPTION_LIST subList; + + LPWSTR pwzRollbackFileName = NULL; + LPWSTR pwzActionData = NULL; + LPWSTR pwzRollbackActionData = NULL; + + int iVersionNT = 0; + int iProgress = 0; + + ::ZeroMemory(&partList, sizeof(CPI_PARTITION_LIST)); + ::ZeroMemory(&partRoleList, sizeof(CPI_PARTITION_ROLE_LIST)); + ::ZeroMemory(&usrInPartRoleList, sizeof(CPI_USER_IN_PARTITION_ROLE_LIST)); + ::ZeroMemory(&partUsrList, sizeof(CPI_PARTITION_USER_LIST)); + ::ZeroMemory(&appList, sizeof(CPI_APPLICATION_LIST)); + ::ZeroMemory(&appRoleList, sizeof(CPI_APPLICATION_ROLE_LIST)); + ::ZeroMemory(&usrInAppRoleList, sizeof(CPI_USER_IN_APPLICATION_ROLE_LIST)); + ::ZeroMemory(&asmList, sizeof(CPI_ASSEMBLY_LIST)); + ::ZeroMemory(&subList, sizeof(CPI_SUBSCRIPTION_LIST)); + + // initialize + hr = WcaInitialize(hInstall, "ConfigureComPlusUninstall"); + ExitOnFailure(hr, "Failed to initialize"); + + hr = ::CoInitialize(NULL); + ExitOnFailure(hr, "Failed to initialize COM"); + fInitializedCom = TRUE; + + CpiInitialize(); + + // check for the prerequsite tables + if (!CpiTableExists(cptComPlusPartition) && !CpiTableExists(cptComPlusApplication) && !CpiTableExists(cptComPlusAssembly)) + { + WcaLog(LOGMSG_VERBOSE, "skipping uninstall COM+ CustomAction, no ComPlusPartition, ComPlusApplication or ComPlusAssembly table present"); + ExitFunction1(hr = S_FALSE); + } + + // make sure we can access the COM+ admin catalog + do { + hr = CpiGetAdminCatalog(&piCatalog); + if (FAILED(hr)) + { + WcaLog(LOGMSG_STANDARD, "Failed to get COM+ admin catalog"); + er = WcaErrorMessage(msierrComPlusCannotConnect, hr, INSTALLMESSAGE_ERROR | MB_ABORTRETRYIGNORE, 0); + switch (er) + { + case IDABORT: + ExitFunction(); // exit with hr from CpiGetAdminCatalog() to kick off a rollback + case IDRETRY: + hr = S_FALSE; + break; + case IDIGNORE: + default: + ExitFunction1(hr = S_OK); // pretend everything is okay and bail + } + } + } while (S_FALSE == hr); + + // get NT version + hr = WcaGetIntProperty(L"VersionNT", &iVersionNT); + ExitOnFailure(hr, "Failed to get VersionNT property"); + + // read elements + if (502 <= iVersionNT && CpiTableExists(cptComPlusPartition)) + { + hr = CpiPartitionsRead(&partList); + MessageExitOnFailure(hr, msierrComPlusPartitionReadFailed, "Failed to read ComPlusPartitions table"); + } + + if (502 <= iVersionNT && CpiTableExists(cptComPlusPartitionRole)) + { + hr = CpiPartitionRolesRead(&partList, &partRoleList); + MessageExitOnFailure(hr, msierrComPlusPartitionRoleReadFailed, "Failed to read ComPlusPartitionRole table"); + } + + if (502 <= iVersionNT && (CpiTableExists(cptComPlusUserInPartitionRole) || CpiTableExists(cptComPlusGroupInPartitionRole))) + { + hr = CpiUsersInPartitionRolesRead(&partRoleList, &usrInPartRoleList); + MessageExitOnFailure(hr, msierrComPlusUserInPartitionRoleReadFailed, "Failed to read ComPlusUserInPartitionRole table"); + } + + if (502 <= iVersionNT && CpiTableExists(cptComPlusPartitionUser)) + { + hr = CpiPartitionUsersRead(&partList, &partUsrList); + MessageExitOnFailure(hr, msierrComPlusPartitionUserReadFailed, "Failed to read ComPlusPartitionUser table"); + } + + if (CpiTableExists(cptComPlusApplication)) + { + hr = CpiApplicationsRead(&partList, &appList); + MessageExitOnFailure(hr, msierrComPlusApplicationReadFailed, "Failed to read ComPlusApplication table"); + } + + if (CpiTableExists(cptComPlusApplicationRole)) + { + hr = CpiApplicationRolesRead(&appList, &appRoleList); + MessageExitOnFailure(hr, msierrComPlusApplicationRoleReadFailed, "Failed to read ComPlusApplicationRole table"); + } + + if (CpiTableExists(cptComPlusUserInApplicationRole) || CpiTableExists(cptComPlusGroupInApplicationRole)) + { + hr = CpiUsersInApplicationRolesRead(&appRoleList, &usrInAppRoleList); + MessageExitOnFailure(hr, msierrComPlusUserInApplicationRoleReadFailed, "Failed to read ComPlusUserInApplicationRole table"); + } + + if (CpiTableExists(cptComPlusAssembly)) + { + hr = CpiAssembliesRead(&appList, &appRoleList, &asmList); + MessageExitOnFailure(hr, msierrComPlusAssembliesReadFailed, "Failed to read ComPlusAssembly table"); + } + + if (CpiTableExists(cptComPlusSubscription)) + { + hr = CpiSubscriptionsRead(&asmList, &subList); + MessageExitOnFailure(hr, msierrComPlusSubscriptionReadFailed, "Failed to read ComPlusSubscription table"); + } + + // verify elements + hr = CpiPartitionsVerifyUninstall(&partList); + ExitOnFailure(hr, "Failed to verify partitions"); + + hr = CpiApplicationsVerifyUninstall(&appList); + ExitOnFailure(hr, "Failed to verify applications"); + + hr = CpiApplicationRolesVerifyUninstall(&appRoleList); + ExitOnFailure(hr, "Failed to verify application roles"); + + hr = CpiAssembliesVerifyUninstall(&asmList); + ExitOnFailure(hr, "Failed to verify assemblies"); + + if (subList.iUninstallCount) + { + hr = CpiSubscriptionsVerifyUninstall(&subList); + ExitOnFailure(hr, "Failed to verify subscriptions"); + } + + // schedule + if (partList.iUninstallCount || appList.iUninstallCount || appRoleList.iUninstallCount || + usrInAppRoleList.iUninstallCount || asmList.iUninstallCount || asmList.iRoleUninstallCount || subList.iUninstallCount) + { + // create rollback file name + hr = CpiGetTempFileName(&pwzRollbackFileName); + ExitOnFailure(hr, "Failed to get rollback file name"); + + // schedule rollback prepare custom action + hr = WcaDoDeferredAction(CP_COMPLUSROLLBACKINSTALLPREPARE, pwzRollbackFileName, 0); + ExitOnFailure(hr, "Failed to schedule ComPlusRollbackInstallPrepare"); + + // schedule prepare custom action + hr = WcaDoDeferredAction(CP_COMPLUSINSTALLPREPARE, pwzRollbackFileName, 0); + ExitOnFailure(hr, "Failed to schedule ComPlusInstallPrepare"); + + // schedule rollback custom action + hr = WcaWriteStringToCaData(pwzRollbackFileName, &pwzRollbackActionData); + ExitOnFailure(hr, "Failed to add rollback file name to rollback custom action data"); + + hr = CpiPartitionsUninstall(&partList, rmRollback, &pwzRollbackActionData, NULL); + ExitOnFailure(hr, "Failed to uninstall partitions"); + hr = CpiUsersInPartitionRolesUninstall(&usrInPartRoleList, rmRollback, &pwzRollbackActionData, NULL); + ExitOnFailure(hr, "Failed to uninstall users in partition roles"); + hr = CpiPartitionUsersUninstall(&partUsrList, rmRollback, &pwzRollbackActionData, NULL); + ExitOnFailure(hr, "Failed to uninstall partition users"); + hr = CpiApplicationsUninstall(&appList, rmRollback, &pwzRollbackActionData, NULL); + ExitOnFailure(hr, "Failed to uninstall applications"); + hr = CpiApplicationRolesUninstall(&appRoleList, rmRollback, &pwzRollbackActionData, NULL); + ExitOnFailure(hr, "Failed to uninstall application roles"); + hr = CpiUsersInApplicationRolesUninstall(&usrInAppRoleList, rmRollback, &pwzRollbackActionData, NULL); + ExitOnFailure(hr, "Failed to uninstall users in application roles"); + hr = CpiAssembliesUninstall(&asmList, rmRollback, &pwzRollbackActionData, NULL); + ExitOnFailure(hr, "Failed to uninstall assemblies"); + hr = CpiRoleAssignmentsUninstall(&asmList, rmRollback, &pwzRollbackActionData, NULL); + ExitOnFailure(hr, "Failed to uninstall assemblies"); + hr = CpiSubscriptionsUninstall(&subList, rmRollback, &pwzRollbackActionData, NULL); + ExitOnFailure(hr, "Failed to uninstall subscriptions"); + + hr = WcaDoDeferredAction(CP_COMPLUSROLLBACKUNINSTALLEXECUTE, pwzRollbackActionData, 0); + ExitOnFailure(hr, "Failed to schedule ComPlusRollbackUninstallExecute"); + + // schedule install custom action + hr = WcaWriteStringToCaData(pwzRollbackFileName, &pwzActionData); + ExitOnFailure(hr, "Failed to add rollback file name to custom action data"); + + hr = CpiSubscriptionsUninstall(&subList, rmDeferred, &pwzActionData, &iProgress); + ExitOnFailure(hr, "Failed to uninstall subscriptions"); + hr = CpiRoleAssignmentsUninstall(&asmList, rmDeferred, &pwzActionData, &iProgress); + ExitOnFailure(hr, "Failed to uninstall assemblies"); + hr = CpiAssembliesUninstall(&asmList, rmDeferred, &pwzActionData, &iProgress); + ExitOnFailure(hr, "Failed to uninstall assemblies"); + hr = CpiUsersInApplicationRolesUninstall(&usrInAppRoleList, rmDeferred, &pwzActionData, &iProgress); + ExitOnFailure(hr, "Failed to uninstall users in application roles"); + hr = CpiApplicationRolesUninstall(&appRoleList, rmDeferred, &pwzActionData, &iProgress); + ExitOnFailure(hr, "Failed to uninstall application roles"); + hr = CpiApplicationsUninstall(&appList, rmDeferred, &pwzActionData, &iProgress); + ExitOnFailure(hr, "Failed to uninstall applications"); + hr = CpiPartitionUsersUninstall(&partUsrList, rmDeferred, &pwzActionData, &iProgress); + ExitOnFailure(hr, "Failed to uninstall partition users"); + hr = CpiUsersInPartitionRolesUninstall(&usrInPartRoleList, rmDeferred, &pwzActionData, &iProgress); + ExitOnFailure(hr, "Failed to uninstall users in partition roles"); + hr = CpiPartitionsUninstall(&partList, rmDeferred, &pwzActionData, &iProgress); + ExitOnFailure(hr, "Failed to uninstall partitions"); + + hr = WcaDoDeferredAction(CP_COMPLUSUNINSTALLEXECUTE, pwzActionData, iProgress); + ExitOnFailure(hr, "Failed to schedule ComPlusUninstallExecute"); + + // schedule commit custom action + hr = WcaDoDeferredAction(CP_COMPLUSINSTALLCOMMIT, pwzRollbackFileName, 0); + ExitOnFailure(hr, "Failed to schedule ComPlusInstallCommit"); + } + + hr = S_OK; + +LExit: + // clean up + ReleaseObject(piCatalog); + + ReleaseStr(pwzRollbackFileName); + ReleaseStr(pwzActionData); + ReleaseStr(pwzRollbackActionData); + + CpiPartitionListFree(&partList); + CpiPartitionRoleListFree(&partRoleList); + CpiUserInPartitionRoleListFree(&usrInPartRoleList); + CpiPartitionUserListFree(&partUsrList); + CpiApplicationListFree(&appList); + CpiApplicationRoleListFree(&appRoleList); + CpiUserInApplicationRoleListFree(&usrInAppRoleList); + CpiAssemblyListFree(&asmList); + CpiSubscriptionListFree(&subList); + + // unitialize + CpiFinalize(); + + if (fInitializedCom) + ::CoUninitialize(); + + er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE; + return WcaFinalize(er); +} diff --git a/src/ca/cpsubsexec.cpp b/src/ca/cpsubsexec.cpp new file mode 100644 index 00000000..bbcf9853 --- /dev/null +++ b/src/ca/cpsubsexec.cpp @@ -0,0 +1,411 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + + +// private structs + +struct CPI_SUBSCRIPTION_ATTRIBUTES +{ + int iActionType; + int iActionCost; + LPWSTR pwzKey; + LPWSTR pwzID; + LPWSTR pwzName; + LPWSTR pwzEventCLSID; + LPWSTR pwzPublisherID; + LPWSTR pwzCompCLSID; + LPWSTR pwzAppID; + LPWSTR pwzPartID; + CPI_PROPERTY* pPropList; +}; + + +// prototypes for private helper functions + +static HRESULT ReadSubscriptionAttributes( + LPWSTR* ppwzData, + CPI_SUBSCRIPTION_ATTRIBUTES* pAttrs + ); +static void FreeSubscriptionAttributes( + CPI_SUBSCRIPTION_ATTRIBUTES* pAttrs + ); +static HRESULT CreateSubscription( + CPI_SUBSCRIPTION_ATTRIBUTES* pAttrs + ); +static HRESULT RemoveSubscription( + CPI_SUBSCRIPTION_ATTRIBUTES* pAttrs + ); + + +// function definitions + +HRESULT CpiConfigureSubscriptions( + LPWSTR* ppwzData, + HANDLE hRollbackFile + ) +{ + HRESULT hr = S_OK; + + CPI_SUBSCRIPTION_ATTRIBUTES attrs; + ::ZeroMemory(&attrs, sizeof(attrs)); + + // read action text + hr = CpiActionStartMessage(ppwzData, FALSE); + ExitOnFailure(hr, "Failed to send action start message"); + + // ger count + int iCnt = 0; + hr = WcaReadIntegerFromCaData(ppwzData, &iCnt); + ExitOnFailure(hr, "Failed to read count"); + + // write count to rollback file + hr = CpiWriteIntegerToRollbackFile(hRollbackFile, iCnt); + ExitOnFailure(hr, "Failed to write count to rollback file"); + + for (int i = 0; i < iCnt; i++) + { + // read attributes from CustomActionData + hr = ReadSubscriptionAttributes(ppwzData, &attrs); + ExitOnFailure(hr, "Failed to read attributes"); + + // progress message + hr = CpiActionDataMessage(1, attrs.pwzName); + ExitOnFailure(hr, "Failed to send progress messages"); + + if (S_FALSE == hr) + ExitFunction(); + + // write key to rollback file + hr = CpiWriteKeyToRollbackFile(hRollbackFile, attrs.pwzKey); + ExitOnFailure(hr, "Failed to write key to rollback file"); + + // action + switch (attrs.iActionType) + { + case atCreate: + hr = CreateSubscription(&attrs); + ExitOnFailure(hr, "Failed to create subscription, key: %S", attrs.pwzKey); + break; + case atRemove: + hr = RemoveSubscription(&attrs); + ExitOnFailure(hr, "Failed to remove subscription, key: %S", attrs.pwzKey); + break; + } + + // write completion status to rollback file + hr = CpiWriteIntegerToRollbackFile(hRollbackFile, 1); + ExitOnFailure(hr, "Failed to write completion status to rollback file"); + + // progress + hr = WcaProgressMessage(attrs.iActionCost, FALSE); + ExitOnFailure(hr, "Failed to update progress"); + } + + hr = S_OK; + +LExit: + // clean up + FreeSubscriptionAttributes(&attrs); + + return hr; +} + +HRESULT CpiRollbackConfigureSubscriptions( + LPWSTR* ppwzData, + CPI_ROLLBACK_DATA* pRollbackDataList + ) +{ + HRESULT hr = S_OK; + + int iRollbackStatus; + + CPI_SUBSCRIPTION_ATTRIBUTES attrs; + ::ZeroMemory(&attrs, sizeof(attrs)); + + // read action text + hr = CpiActionStartMessage(ppwzData, NULL == pRollbackDataList); + ExitOnFailure(hr, "Failed to send action start message"); + + // ger count + int iCnt = 0; + hr = WcaReadIntegerFromCaData(ppwzData, &iCnt); + ExitOnFailure(hr, "Failed to read count"); + + for (int i = 0; i < iCnt; i++) + { + // read attributes from CustomActionData + hr = ReadSubscriptionAttributes(ppwzData, &attrs); + ExitOnFailure(hr, "Failed to read attributes"); + + // rollback status + hr = CpiFindRollbackStatus(pRollbackDataList, attrs.pwzKey, &iRollbackStatus); + + if (S_FALSE == hr) + continue; // not found, nothing to rollback + + // progress message + hr = CpiActionDataMessage(1, attrs.pwzName); + ExitOnFailure(hr, "Failed to send progress messages"); + + if (S_FALSE == hr) + ExitFunction(); + + // action + switch (attrs.iActionType) + { + case atCreate: + hr = CreateSubscription(&attrs); + if (FAILED(hr)) + WcaLog(LOGMSG_STANDARD, "Failed to create subscription, hr: 0x%x, key: %S", hr, attrs.pwzKey); + break; + case atRemove: + hr = RemoveSubscription(&attrs); + if (FAILED(hr)) + WcaLog(LOGMSG_STANDARD, "Failed to remove subscription, hr: 0x%x, key: %S", hr, attrs.pwzKey); + break; + } + + // check rollback status + if (0 == iRollbackStatus) + continue; // operation did not complete, skip progress + + // progress + hr = WcaProgressMessage(attrs.iActionCost, FALSE); + ExitOnFailure(hr, "Failed to update progress"); + } + + hr = S_OK; + +LExit: + // clean up + FreeSubscriptionAttributes(&attrs); + + return hr; +} + + +// helper function definitions + +static HRESULT ReadSubscriptionAttributes( + LPWSTR* ppwzData, + CPI_SUBSCRIPTION_ATTRIBUTES* pAttrs + ) +{ + HRESULT hr = S_OK; + + hr = WcaReadIntegerFromCaData(ppwzData, &pAttrs->iActionType); + ExitOnFailure(hr, "Failed to read action type"); + hr = WcaReadIntegerFromCaData(ppwzData, &pAttrs->iActionCost); + ExitOnFailure(hr, "Failed to read action cost"); + hr = WcaReadStringFromCaData(ppwzData, &pAttrs->pwzKey); + ExitOnFailure(hr, "Failed to read key"); + hr = WcaReadStringFromCaData(ppwzData, &pAttrs->pwzID); + ExitOnFailure(hr, "Failed to read id"); + hr = WcaReadStringFromCaData(ppwzData, &pAttrs->pwzName); + ExitOnFailure(hr, "Failed to read name"); + hr = WcaReadStringFromCaData(ppwzData, &pAttrs->pwzEventCLSID); + ExitOnFailure(hr, "Failed to read event clsid"); + hr = WcaReadStringFromCaData(ppwzData, &pAttrs->pwzPublisherID); + ExitOnFailure(hr, "Failed to read publisher id"); + hr = WcaReadStringFromCaData(ppwzData, &pAttrs->pwzCompCLSID); + ExitOnFailure(hr, "Failed to read component clsid"); + hr = WcaReadStringFromCaData(ppwzData, &pAttrs->pwzAppID); + ExitOnFailure(hr, "Failed to read application id"); + hr = WcaReadStringFromCaData(ppwzData, &pAttrs->pwzPartID); + ExitOnFailure(hr, "Failed to read partition id"); + + hr = CpiReadPropertyList(ppwzData, &pAttrs->pPropList); + ExitOnFailure(hr, "Failed to read properties"); + + hr = S_OK; + +LExit: + return hr; +} + +static void FreeSubscriptionAttributes( + CPI_SUBSCRIPTION_ATTRIBUTES* pAttrs + ) +{ + ReleaseStr(pAttrs->pwzKey); + ReleaseStr(pAttrs->pwzID); + ReleaseStr(pAttrs->pwzName); + ReleaseStr(pAttrs->pwzEventCLSID); + ReleaseStr(pAttrs->pwzPublisherID); + ReleaseStr(pAttrs->pwzCompCLSID); + ReleaseStr(pAttrs->pwzAppID); + ReleaseStr(pAttrs->pwzPartID); + + if (pAttrs->pPropList) + CpiFreePropertyList(pAttrs->pPropList); +} + +static HRESULT CreateSubscription( + CPI_SUBSCRIPTION_ATTRIBUTES* pAttrs + ) +{ + HRESULT hr = S_OK; + UINT er = ERROR_SUCCESS; + + ICatalogCollection* piSubsColl = NULL; + ICatalogObject* piSubsObj = NULL; + + PSID pSid = NULL; + long lChanges = 0; + + // log + WcaLog(LOGMSG_VERBOSE, "Creating subscription, key: %S", pAttrs->pwzKey); + + // get subscriptions collection + hr = CpiGetSubscriptionsCollection(pAttrs->pwzPartID, pAttrs->pwzAppID, pAttrs->pwzCompCLSID, &piSubsColl); + if (S_FALSE == hr) + hr = HRESULT_FROM_WIN32(ERROR_NOT_FOUND); + ExitOnFailure(hr, "Failed to get subscriptions collection"); + + // check if subscription exists + hr = CpiFindCollectionObjectByStringKey(piSubsColl, pAttrs->pwzID, &piSubsObj); + ExitOnFailure(hr, "Failed to find subscription"); + + if (S_FALSE == hr) + { + // create subscription + hr = CpiAddCollectionObject(piSubsColl, &piSubsObj); + ExitOnFailure(hr, "Failed to add subscription to collection"); + + hr = CpiPutCollectionObjectValue(piSubsObj, L"ID", pAttrs->pwzID); + ExitOnFailure(hr, "Failed to set subscription id property"); + + hr = CpiPutCollectionObjectValue(piSubsObj, L"Name", pAttrs->pwzName); + ExitOnFailure(hr, "Failed to set subscription name property"); + + if (pAttrs->pwzEventCLSID && *pAttrs->pwzEventCLSID) + { + hr = CpiPutCollectionObjectValue(piSubsObj, L"EventCLSID", pAttrs->pwzEventCLSID); + ExitOnFailure(hr, "Failed to set role event clsid property"); + } + + if (pAttrs->pwzPublisherID && *pAttrs->pwzPublisherID) + { + hr = CpiPutCollectionObjectValue(piSubsObj, L"PublisherID", pAttrs->pwzPublisherID); + ExitOnFailure(hr, "Failed to set role publisher id property"); + } + } + + // properties + for (CPI_PROPERTY* pItm = pAttrs->pPropList; pItm; pItm = pItm->pNext) + { + // UserName property + if (0 == lstrcmpW(pItm->wzName, L"UserName")) + { + // get SID for account + do { + er = ERROR_SUCCESS; + hr = CpiAccountNameToSid(pItm->pwzValue, &pSid); + if (!::MsiGetMode(WcaGetInstallHandle(), MSIRUNMODE_ROLLBACK)) + { + if (HRESULT_FROM_WIN32(ERROR_NONE_MAPPED) == hr) + { + WcaLog(LOGMSG_STANDARD, "Failed to lookup account name, hr: 0x%x, account: '%S'", hr, pItm->pwzValue); + er = WcaErrorMessage(msierrComPlusFailedLookupNames, hr, INSTALLMESSAGE_ERROR | MB_ABORTRETRYIGNORE, 0); + switch (er) + { + case IDABORT: + ExitFunction(); // exit with error code from CpiAccountNameToSid() + case IDRETRY: + break; + case IDIGNORE: + default: + hr = S_FALSE; + } + } + else + ExitOnFailure(hr, "Failed to get SID for account, account: '%S'", pItm->pwzValue); + } + else if (FAILED(hr)) + { + WcaLog(LOGMSG_STANDARD, "Failed to get SID for account, hr: 0x%x, account: '%S'", hr, pItm->pwzValue); + hr = S_FALSE; + } + } while (IDRETRY == er); + + if (S_FALSE == hr) + continue; + + // convert SID back to account name + hr = CpiSidToAccountName(pSid, &pItm->pwzValue); + ExitOnFailure(hr, "Failed to convert SID to account name"); + } + + // set property + hr = CpiPutCollectionObjectValue(piSubsObj, pItm->wzName, pItm->pwzValue); + ExitOnFailure(hr, "Failed to set object property value, name: %S", pItm->wzName); + } + + // save changes + hr = piSubsColl->SaveChanges(&lChanges); + if (COMADMIN_E_OBJECTERRORS == hr) + CpiLogCatalogErrorInfo(); + ExitOnFailure(hr, "Failed to save changes"); + + // log + WcaLog(LOGMSG_VERBOSE, "%d changes saved to catalog, key: %S", lChanges, pAttrs->pwzKey); + + hr = S_OK; + +LExit: + // clean up + ReleaseObject(piSubsColl); + ReleaseObject(piSubsObj); + + if (pSid) + ::HeapFree(::GetProcessHeap(), 0, pSid); + + return hr; +} + +static HRESULT RemoveSubscription( + CPI_SUBSCRIPTION_ATTRIBUTES* pAttrs + ) +{ + HRESULT hr = S_OK; + + ICatalogCollection* piSubsColl = NULL; + + long lChanges = 0; + + // log + WcaLog(LOGMSG_VERBOSE, "Removing subscription, key: %S", pAttrs->pwzKey); + + // get subscriptions collection + hr = CpiGetSubscriptionsCollection(pAttrs->pwzPartID, pAttrs->pwzAppID, pAttrs->pwzCompCLSID, &piSubsColl); + ExitOnFailure(hr, "Failed to get subscriptions collection"); + + if (S_FALSE == hr) + { + // subscription not found + WcaLog(LOGMSG_VERBOSE, "Unable to retrieve subscriptions collection, nothing to delete, key: %S", pAttrs->pwzKey); + ExitFunction1(hr = S_OK); + } + + // remove + hr = CpiRemoveCollectionObject(piSubsColl, pAttrs->pwzID, NULL, FALSE); + ExitOnFailure(hr, "Failed to remove subscriptions"); + + // save changes + hr = piSubsColl->SaveChanges(&lChanges); + if (COMADMIN_E_OBJECTERRORS == hr) + CpiLogCatalogErrorInfo(); + ExitOnFailure(hr, "Failed to save changes"); + + // log + WcaLog(LOGMSG_VERBOSE, "%d changes saved to catalog, key: %S", lChanges, pAttrs->pwzKey); + + hr = S_OK; + +LExit: + // clean up + ReleaseObject(piSubsColl); + + return hr; +} diff --git a/src/ca/cpsubsexec.h b/src/ca/cpsubsexec.h new file mode 100644 index 00000000..2f4d3c75 --- /dev/null +++ b/src/ca/cpsubsexec.h @@ -0,0 +1,12 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + + +HRESULT CpiConfigureSubscriptions( + LPWSTR* ppwzData, + HANDLE hRollbackFile + ); +HRESULT CpiRollbackConfigureSubscriptions( + LPWSTR* ppwzData, + CPI_ROLLBACK_DATA* pRollbackDataList + ); diff --git a/src/ca/cpsubssched.cpp b/src/ca/cpsubssched.cpp new file mode 100644 index 00000000..73fd4f6d --- /dev/null +++ b/src/ca/cpsubssched.cpp @@ -0,0 +1,606 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + + +// sql queries + +LPCWSTR vcsSubscriptionQuery = + L"SELECT `Subscription`, `ComPlusComponent_`, `Component_`, `Id`, `Name`, `EventCLSID`, `PublisherID` FROM `ComPlusSubscription`"; +enum eSubscriptionQuery { sqSubscription = 1, sqComPlusComponent, sqComponent, sqID, sqName, sqEventCLSID, sqPublisherID }; + +LPCWSTR vcsSubscriptionPropertyQuery = + L"SELECT `Name`, `Value` FROM `ComPlusSubscriptionProperty` WHERE `Subscription_` = ?"; + + +// property definitions + +CPI_PROPERTY_DEFINITION pdlSubscriptionProperties[] = +{ + {L"Description", cpptString, 500}, + {L"Enabled", cpptBoolean, 500}, + {L"EventClassPartitionID", cpptString, 502}, + {L"FilterCriteria", cpptString, 500}, + {L"InterfaceID", cpptString, 500}, + {L"MachineName", cpptString, 500}, + {L"MethodName", cpptString, 500}, + {L"PerUser", cpptBoolean, 500}, + {L"Queued", cpptBoolean, 500}, + {L"SubscriberMoniker", cpptString, 500}, + {L"UserName", cpptUser, 500}, + {NULL, cpptNone, 0} +}; + + +// prototypes for private helper functions + +static void FreeSubscription( + CPI_SUBSCRIPTION* pItm + ); +static HRESULT FindObjectForSubscription( + CPI_SUBSCRIPTION* pItm, + BOOL fFindId, + BOOL fFindName, + ICatalogObject** ppiSubsObj + ); +static HRESULT AddSubscriptionToActionData( + CPI_SUBSCRIPTION* pItm, + int iActionType, + int iActionCost, + LPWSTR* ppwzActionData + ); +static HRESULT ComponentFindByKey( + CPI_ASSEMBLY_LIST* pAsmList, + LPCWSTR pwzKey, + CPI_ASSEMBLY** ppAsmItm, + CPI_COMPONENT** ppCompItm + ); + + +// function definitions + +void CpiSubscriptionListFree( + CPI_SUBSCRIPTION_LIST* pList + ) +{ + CPI_SUBSCRIPTION* pItm = pList->pFirst; + + while (pItm) + { + CPI_SUBSCRIPTION* pDelete = pItm; + pItm = pItm->pNext; + FreeSubscription(pDelete); + } +} + +HRESULT CpiSubscriptionsRead( + CPI_ASSEMBLY_LIST* pAsmList, + CPI_SUBSCRIPTION_LIST* pSubList + ) +{ + HRESULT hr = S_OK; + UINT er = ERROR_SUCCESS; + + PMSIHANDLE hView, hRec; + + CPI_SUBSCRIPTION* pItm = NULL; + LPWSTR pwzData = NULL; + BOOL fMatchingArchitecture = FALSE; + + // loop through all applications + hr = WcaOpenExecuteView(vcsSubscriptionQuery, &hView); + ExitOnFailure(hr, "Failed to execute view on ComPlusSubscription table"); + + while (S_OK == (hr = WcaFetchRecord(hView, &hRec))) + { + // get component + hr = WcaGetRecordString(hRec, sqComponent, &pwzData); + ExitOnFailure(hr, "Failed to get component"); + + // check if the component is our processor architecture + hr = CpiVerifyComponentArchitecure(pwzData, &fMatchingArchitecture); + ExitOnFailure(hr, "Failed to get component architecture."); + + if (!fMatchingArchitecture) + { + continue; // not the same architecture, ignore + } + + // create entry + pItm = (CPI_SUBSCRIPTION*)::HeapAlloc(::GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(CPI_SUBSCRIPTION)); + if (!pItm) + ExitFunction1(hr = E_OUTOFMEMORY); + + // get component install state + er = ::MsiGetComponentStateW(WcaGetInstallHandle(), pwzData, &pItm->isInstalled, &pItm->isAction); + ExitOnFailure(hr = HRESULT_FROM_WIN32(er), "Failed to get component state"); + + // get key + hr = WcaGetRecordString(hRec, sqSubscription, &pwzData); + ExitOnFailure(hr, "Failed to get key"); + StringCchCopyW(pItm->wzKey, countof(pItm->wzKey), pwzData); + + // get com+ component + hr = WcaGetRecordString(hRec, sqComPlusComponent, &pwzData); + ExitOnFailure(hr, "Failed to get COM+ component"); + + hr = ComponentFindByKey(pAsmList, pwzData, &pItm->pAssembly, &pItm->pComponent); + + if (S_FALSE == hr) + { + // component not found + ExitOnFailure(hr = E_FAIL, "Failed to find component, key: %S", pwzData); + } + + // get id + hr = WcaGetRecordFormattedString(hRec, sqID, &pwzData); + ExitOnFailure(hr, "Failed to get id"); + + if (pwzData && *pwzData) + { + hr = PcaGuidToRegFormat(pwzData, pItm->wzID, countof(pItm->wzID)); + ExitOnFailure(hr, "Failed to parse id guid value, key: %S, value: '%S'", pItm->wzKey, pwzData); + } + + // get name + hr = WcaGetRecordFormattedString(hRec, sqName, &pwzData); + ExitOnFailure(hr, "Failed to get name"); + StringCchCopyW(pItm->wzName, countof(pItm->wzName), pwzData); + + // get event clsid + hr = WcaGetRecordFormattedString(hRec, sqEventCLSID, &pwzData); + ExitOnFailure(hr, "Failed to get event clsid"); + StringCchCopyW(pItm->wzEventCLSID, countof(pItm->wzEventCLSID), pwzData); + + // get publisher id + hr = WcaGetRecordFormattedString(hRec, sqPublisherID, &pwzData); + ExitOnFailure(hr, "Failed to get publisher id"); + StringCchCopyW(pItm->wzPublisherID, countof(pItm->wzPublisherID), pwzData); + + // get properties + if (CpiTableExists(cptComPlusSubscriptionProperty)) + { + hr = CpiPropertiesRead(vcsSubscriptionPropertyQuery, pItm->wzKey, pdlSubscriptionProperties, &pItm->pProperties, &pItm->iPropertyCount); + ExitOnFailure(hr, "Failed to get subscription properties"); + } + + // set references & increment counters + if (WcaIsInstalling(pItm->isInstalled, pItm->isAction)) + { + CpiApplicationAddReferenceInstall(pItm->pAssembly->pApplication); + pItm->pAssembly->fReferencedForInstall = TRUE; + pSubList->iInstallCount++; + if (pItm->pAssembly->iAttributes & aaRunInCommit) + pSubList->iCommitCount++; + } + if (WcaIsUninstalling(pItm->isInstalled, pItm->isAction)) + { + CpiApplicationAddReferenceUninstall(pItm->pAssembly->pApplication); + pItm->pAssembly->fReferencedForUninstall = TRUE; + pSubList->iUninstallCount++; + } + + // add entry + if (pSubList->pFirst) + pItm->pNext = pSubList->pFirst; + pSubList->pFirst = pItm; + pItm = NULL; + } + + if (E_NOMOREITEMS == hr) + hr = S_OK; + +LExit: + // clean up + if (pItm) + FreeSubscription(pItm); + + ReleaseStr(pwzData); + + return hr; +} + +HRESULT CpiSubscriptionsVerifyInstall( + CPI_SUBSCRIPTION_LIST* pList + ) +{ + HRESULT hr = S_OK; + UINT er = ERROR_SUCCESS; + + ICatalogObject* piSubsObj = NULL; + + for (CPI_SUBSCRIPTION* pItm = pList->pFirst; pItm; pItm = pItm->pNext) + { + // subscriptions that are being installed + if (!WcaIsInstalling(pItm->isInstalled, pItm->isAction)) + continue; + + // subscription is supposed to exist + if (CpiIsInstalled(pItm->isInstalled)) + { + // if we don't have an id + if (!*pItm->wzID) + { + // find subscriptions with conflicting name + hr = FindObjectForSubscription(pItm, FALSE, TRUE, &piSubsObj); + ExitOnFailure(hr, "Failed to find collection object for subscription"); + + // if the subscription was found + if (S_OK == hr) + { + // get id from subscription object + hr = CpiGetKeyForObject(piSubsObj, pItm->wzID, countof(pItm->wzID)); + ExitOnFailure(hr, "Failed to get id"); + } + + // if the subscription was not found + else + { + // create a new id + hr = CpiCreateId(pItm->wzID, countof(pItm->wzID)); + ExitOnFailure(hr, "Failed to create id"); + } + } + } + + // subscription is supposed to be created + else + { + // check for conflicts + do { + if (*pItm->wzID) + { + // find subscriptions with conflicting id + hr = FindObjectForSubscription(pItm, TRUE, FALSE, &piSubsObj); + ExitOnFailure(hr, "Failed to find collection object for subscription"); + + if (S_FALSE == hr) + { + // find subscriptions with conflicting name + hr = FindObjectForSubscription(pItm, FALSE, TRUE, &piSubsObj); + ExitOnFailure(hr, "Failed to find collection object for subscription"); + + if (S_OK == hr) + // "A subscription with a conflictiong name exists. retry cancel" + er = WcaErrorMessage(msierrComPlusSubscriptionNameConflict, hr, INSTALLMESSAGE_ERROR | MB_RETRYCANCEL, 0); + else + break; // no conflicting entry found, break loop + } + else + // "A subscription with a conflicting id exists. abort retry ignore" + er = WcaErrorMessage(msierrComPlusSubscriptionIdConflict, hr, INSTALLMESSAGE_ERROR | MB_ABORTRETRYIGNORE, 0); + } + else + { + // find subscriptions with conflicting name + hr = FindObjectForSubscription(pItm, FALSE, TRUE, &piSubsObj); + ExitOnFailure(hr, "Failed to find collection object for subscription"); + + if (S_OK == hr) + // "A subscription with a conflictiong name exists. abort retry ignore" + er = WcaErrorMessage(msierrComPlusSubscriptionNameConflict, hr, INSTALLMESSAGE_ERROR | MB_ABORTRETRYIGNORE, 0); + else + break; // no conflicting entry found, break loop + } + + switch (er) + { + case IDCANCEL: + case IDABORT: + ExitOnFailure(hr = E_FAIL, "A subscription with a conflictiong name or id exists, key: %S", pItm->wzKey); + break; + case IDRETRY: + break; + case IDIGNORE: + default: + // if we don't have an id, copy id from object + if (!*pItm->wzID) + { + hr = CpiGetKeyForObject(piSubsObj, pItm->wzID, countof(pItm->wzID)); + ExitOnFailure(hr, "Failed to get id"); + } + hr = S_FALSE; // indicate that this is not a conflict + } + } while (S_OK == hr); // hr = S_FALSE if we don't have any conflicts + + // create a new id if one is missing + if (!*pItm->wzID) + { + hr = CpiCreateId(pItm->wzID, countof(pItm->wzID)); + ExitOnFailure(hr, "Failed to create id"); + } + } + + // clean up + ReleaseNullObject(piSubsObj); + } + + hr = S_OK; + +LExit: + // clean up + ReleaseObject(piSubsObj); + + return hr; +} + +HRESULT CpiSubscriptionsVerifyUninstall( + CPI_SUBSCRIPTION_LIST* pList + ) +{ + HRESULT hr = S_OK; + ICatalogObject* piSubsObj = NULL; + + for (CPI_SUBSCRIPTION* pItm = pList->pFirst; pItm; pItm = pItm->pNext) + { + // subscriptions that are being installed + if (!WcaIsUninstalling(pItm->isInstalled, pItm->isAction)) + continue; + + // find subscriptions with conflicting name + hr = FindObjectForSubscription(pItm, 0 != *pItm->wzID, 0 == *pItm->wzID, &piSubsObj); + ExitOnFailure(hr, "Failed to find collection object for subscription"); + + // if the subscription was found + if (S_OK == hr) + { + // if we don't have an id, copy id from object + if (!*pItm->wzID) + { + hr = CpiGetKeyForObject(piSubsObj, pItm->wzID, countof(pItm->wzID)); + ExitOnFailure(hr, "Failed to get id"); + } + } + + // if the subscription was not found + else + { + pItm->fObjectNotFound = TRUE; + pList->iUninstallCount--; // elements with the fObjectNotFound flag set will not be scheduled for uninstall + } + + // clean up + ReleaseNullObject(piSubsObj); + } + + hr = S_OK; + +LExit: + // clean up + ReleaseObject(piSubsObj); + + return hr; +} + +HRESULT CpiSubscriptionsInstall( + CPI_SUBSCRIPTION_LIST* pList, + int iRunMode, + LPWSTR* ppwzActionData, + int* piProgress + ) +{ + HRESULT hr = S_OK; + + int iActionType; + int iCount = 0; + + // add action text + hr = CpiAddActionTextToActionData(L"CreateSubscrComPlusComponents", ppwzActionData); + ExitOnFailure(hr, "Failed to add action text to custom action data"); + + // subscription count + switch (iRunMode) + { + case rmDeferred: + iCount = pList->iInstallCount - pList->iCommitCount; + break; + case rmCommit: + iCount = pList->iCommitCount; + break; + case rmRollback: + iCount = pList->iInstallCount; + break; + } + + // add subscription count to action data + hr = WcaWriteIntegerToCaData(iCount, ppwzActionData); + ExitOnFailure(hr, "Failed to add count to custom action data"); + + // add assemblies to custom action data in forward order + for (CPI_SUBSCRIPTION* pItm = pList->pFirst; pItm; pItm = pItm->pNext) + { + // roles that are being installed only + if ((rmCommit == iRunMode && !(pItm->pAssembly->iAttributes & aaRunInCommit)) || + (rmDeferred == iRunMode && (pItm->pAssembly->iAttributes & aaRunInCommit)) || + !WcaIsInstalling(pItm->isInstalled, pItm->isAction)) + continue; + + // action type + if (rmRollback == iRunMode) + { + if (CpiIsInstalled(pItm->isInstalled)) + iActionType = atNoOp; + else + iActionType = atRemove; + } + else + iActionType = atCreate; + + // add to action data + hr = AddSubscriptionToActionData(pItm, iActionType, COST_SUBSCRIPTION_CREATE, ppwzActionData); + ExitOnFailure(hr, "Failed to add subscription to custom action data, key: %S", pItm->wzKey); + } + + // add progress tics + if (piProgress) + *piProgress += COST_SUBSCRIPTION_CREATE * pList->iInstallCount; + + hr = S_OK; + +LExit: + return hr; +} + +HRESULT CpiSubscriptionsUninstall( + CPI_SUBSCRIPTION_LIST* pList, + int iRunMode, + LPWSTR* ppwzActionData, + int* piProgress + ) +{ + HRESULT hr = S_OK; + + int iActionType; + + // add action text + hr = CpiAddActionTextToActionData(L"RemoveSubscrComPlusComponents", ppwzActionData); + ExitOnFailure(hr, "Failed to add action text to custom action data"); + + // add subscription count to action data + hr = WcaWriteIntegerToCaData(pList->iUninstallCount, ppwzActionData); + ExitOnFailure(hr, "Failed to add count to custom action data"); + + // add assemblies to custom action data in reverse order + for (CPI_SUBSCRIPTION* pItm = pList->pFirst; pItm; pItm = pItm->pNext) + { + // roles that are being uninstalled only + if (pItm->fObjectNotFound || !WcaIsUninstalling(pItm->isInstalled, pItm->isAction)) + continue; + + // action type + if (rmRollback == iRunMode) + iActionType = atCreate; + else + iActionType = atRemove; + + // add to action data + hr = AddSubscriptionToActionData(pItm, iActionType, COST_SUBSCRIPTION_DELETE, ppwzActionData); + ExitOnFailure(hr, "Failed to add subscription to custom action data, key: %S", pItm->wzKey); + } + + // add progress tics + if (piProgress) + *piProgress += COST_SUBSCRIPTION_DELETE * pList->iUninstallCount; + + hr = S_OK; + +LExit: + return hr; +} + + +// helper function definitions + +static void FreeSubscription( + CPI_SUBSCRIPTION* pItm + ) +{ + if (pItm->pProperties) + CpiPropertiesFreeList(pItm->pProperties); + + ::HeapFree(::GetProcessHeap(), 0, pItm); +} + +static HRESULT FindObjectForSubscription( + CPI_SUBSCRIPTION* pItm, + BOOL fFindId, + BOOL fFindName, + ICatalogObject** ppiSubsObj + ) +{ + HRESULT hr = S_OK; + + ICatalogCollection* piSubsColl = NULL; + + // get applications collection + hr = CpiGetSubscriptionsCollForComponent(pItm->pAssembly, pItm->pComponent, &piSubsColl); + ExitOnFailure(hr, "Failed to get collection"); + + if (S_FALSE == hr) + ExitFunction(); // exit with hr = S_FALSE + + // find application object + hr = CpiFindCollectionObject(piSubsColl, fFindId ? pItm->wzID : NULL, fFindName ? pItm->wzName : NULL, ppiSubsObj); + ExitOnFailure(hr, "Failed to find object"); + + // exit with hr from CpiFindCollectionObject() + +LExit: + // clean up + ReleaseObject(piSubsColl); + + return hr; +} + +static HRESULT AddSubscriptionToActionData( + CPI_SUBSCRIPTION* pItm, + int iActionType, + int iActionCost, + LPWSTR* ppwzActionData + ) +{ + HRESULT hr = S_OK; + + // add action information to custom action data + hr = WcaWriteIntegerToCaData(iActionType, ppwzActionData); + ExitOnFailure(hr, "Failed to add action type to custom action data"); + hr = WcaWriteIntegerToCaData(iActionCost, ppwzActionData); + ExitOnFailure(hr, "Failed to add action cost to custom action data"); + + // add application role information to custom action data + hr = WcaWriteStringToCaData(pItm->wzKey, ppwzActionData); + ExitOnFailure(hr, "Failed to add subscription key to custom action data"); + hr = WcaWriteStringToCaData(pItm->wzID, ppwzActionData); + ExitOnFailure(hr, "Failed to add subscription id to custom action data"); + hr = WcaWriteStringToCaData(pItm->wzName, ppwzActionData); + ExitOnFailure(hr, "Failed to add subscription name to custom action data"); + hr = WcaWriteStringToCaData(atCreate == iActionType ? pItm->wzEventCLSID : L"", ppwzActionData); + ExitOnFailure(hr, "Failed to add assembly tlb path to custom action data"); + hr = WcaWriteStringToCaData(atCreate == iActionType ? pItm->wzPublisherID : L"", ppwzActionData); + ExitOnFailure(hr, "Failed to add assembly proxy-stub dll path to custom action data"); + + // add component information to custom action data + hr = WcaWriteStringToCaData(pItm->pComponent->wzCLSID, ppwzActionData); + ExitOnFailure(hr, "Failed to add application id to custom action data"); + + // add application information to custom action data + hr = WcaWriteStringToCaData(pItm->pAssembly->pApplication->wzID, ppwzActionData); + ExitOnFailure(hr, "Failed to add application id to custom action data"); + + // add partition information to custom action data + LPCWSTR pwzPartID = pItm->pAssembly->pApplication->pPartition ? pItm->pAssembly->pApplication->pPartition->wzID : L""; + hr = WcaWriteStringToCaData(pwzPartID, ppwzActionData); + ExitOnFailure(hr, "Failed to add partition id to custom action data"); + + // add properties to custom action data + hr = CpiAddPropertiesToActionData(atCreate == iActionType ? pItm->iPropertyCount : 0, pItm->pProperties, ppwzActionData); + ExitOnFailure(hr, "Failed to add properties to custom action data"); + + hr = S_OK; + +LExit: + return hr; +} + +static HRESULT ComponentFindByKey( + CPI_ASSEMBLY_LIST* pAsmList, + LPCWSTR pwzKey, + CPI_ASSEMBLY** ppAsmItm, + CPI_COMPONENT** ppCompItm + ) +{ + for (CPI_ASSEMBLY* pAsmItm = pAsmList->pFirst; pAsmItm; pAsmItm = pAsmItm->pNext) + { + for (CPI_COMPONENT* pCompItm = pAsmItm->pComponents; pCompItm; pCompItm = pCompItm->pNext) + { + if (0 == lstrcmpW(pCompItm->wzKey, pwzKey)) + { + *ppAsmItm = pAsmItm; + *ppCompItm = pCompItm; + return S_OK; + } + } + } + + return S_FALSE; +} diff --git a/src/ca/cpsubssched.h b/src/ca/cpsubssched.h new file mode 100644 index 00000000..3fc18478 --- /dev/null +++ b/src/ca/cpsubssched.h @@ -0,0 +1,62 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + + +struct CPI_SUBSCRIPTION +{ + WCHAR wzKey[MAX_DARWIN_KEY + 1]; + WCHAR wzID[CPI_MAX_GUID + 1]; + WCHAR wzName[MAX_DARWIN_COLUMN + 1]; + WCHAR wzEventCLSID[CPI_MAX_GUID + 1]; + WCHAR wzPublisherID[CPI_MAX_GUID + 1]; + + BOOL fObjectNotFound; + + int iPropertyCount; + CPI_PROPERTY* pProperties; + + INSTALLSTATE isInstalled, isAction; + + CPI_ASSEMBLY* pAssembly; + CPI_COMPONENT* pComponent; + + CPI_SUBSCRIPTION* pNext; +}; + +struct CPI_SUBSCRIPTION_LIST +{ + CPI_SUBSCRIPTION* pFirst; + + int iInstallCount; + int iCommitCount; + int iUninstallCount; +}; + + +// function prototypes + +void CpiSubscriptionListFree( + CPI_SUBSCRIPTION_LIST* pList + ); +HRESULT CpiSubscriptionsRead( + CPI_ASSEMBLY_LIST* pAsmList, + CPI_SUBSCRIPTION_LIST* pSubList + ); +HRESULT CpiSubscriptionsVerifyInstall( + CPI_SUBSCRIPTION_LIST* pList + ); +HRESULT CpiSubscriptionsVerifyUninstall( + CPI_SUBSCRIPTION_LIST* pList + ); +HRESULT CpiSubscriptionsInstall( + CPI_SUBSCRIPTION_LIST* pList, + int iRunMode, + LPWSTR* ppwzActionData, + int* piProgress + ); +HRESULT CpiSubscriptionsUninstall( + CPI_SUBSCRIPTION_LIST* pList, + int iRunMode, + LPWSTR* ppwzActionData, + int* piProgress + ); diff --git a/src/ca/cputilexec.cpp b/src/ca/cputilexec.cpp new file mode 100644 index 00000000..e081678b --- /dev/null +++ b/src/ca/cputilexec.cpp @@ -0,0 +1,1881 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + + +// private structs + +struct CPI_WELLKNOWN_SID +{ + LPCWSTR pwzName; + SID_IDENTIFIER_AUTHORITY iaIdentifierAuthority; + BYTE nSubAuthorityCount; + DWORD dwSubAuthority[8]; +}; + + +// well known SIDs + +CPI_WELLKNOWN_SID wsWellKnownSids[] = { + {L"\\Everyone", SECURITY_WORLD_SID_AUTHORITY, 1, {SECURITY_WORLD_RID, 0, 0, 0, 0, 0, 0, 0}}, + {L"\\Administrators", SECURITY_NT_AUTHORITY, 2, {SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_ADMINS, 0, 0, 0, 0, 0, 0}}, + {L"\\LocalSystem", SECURITY_NT_AUTHORITY, 1, {SECURITY_LOCAL_SYSTEM_RID, 0, 0, 0, 0, 0, 0, 0}}, + {L"\\LocalService", SECURITY_NT_AUTHORITY, 1, {SECURITY_LOCAL_SERVICE_RID, 0, 0, 0, 0, 0, 0, 0}}, + {L"\\NetworkService", SECURITY_NT_AUTHORITY, 1, {SECURITY_NETWORK_SERVICE_RID, 0, 0, 0, 0, 0, 0, 0}}, + {L"\\AuthenticatedUser", SECURITY_NT_AUTHORITY, 1, {SECURITY_AUTHENTICATED_USER_RID, 0, 0, 0, 0, 0, 0, 0}}, + {L"\\Guests", SECURITY_NT_AUTHORITY, 2, {SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_GUESTS, 0, 0, 0, 0, 0, 0}}, + {L"\\Users", SECURITY_NT_AUTHORITY, 2, {SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_USERS, 0, 0, 0, 0, 0, 0}}, + {L"\\CREATOR OWNER", SECURITY_NT_AUTHORITY, 1, {SECURITY_CREATOR_OWNER_RID, 0, 0, 0, 0, 0, 0, 0}}, + {NULL, SECURITY_NULL_SID_AUTHORITY, 0, {0, 0, 0, 0, 0, 0, 0, 0}} +}; + + +// prototypes for private helper functions + +static HRESULT FindUserCollectionObjectIndex( + ICatalogCollection* piColl, + PSID pSid, + int* pi + ); +static HRESULT CreateSidFromDomainRidPair( + PSID pDomainSid, + DWORD dwRid, + PSID* ppSid + ); +static HRESULT InitLsaUnicodeString( + PLSA_UNICODE_STRING plusStr, + LPCWSTR pwzStr, + DWORD dwLen + ); +static void FreeLsaUnicodeString( + PLSA_UNICODE_STRING plusStr + ); +static HRESULT WriteFileAll( + HANDLE hFile, + PBYTE pbBuffer, + DWORD dwBufferLength + ); +static HRESULT ReadFileAll( + HANDLE hFile, + PBYTE pbBuffer, + DWORD dwBufferLength + ); + + +// variables + +static ICOMAdminCatalog* gpiCatalog; + + +// function definitions + +void CpiInitialize() +{ + // collections + gpiCatalog = NULL; +} + +void CpiFinalize() +{ + // collections + ReleaseObject(gpiCatalog); +} + +HRESULT CpiActionStartMessage( + LPWSTR* ppwzActionData, + BOOL fSuppress + ) +{ + HRESULT hr = S_OK; + UINT er = ERROR_SUCCESS; + + PMSIHANDLE hRec; + + LPWSTR pwzData = NULL; + + // create record + hRec = ::MsiCreateRecord(3); + ExitOnNull(hRec, hr, E_OUTOFMEMORY, "Failed to create record"); + + // action name + hr = WcaReadStringFromCaData(ppwzActionData, &pwzData); + ExitOnFailure(hr, "Failed to action name"); + + er = ::MsiRecordSetStringW(hRec, 1, pwzData); + ExitOnFailure(hr = HRESULT_FROM_WIN32(er), "Failed to set action name"); + + // description + hr = WcaReadStringFromCaData(ppwzActionData, &pwzData); + ExitOnFailure(hr, "Failed to description"); + + er = ::MsiRecordSetStringW(hRec, 2, pwzData); + ExitOnFailure(hr = HRESULT_FROM_WIN32(er), "Failed to set description"); + + // template + hr = WcaReadStringFromCaData(ppwzActionData, &pwzData); + ExitOnFailure(hr, "Failed to template"); + + er = ::MsiRecordSetStringW(hRec, 3, pwzData); + ExitOnFailure(hr = HRESULT_FROM_WIN32(er), "Failed to set template"); + + // message + if (!fSuppress) + { + er = WcaProcessMessage(INSTALLMESSAGE_ACTIONSTART, hRec); + if (0 == er || IDOK == er || IDYES == er) + { + hr = S_OK; + } + else if (IDABORT == er || IDCANCEL == er) + { + WcaSetReturnValue(ERROR_INSTALL_USEREXIT); // note that the user said exit + hr = S_FALSE; + } + else + hr = E_UNEXPECTED; + } + +LExit: + // clean up + ReleaseStr(pwzData); + + return hr; +} + +HRESULT CpiActionDataMessage( + DWORD cArgs, + ... + ) +{ + HRESULT hr = S_OK; + UINT er = ERROR_SUCCESS; + + PMSIHANDLE hRec; + va_list args; + + // record + hRec = ::MsiCreateRecord(cArgs); + ExitOnNull(hRec, hr, E_OUTOFMEMORY, "Failed to create record"); + + va_start(args, cArgs); + for (DWORD i = 1; i <= cArgs; i++) + { + LPCWSTR pwzArg = va_arg(args, WCHAR*); + if (pwzArg && *pwzArg) + { + er = ::MsiRecordSetStringW(hRec, i, pwzArg); + ExitOnFailure(hr = HRESULT_FROM_WIN32(er), "Failed to set record field string"); + } + } + va_end(args); + + // message + er = WcaProcessMessage(INSTALLMESSAGE_ACTIONDATA, hRec); + if (0 == er || IDOK == er || IDYES == er) + { + hr = S_OK; + } + else if (IDABORT == er || IDCANCEL == er) + { + WcaSetReturnValue(ERROR_INSTALL_USEREXIT); // note that the user said exit + hr = S_FALSE; + } + else + hr = E_UNEXPECTED; + +LExit: + return hr; +} + +HRESULT CpiGetAdminCatalog( + ICOMAdminCatalog** ppiCatalog + ) +{ + HRESULT hr = S_OK; + + if (!gpiCatalog) + { + // get collection + hr = ::CoCreateInstance(CLSID_COMAdminCatalog, NULL, CLSCTX_ALL, IID_ICOMAdminCatalog, (void**)&gpiCatalog); + ExitOnFailure(hr, "Failed to create COM+ admin catalog object"); + } + + // return value + gpiCatalog->AddRef(); + *ppiCatalog = gpiCatalog; + + hr = S_OK; + +LExit: + return hr; +} + +HRESULT CpiLogCatalogErrorInfo() +{ + HRESULT hr = S_OK; + + ICOMAdminCatalog* piCatalog = NULL; + ICatalogCollection* piErrColl = NULL; + IDispatch* piDisp = NULL; + ICatalogObject* piObj = NULL; + + LPWSTR pwzName = NULL; + LPWSTR pwzErrorCode = NULL; + LPWSTR pwzMajorRef = NULL; + LPWSTR pwzMinorRef = NULL; + + // get catalog + hr = CpiGetAdminCatalog(&piCatalog); + ExitOnFailure(hr, "Failed to get COM+ admin catalog"); + + // get error info collection + hr = CpiGetCatalogCollection(L"ErrorInfo", &piErrColl); + ExitOnFailure(hr, "Failed to get error info collection"); + + // loop objects + long lCnt; + hr = piErrColl->get_Count(&lCnt); + ExitOnFailure(hr, "Failed to get to number of items in collection"); + + for (long i = 0; i < lCnt; i++) + { + // get ICatalogObject interface + hr = piErrColl->get_Item(i, &piDisp); + ExitOnFailure(hr, "Failed to get item from partitions collection"); + + hr = piDisp->QueryInterface(IID_ICatalogObject, (void**)&piObj); + ExitOnFailure(hr, "Failed to get IID_ICatalogObject interface"); + + // get properties + hr = CpiGetCollectionObjectValue(piObj, L"Name", &pwzName); + ExitOnFailure(hr, "Failed to get name"); + hr = CpiGetCollectionObjectValue(piObj, L"ErrorCode", &pwzErrorCode); + ExitOnFailure(hr, "Failed to get error code"); + hr = CpiGetCollectionObjectValue(piObj, L"MajorRef", &pwzMajorRef); + ExitOnFailure(hr, "Failed to get major ref"); + hr = CpiGetCollectionObjectValue(piObj, L"MinorRef", &pwzMinorRef); + ExitOnFailure(hr, "Failed to get minor ref"); + + // write to log + WcaLog(LOGMSG_STANDARD, "ErrorInfo: Name='%S', ErrorCode='%S', MajorRef='%S', MinorRef='%S'", + pwzName, pwzErrorCode, pwzMajorRef, pwzMinorRef); + + // clean up + ReleaseNullObject(piDisp); + ReleaseNullObject(piObj); + } + + hr = S_OK; + +LExit: + // clean up + ReleaseObject(piCatalog); + ReleaseObject(piErrColl); + ReleaseObject(piDisp); + ReleaseObject(piObj); + + ReleaseStr(pwzName); + ReleaseStr(pwzErrorCode); + ReleaseStr(pwzMajorRef); + ReleaseStr(pwzMinorRef); + + return hr; +} + +HRESULT CpiGetCatalogCollection( + LPCWSTR pwzName, + ICatalogCollection** ppiColl + ) +{ + HRESULT hr = S_OK; + + ICOMAdminCatalog* piCatalog = NULL; + IDispatch* piDisp = NULL; + + BSTR bstrName = NULL; + + // copy name string + bstrName = ::SysAllocString(pwzName); + ExitOnNull(bstrName, hr, E_OUTOFMEMORY, "Failed to allocate BSTR for collection name"); + + // get catalog + hr = CpiGetAdminCatalog(&piCatalog); + ExitOnFailure(hr, "Failed to get COM+ admin catalog"); + + // get collecton from catalog + hr = piCatalog->GetCollection(bstrName, &piDisp); + ExitOnFailure(hr, "Failed to get collection"); + + hr = piDisp->QueryInterface(IID_ICatalogCollection, (void**)ppiColl); + ExitOnFailure(hr, "Failed to get IID_ICatalogCollection interface"); + + // populate collection + hr = (*ppiColl)->Populate(); + if (COMADMIN_E_OBJECTERRORS == hr) + CpiLogCatalogErrorInfo(); + ExitOnFailure(hr, "Failed to populate collection"); + + hr = S_OK; + +LExit: + // clean up + ReleaseObject(piCatalog); + ReleaseObject(piDisp); + ReleaseBSTR(bstrName); + + return hr; +} + +HRESULT CpiGetCatalogCollection( + ICatalogCollection* piColl, + ICatalogObject* piObj, + LPCWSTR pwzName, + ICatalogCollection** ppiColl + ) +{ + HRESULT hr = S_OK; + + ICOMAdminCatalog* piCatalog = NULL; + IDispatch* piDisp = NULL; + + BSTR bstrName = NULL; + + VARIANT vtKey; + ::VariantInit(&vtKey); + + // copy name string + bstrName = ::SysAllocString(pwzName); + ExitOnNull(bstrName, hr, E_OUTOFMEMORY, "Failed to allocate BSTR for collection name"); + + // get catalog + hr = CpiGetAdminCatalog(&piCatalog); + ExitOnFailure(hr, "Failed to get COM+ admin catalog"); + + // get key + hr = piObj->get_Key(&vtKey); + ExitOnFailure(hr, "Failed to get object key"); + + // get collecton from catalog + hr = piColl->GetCollection(bstrName, vtKey, &piDisp); + ExitOnFailure(hr, "Failed to get collection"); + + hr = piDisp->QueryInterface(IID_ICatalogCollection, (void**)ppiColl); + ExitOnFailure(hr, "Failed to get IID_ICatalogCollection interface"); + + // populate collection + hr = (*ppiColl)->Populate(); + if (COMADMIN_E_OBJECTERRORS == hr) + CpiLogCatalogErrorInfo(); + ExitOnFailure(hr, "Failed to populate collection"); + + hr = S_OK; + +LExit: + // clean up + ReleaseObject(piCatalog); + ReleaseObject(piDisp); + ReleaseBSTR(bstrName); + ::VariantClear(&vtKey); + + return hr; +} + +HRESULT CpiAddCollectionObject( + ICatalogCollection* piColl, + ICatalogObject** ppiObj + ) +{ + HRESULT hr = S_OK; + + IDispatch* piDisp = NULL; + + hr = piColl->Add(&piDisp); + ExitOnFailure(hr, "Failed to add object to collection"); + + hr = piDisp->QueryInterface(IID_ICatalogObject, (void**)ppiObj); + ExitOnFailure(hr, "Failed to get IID_ICatalogObject interface"); + + hr = S_OK; + +LExit: + // clean up + ReleaseObject(piDisp); + + return hr; +} + +HRESULT CpiPutCollectionObjectValue( + ICatalogObject* piObj, + LPCWSTR pwzPropName, + LPCWSTR pwzValue + ) +{ + HRESULT hr = S_OK; + + BSTR bstrPropName = NULL; + + VARIANT vtVal; + ::VariantInit(&vtVal); + + // allocate property name string + bstrPropName = ::SysAllocString(pwzPropName); + ExitOnNull(bstrPropName, hr, E_OUTOFMEMORY, "Failed to allocate property name string"); + + // prepare value variant + vtVal.vt = VT_BSTR; + vtVal.bstrVal = ::SysAllocString(pwzValue); + ExitOnNull(vtVal.bstrVal, hr, E_OUTOFMEMORY, "Failed to allocate property value string"); + + // put value + hr = piObj->put_Value(bstrPropName, vtVal); + ExitOnFailure(hr, "Failed to put property value"); + + hr = S_OK; + +LExit: + // clean up + ReleaseBSTR(bstrPropName); + ::VariantClear(&vtVal); + + return hr; +} + +HRESULT CpiPutCollectionObjectValues( + ICatalogObject* piObj, + CPI_PROPERTY* pPropList + ) +{ + HRESULT hr = S_OK; + + for (CPI_PROPERTY* pItm = pPropList; pItm; pItm = pItm->pNext) + { + // set property + hr = CpiPutCollectionObjectValue(piObj, pItm->wzName, pItm->pwzValue); + ExitOnFailure(hr, "Failed to set object property value, name: %S", pItm->wzName); + } + + hr = S_OK; + +LExit: + return hr; +} + +HRESULT CpiGetCollectionObjectValue( + ICatalogObject* piObj, + LPCWSTR szPropName, + LPWSTR* ppwzValue + ) +{ + HRESULT hr = S_OK; + + BSTR bstrPropName = NULL; + + VARIANT vtVal; + ::VariantInit(&vtVal); + + // allocate property name string + bstrPropName = ::SysAllocString(szPropName); + ExitOnNull(bstrPropName, hr, E_OUTOFMEMORY, "Failed to allocate property name string"); + + // get value + hr = piObj->get_Value(bstrPropName, &vtVal); + ExitOnFailure(hr, "Failed to get property value"); + + hr = ::VariantChangeType(&vtVal, &vtVal, 0, VT_BSTR); + ExitOnFailure(hr, "Failed to change variant type"); + + hr = StrAllocString(ppwzValue, vtVal.bstrVal, ::SysStringLen(vtVal.bstrVal)); + ExitOnFailure(hr, "Failed to allocate memory for value string"); + + hr = S_OK; + +LExit: + // clean up + ReleaseBSTR(bstrPropName); + ::VariantClear(&vtVal); + + return hr; +} + +HRESULT CpiResetObjectProperty( + ICatalogCollection* piColl, + ICatalogObject* piObj, + LPCWSTR pwzPropName + ) +{ + HRESULT hr = S_OK; + + BSTR bstrPropName = NULL; + + long lChanges = 0; + + VARIANT vtVal; + ::VariantInit(&vtVal); + + // allocate property name string + bstrPropName = ::SysAllocString(pwzPropName); + ExitOnNull(bstrPropName, hr, E_OUTOFMEMORY, "Failed to allocate deleteable property name string"); + + // get value + hr = piObj->get_Value(bstrPropName, &vtVal); + ExitOnFailure(hr, "Failed to get deleteable property value"); + + hr = ::VariantChangeType(&vtVal, &vtVal, 0, VT_BOOL); + ExitOnFailure(hr, "Failed to change variant type"); + + // if the deleteable property is set + if (VARIANT_FALSE == vtVal.boolVal) + { + // clear property + vtVal.boolVal = VARIANT_TRUE; + + hr = piObj->put_Value(bstrPropName, vtVal); + ExitOnFailure(hr, "Failed to get property value"); + + // save changes + hr = piColl->SaveChanges(&lChanges); + if (COMADMIN_E_OBJECTERRORS == hr) + CpiLogCatalogErrorInfo(); + ExitOnFailure(hr, "Failed to save changes"); + } + + hr = S_OK; + +LExit: + // clean up + ReleaseBSTR(bstrPropName); + ::VariantClear(&vtVal); + + return hr; +} + +HRESULT CpiRemoveCollectionObject( + ICatalogCollection* piColl, + LPCWSTR pwzID, + LPCWSTR pwzName, + BOOL fResetDeleteable + ) +{ + HRESULT hr = S_OK; + + IDispatch* piDisp = NULL; + ICatalogObject* piObj = NULL; + + BOOL fMatch = FALSE; + + VARIANT vtVal; + ::VariantInit(&vtVal); + + long lCnt; + hr = piColl->get_Count(&lCnt); + ExitOnFailure(hr, "Failed to get to number of items in collection"); + + for (long i = 0; i < lCnt; i++) + { + // get ICatalogObject interface + hr = piColl->get_Item(i, &piDisp); + ExitOnFailure(hr, "Failed to get object from collection"); + + hr = piDisp->QueryInterface(IID_ICatalogObject, (void**)&piObj); + ExitOnFailure(hr, "Failed to get IID_ICatalogObject interface"); + + // compare id + if (pwzID && *pwzID) + { + hr = piObj->get_Key(&vtVal); + ExitOnFailure(hr, "Failed to get key"); + + hr = ::VariantChangeType(&vtVal, &vtVal, 0, VT_BSTR); + ExitOnFailure(hr, "Failed to change variant type"); + + if (0 == lstrcmpiW(vtVal.bstrVal, pwzID)) + fMatch = TRUE; + + ::VariantClear(&vtVal); + } + + // compare name + if (pwzName && *pwzName) + { + hr = piObj->get_Name(&vtVal); + ExitOnFailure(hr, "Failed to get name"); + + hr = ::VariantChangeType(&vtVal, &vtVal, 0, VT_BSTR); + ExitOnFailure(hr, "Failed to change variant type"); + + if (0 == lstrcmpW(vtVal.bstrVal, pwzName)) + fMatch = TRUE; + + ::VariantClear(&vtVal); + } + + // if it's a match, remove it + if (fMatch) + { + if (fResetDeleteable) + { + // reset deleteable property, if set + hr = CpiResetObjectProperty(piColl, piObj, L"Deleteable"); + ExitOnFailure(hr, "Failed to reset deleteable property"); + } + + hr = piColl->Remove(i); + ExitOnFailure(hr, "Failed to remove item from collection"); + break; + } + + // release interface pointers + ReleaseNullObject(piDisp); + ReleaseNullObject(piObj); + } + + hr = S_OK; + +LExit: + // clean up + ReleaseObject(piDisp); + ReleaseObject(piObj); + + ::VariantClear(&vtVal); + + return hr; +} + +HRESULT CpiRemoveUserCollectionObject( + ICatalogCollection* piColl, + PSID pSid + ) +{ + HRESULT hr = S_OK; + + int i = 0; + + // find index + hr = FindUserCollectionObjectIndex(piColl, pSid, &i); + ExitOnFailure(hr, "Failed to find user collection index"); + + if (S_FALSE == hr) + ExitFunction(); // not found, exit with hr = S_FALSE + + // remove object + hr = piColl->Remove(i); + ExitOnFailure(hr, "Failed to remove object from collection"); + + hr = S_OK; + +LExit: + return hr; +} + +HRESULT CpiFindCollectionObjectByStringKey( + ICatalogCollection* piColl, + LPCWSTR pwzKey, + ICatalogObject** ppiObj + ) +{ + HRESULT hr = S_OK; + + IDispatch* piDisp = NULL; + ICatalogObject* piObj = NULL; + + VARIANT vtVal; + ::VariantInit(&vtVal); + + long lCnt; + hr = piColl->get_Count(&lCnt); + ExitOnFailure(hr, "Failed to get to number of items in collection"); + + for (long i = 0; i < lCnt; i++) + { + // get ICatalogObject interface + hr = piColl->get_Item(i, &piDisp); + ExitOnFailure(hr, "Failed to get object from collection"); + + hr = piDisp->QueryInterface(IID_ICatalogObject, (void**)&piObj); + ExitOnFailure(hr, "Failed to get IID_ICatalogObject interface"); + + // compare key + hr = piObj->get_Key(&vtVal); + ExitOnFailure(hr, "Failed to get key"); + + hr = ::VariantChangeType(&vtVal, &vtVal, 0, VT_BSTR); + ExitOnFailure(hr, "Failed to change variant type"); + + if (0 == lstrcmpiW(vtVal.bstrVal, pwzKey)) + { + if (ppiObj) + { + *ppiObj = piObj; + piObj = NULL; + } + ExitFunction1(hr = S_OK); + } + + // clean up + ReleaseNullObject(piDisp); + ReleaseNullObject(piObj); + + ::VariantClear(&vtVal); + } + + hr = S_FALSE; + +LExit: + // clean up + ReleaseObject(piDisp); + ReleaseObject(piObj); + + ::VariantClear(&vtVal); + + return hr; +} + +HRESULT CpiFindCollectionObjectByIntegerKey( + ICatalogCollection* piColl, + long lKey, + ICatalogObject** ppiObj + ) +{ + HRESULT hr = S_OK; + + IDispatch* piDisp = NULL; + ICatalogObject* piObj = NULL; + + VARIANT vtVal; + ::VariantInit(&vtVal); + + long lCnt; + hr = piColl->get_Count(&lCnt); + ExitOnFailure(hr, "Failed to get to number of items in collection"); + + for (long i = 0; i < lCnt; i++) + { + // get ICatalogObject interface + hr = piColl->get_Item(i, &piDisp); + ExitOnFailure(hr, "Failed to get object from collection"); + + hr = piDisp->QueryInterface(IID_ICatalogObject, (void**)&piObj); + ExitOnFailure(hr, "Failed to get IID_ICatalogObject interface"); + + // compare key + hr = piObj->get_Key(&vtVal); + ExitOnFailure(hr, "Failed to get key"); + + hr = ::VariantChangeType(&vtVal, &vtVal, 0, VT_I4); + ExitOnFailure(hr, "Failed to change variant type"); + + if (vtVal.lVal == lKey) + { + if (ppiObj) + { + *ppiObj = piObj; + piObj = NULL; + } + ExitFunction1(hr = S_OK); + } + + // clean up + ReleaseNullObject(piDisp); + ReleaseNullObject(piObj); + + ::VariantClear(&vtVal); + } + + hr = S_FALSE; + +LExit: + // clean up + ReleaseObject(piDisp); + ReleaseObject(piObj); + + ::VariantClear(&vtVal); + + return hr; +} + +HRESULT CpiFindCollectionObjectByName( + ICatalogCollection* piColl, + LPCWSTR pwzName, + ICatalogObject** ppiObj + ) +{ + HRESULT hr = S_OK; + + IDispatch* piDisp = NULL; + ICatalogObject* piObj = NULL; + + VARIANT vtVal; + ::VariantInit(&vtVal); + + long lCnt; + hr = piColl->get_Count(&lCnt); + ExitOnFailure(hr, "Failed to get to number of items in collection"); + + for (long i = 0; i < lCnt; i++) + { + // get ICatalogObject interface + hr = piColl->get_Item(i, &piDisp); + ExitOnFailure(hr, "Failed to get object from collection"); + + hr = piDisp->QueryInterface(IID_ICatalogObject, (void**)&piObj); + ExitOnFailure(hr, "Failed to get IID_ICatalogObject interface"); + + // compare key + hr = piObj->get_Name(&vtVal); + ExitOnFailure(hr, "Failed to get key"); + + hr = ::VariantChangeType(&vtVal, &vtVal, 0, VT_BSTR); + ExitOnFailure(hr, "Failed to change variant type"); + + if (0 == lstrcmpW(vtVal.bstrVal, pwzName)) + { + if (ppiObj) + { + *ppiObj = piObj; + piObj = NULL; + } + ExitFunction1(hr = S_OK); + } + + // clean up + ReleaseNullObject(piDisp); + ReleaseNullObject(piObj); + + ::VariantClear(&vtVal); + } + + hr = S_FALSE; + +LExit: + // clean up + ReleaseObject(piDisp); + ReleaseObject(piObj); + + ::VariantClear(&vtVal); + + return hr; +} + +HRESULT CpiFindUserCollectionObject( + ICatalogCollection* piColl, + PSID pSid, + ICatalogObject** ppiObj + ) +{ + HRESULT hr = S_OK; + + int i = 0; + + IDispatch* piDisp = NULL; + + // find index + hr = FindUserCollectionObjectIndex(piColl, pSid, &i); + ExitOnFailure(hr, "Failed to find user collection index"); + + if (S_FALSE == hr) + ExitFunction(); // not found, exit with hr = S_FALSE + + // get object + if (ppiObj) + { + hr = piColl->get_Item(i, &piDisp); + ExitOnFailure(hr, "Failed to get object from collection"); + + hr = piDisp->QueryInterface(IID_ICatalogObject, (void**)ppiObj); + ExitOnFailure(hr, "Failed to get IID_ICatalogObject interface"); + } + + hr = S_OK; + +LExit: + // clean up + ReleaseObject(piDisp); + + return hr; +} + +HRESULT CpiGetPartitionsCollection( + ICatalogCollection** ppiPartColl + ) +{ + HRESULT hr = S_OK; + + // get collection + hr = CpiGetCatalogCollection(L"Partitions", ppiPartColl); + ExitOnFailure(hr, "Failed to get catalog collection"); + + hr = S_OK; + +LExit: + return hr; +} + +HRESULT CpiGetPartitionRolesCollection( + LPCWSTR pwzPartID, + ICatalogCollection** ppiRolesColl + ) +{ + HRESULT hr = S_OK; + + ICatalogCollection* piPartColl = NULL; + ICatalogObject* piPartObj = NULL; + + // get partitions collection + hr = CpiGetPartitionsCollection(&piPartColl); + ExitOnFailure(hr, "Failed to get partitions collection"); + + if (S_FALSE == hr) + ExitFunction(); // partitions collection not found, exit with hr = S_FALSE + + // find object + hr = CpiFindCollectionObjectByStringKey(piPartColl, pwzPartID, &piPartObj); + ExitOnFailure(hr, "Failed to find collection object"); + + if (S_FALSE == hr) + ExitFunction(); // partition not found, exit with hr = S_FALSE + + // get roles collection + hr = CpiGetCatalogCollection(piPartColl, piPartObj, L"RolesForPartition", ppiRolesColl); + ExitOnFailure(hr, "Failed to get catalog collection"); + + hr = S_OK; + +LExit: + // clean up + ReleaseObject(piPartColl); + ReleaseObject(piPartObj); + + return hr; +} + +HRESULT CpiGetUsersInPartitionRoleCollection( + LPCWSTR pwzPartID, + LPCWSTR pwzRoleName, + ICatalogCollection** ppiUsrInRoleColl + ) +{ + HRESULT hr = S_OK; + + ICatalogCollection* piRoleColl = NULL; + ICatalogObject* piRoleObj = NULL; + + // get roles collection + hr = CpiGetPartitionRolesCollection(pwzPartID, &piRoleColl); + ExitOnFailure(hr, "Failed to get roles collection"); + + if (S_FALSE == hr) + ExitFunction(); // partition roles collection not found, exit with hr = S_FALSE + + // find object + hr = CpiFindCollectionObjectByName(piRoleColl, pwzRoleName, &piRoleObj); + ExitOnFailure(hr, "Failed to find collection object"); + + if (S_FALSE == hr) + ExitFunction(); // user not found, exit with hr = S_FALSE + + // get roles collection + hr = CpiGetCatalogCollection(piRoleColl, piRoleObj, L"UsersInPartitionRole", ppiUsrInRoleColl); + ExitOnFailure(hr, "Failed to get catalog collection"); + + hr = S_OK; + +LExit: + // clean up + ReleaseObject(piRoleColl); + ReleaseObject(piRoleObj); + + return hr; +} + +HRESULT CpiGetPartitionUsersCollection( + ICatalogCollection** ppiUserColl + ) +{ + HRESULT hr = S_OK; + + // get roles collection + hr = CpiGetCatalogCollection(L"PartitionUsers", ppiUserColl); + ExitOnFailure(hr, "Failed to get catalog collection"); + + hr = S_OK; + +LExit: + return hr; +} + +HRESULT CpiGetApplicationsCollection( + LPCWSTR pwzPartID, + ICatalogCollection** ppiAppColl + ) +{ + HRESULT hr = S_OK; + + ICOMAdminCatalog* piCatalog = NULL; + ICOMAdminCatalog2* piCatalog2 = NULL; + BSTR bstrGlobPartID = NULL; + + ICatalogCollection* piPartColl = NULL; + ICatalogObject* piPartObj = NULL; + + // get catalog + hr = CpiGetAdminCatalog(&piCatalog); + ExitOnFailure(hr, "Failed to get COM+ admin catalog"); + + // get ICOMAdminCatalog2 interface + hr = piCatalog->QueryInterface(IID_ICOMAdminCatalog2, (void**)&piCatalog2); + + // COM+ 1.5 or later + if (E_NOINTERFACE != hr) + { + ExitOnFailure(hr, "Failed to get IID_ICOMAdminCatalog2 interface"); + + // partition id + if (!pwzPartID || !*pwzPartID) + { + // get global partition id + hr = piCatalog2->get_GlobalPartitionID(&bstrGlobPartID); + ExitOnFailure(hr, "Failed to get global partition id"); + } + + // get partitions collection + hr = CpiGetPartitionsCollection(&piPartColl); + ExitOnFailure(hr, "Failed to get partitions collection"); + + // find object + hr = CpiFindCollectionObjectByStringKey(piPartColl, bstrGlobPartID ? bstrGlobPartID : pwzPartID, &piPartObj); + ExitOnFailure(hr, "Failed to find collection object"); + + if (S_FALSE == hr) + ExitFunction(); // partition not found, exit with hr = S_FALSE + + // get applications collection + hr = CpiGetCatalogCollection(piPartColl, piPartObj, L"Applications", ppiAppColl); + ExitOnFailure(hr, "Failed to get catalog collection for partition"); + } + + // COM+ pre 1.5 + else + { + // this version of COM+ does not support partitions, make sure a partition was not specified + if (pwzPartID && *pwzPartID) + ExitOnFailure(hr = E_FAIL, "Partitions are not supported by this version of COM+"); + + // get applications collection + hr = CpiGetCatalogCollection(L"Applications", ppiAppColl); + ExitOnFailure(hr, "Failed to get catalog collection"); + } + + hr = S_OK; + +LExit: + // clean up + ReleaseObject(piCatalog); + ReleaseObject(piCatalog2); + ReleaseBSTR(bstrGlobPartID); + + ReleaseObject(piPartColl); + ReleaseObject(piPartObj); + + return hr; +} + +HRESULT CpiGetRolesCollection( + LPCWSTR pwzPartID, + LPCWSTR pwzAppID, + ICatalogCollection** ppiRolesColl + ) +{ + HRESULT hr = S_OK; + + ICatalogCollection* piAppColl = NULL; + ICatalogObject* piAppObj = NULL; + + // get applications collection + hr = CpiGetApplicationsCollection(pwzPartID, &piAppColl); + ExitOnFailure(hr, "Failed to get applications collection"); + + if (S_FALSE == hr) + ExitFunction(); // applications collection not found, exit with hr = S_FALSE + + // find object + hr = CpiFindCollectionObjectByStringKey(piAppColl, pwzAppID, &piAppObj); + ExitOnFailure(hr, "Failed to find collection object"); + + if (S_FALSE == hr) + ExitFunction(); // application not found, exit with hr = S_FALSE + + // get roles collection + hr = CpiGetCatalogCollection(piAppColl, piAppObj, L"Roles", ppiRolesColl); + ExitOnFailure(hr, "Failed to catalog collection"); + + hr = S_OK; + +LExit: + // clean up + ReleaseObject(piAppColl); + ReleaseObject(piAppObj); + + return hr; +} + +HRESULT CpiGetUsersInRoleCollection( + LPCWSTR pwzPartID, + LPCWSTR pwzAppID, + LPCWSTR pwzRoleName, + ICatalogCollection** ppiUsrInRoleColl + ) +{ + HRESULT hr = S_OK; + + ICatalogCollection* piRoleColl = NULL; + ICatalogObject* piRoleObj = NULL; + + // get roles collection + hr = CpiGetRolesCollection(pwzPartID, pwzAppID, &piRoleColl); + ExitOnFailure(hr, "Failed to get roles collection"); + + if (S_FALSE == hr) + ExitFunction(); // roles collection not found, exit with hr = S_FALSE + + // find object + hr = CpiFindCollectionObjectByName(piRoleColl, pwzRoleName, &piRoleObj); + ExitOnFailure(hr, "Failed to find collection object"); + + if (S_FALSE == hr) + ExitFunction(); // role not found, exit with hr = S_FALSE + + // get roles collection + hr = CpiGetCatalogCollection(piRoleColl, piRoleObj, L"UsersInRole", ppiUsrInRoleColl); + ExitOnFailure(hr, "Failed to get catalog collection"); + + hr = S_OK; + +LExit: + // clean up + ReleaseObject(piRoleColl); + ReleaseObject(piRoleObj); + + return hr; +} + +HRESULT CpiGetComponentsCollection( + LPCWSTR pwzPartID, + LPCWSTR pwzAppID, + ICatalogCollection** ppiCompsColl + ) +{ + HRESULT hr = S_OK; + + ICatalogCollection* piAppColl = NULL; + ICatalogObject* piAppObj = NULL; + + // get applications collection + hr = CpiGetApplicationsCollection(pwzPartID, &piAppColl); + ExitOnFailure(hr, "Failed to get applications collection"); + + if (S_FALSE == hr) + ExitFunction(); // applications collection not found, exit with hr = S_FALSE + + // find object + hr = CpiFindCollectionObjectByStringKey(piAppColl, pwzAppID, &piAppObj); + ExitOnFailure(hr, "Failed to find collection object"); + + if (S_FALSE == hr) + ExitFunction(); // application not found, exit with hr = S_FALSE + + // get components collection + hr = CpiGetCatalogCollection(piAppColl, piAppObj, L"Components", ppiCompsColl); + ExitOnFailure(hr, "Failed to get catalog collection"); + + hr = S_OK; + +LExit: + // clean up + ReleaseObject(piAppColl); + ReleaseObject(piAppObj); + + return hr; +} + +HRESULT CpiGetInterfacesCollection( + ICatalogCollection* piCompColl, + ICatalogObject* piCompObj, + ICatalogCollection** ppiIntfColl + ) +{ + HRESULT hr = S_OK; + + // get interfaces collection + hr = CpiGetCatalogCollection(piCompColl, piCompObj, L"InterfacesForComponent", ppiIntfColl); + ExitOnFailure(hr, "Failed to get catalog collection"); + + hr = S_OK; + +LExit: + return hr; +} + +HRESULT CpiGetMethodsCollection( + ICatalogCollection* piIntfColl, + ICatalogObject* piIntfObj, + ICatalogCollection** ppiMethColl + ) +{ + HRESULT hr = S_OK; + + // get interfaces collection + hr = CpiGetCatalogCollection(piIntfColl, piIntfObj, L"MethodsForInterface", ppiMethColl); + ExitOnFailure(hr, "Failed to get catalog collection"); + + hr = S_OK; + +LExit: + return hr; +} + +HRESULT CpiGetSubscriptionsCollection( + LPCWSTR pwzPartID, + LPCWSTR pwzAppID, + LPCWSTR pwzCompCLSID, + ICatalogCollection** ppiSubsColl + ) +{ + HRESULT hr = S_OK; + + ICatalogCollection* piCompColl = NULL; + ICatalogObject* piCompObj = NULL; + + // get components collection + hr = CpiGetComponentsCollection(pwzPartID, pwzAppID, &piCompColl); + ExitOnFailure(hr, "Failed to get components collection"); + + if (S_FALSE == hr) + ExitFunction(); // components collection not found, exit with hr = S_FALSE + + // find object + hr = CpiFindCollectionObjectByStringKey(piCompColl, pwzCompCLSID, &piCompObj); + ExitOnFailure(hr, "Failed to find collection object"); + + if (S_FALSE == hr) + ExitFunction(); // component not found, exit with hr = S_FALSE + + // get subscriptions collection + hr = CpiGetCatalogCollection(piCompColl, piCompObj, L"SubscriptionsForComponent", ppiSubsColl); + ExitOnFailure(hr, "Failed to get catalog collection"); + + hr = S_OK; + +LExit: + // clean up + ReleaseObject(piCompColl); + ReleaseObject(piCompObj); + + return hr; +} + +HRESULT CpiReadPropertyList( + LPWSTR* ppwzData, + CPI_PROPERTY** ppPropList + ) +{ + HRESULT hr = S_OK; + + CPI_PROPERTY* pItm = NULL; + LPWSTR pwzName = NULL; + + // clear list if it already contains items + if (*ppPropList) + CpiFreePropertyList(*ppPropList); + *ppPropList = NULL; + + // read property count + int iPropCnt = 0; + hr = WcaReadIntegerFromCaData(ppwzData, &iPropCnt); + ExitOnFailure(hr, "Failed to read property count"); + + for (int i = 0; i < iPropCnt; i++) + { + // allocate new element + pItm = (CPI_PROPERTY*)::HeapAlloc(::GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(CPI_PROPERTY)); + if (!pItm) + ExitFunction1(hr = E_OUTOFMEMORY); + + // Name + hr = WcaReadStringFromCaData(ppwzData, &pwzName); + ExitOnFailure(hr, "Failed to read name"); + StringCchCopyW(pItm->wzName, countof(pItm->wzName), pwzName); + + // Value + hr = WcaReadStringFromCaData(ppwzData, &pItm->pwzValue); + ExitOnFailure(hr, "Failed to read property value"); + + // add to list + if (*ppPropList) + pItm->pNext = *ppPropList; + *ppPropList = pItm; + pItm = NULL; + } + + hr = S_OK; + +LExit: + // clean up + ReleaseStr(pwzName); + + if (pItm) + CpiFreePropertyList(pItm); + + return hr; +} + +void CpiFreePropertyList( + CPI_PROPERTY* pList + ) +{ + while (pList) + { + ReleaseStr(pList->pwzValue); + + CPI_PROPERTY* pDelete = pList; + pList = pList->pNext; + ::HeapFree(::GetProcessHeap(), 0, pDelete); + } +} + +HRESULT CpiWriteKeyToRollbackFile( + HANDLE hFile, + LPCWSTR pwzKey + ) +{ + HRESULT hr = S_OK; + + WCHAR wzKey[MAX_DARWIN_KEY + 1]; + ::ZeroMemory(wzKey, sizeof(wzKey)); + hr = StringCchCopyW(wzKey, countof(wzKey), pwzKey); + ExitOnFailure(hr, "Failed to copy key"); + + hr = WriteFileAll(hFile, (PBYTE)wzKey, MAX_DARWIN_KEY * sizeof(WCHAR)); + ExitOnFailure(hr, "Failed to write buffer"); + + FlushFileBuffers(hFile); + + hr = S_OK; + +LExit: + return hr; +} + +HRESULT CpiWriteIntegerToRollbackFile( + HANDLE hFile, + int i + ) +{ + HRESULT hr = S_OK; + + hr = WriteFileAll(hFile, (PBYTE)&i, sizeof(int)); + ExitOnFailure(hr, "Failed to write buffer"); + + FlushFileBuffers(hFile); + + hr = S_OK; + +LExit: + return hr; +} + +HRESULT CpiReadRollbackDataList( + HANDLE hFile, + CPI_ROLLBACK_DATA** pprdList + ) +{ + HRESULT hr = S_OK; + + int iCount; + + CPI_ROLLBACK_DATA* pItm = NULL; + + // read count + hr = ReadFileAll(hFile, (PBYTE)&iCount, sizeof(int)); + if (HRESULT_FROM_WIN32(ERROR_HANDLE_EOF) == hr) + ExitFunction1(hr = S_OK); // EOF reached, nothing left to read + ExitOnFailure(hr, "Failed to read count"); + + for (int i = 0; i < iCount; i++) + { + // allocate new element + pItm = (CPI_ROLLBACK_DATA*)::HeapAlloc(::GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(CPI_ROLLBACK_DATA)); + if (!pItm) + ExitFunction1(hr = E_OUTOFMEMORY); + + // read from file + hr = ReadFileAll(hFile, (PBYTE)pItm->wzKey, MAX_DARWIN_KEY * sizeof(WCHAR)); + if (HRESULT_FROM_WIN32(ERROR_HANDLE_EOF) == hr) + break; // EOF reached, nothing left to read + ExitOnFailure(hr, "Failed to read key"); + + hr = ReadFileAll(hFile, (PBYTE)&pItm->iStatus, sizeof(int)); + if (HRESULT_FROM_WIN32(ERROR_HANDLE_EOF) == hr) + pItm->iStatus = 0; // EOF reached, the operation was interupted; set status to zero + else + ExitOnFailure(hr, "Failed to read status"); + + // add to list + if (*pprdList) + pItm->pNext = *pprdList; + *pprdList = pItm; + pItm = NULL; + } + + hr = S_OK; + +LExit: + // clean up + if (pItm) + CpiFreeRollbackDataList(pItm); + + return hr; +} + +void CpiFreeRollbackDataList( + CPI_ROLLBACK_DATA* pList + ) +{ + while (pList) + { + CPI_ROLLBACK_DATA* pDelete = pList; + pList = pList->pNext; + ::HeapFree(::GetProcessHeap(), 0, pDelete); + } +} + +HRESULT CpiFindRollbackStatus( + CPI_ROLLBACK_DATA* pList, + LPCWSTR pwzKey, + int* piStatus + ) +{ + HRESULT hr = S_OK; + + for (; pList; pList = pList->pNext) + { + if (0 == lstrcmpW(pList->wzKey, pwzKey)) + { + *piStatus = pList->iStatus; + ExitFunction1(hr = S_OK); + } + } + + hr = S_FALSE; + +LExit: + return hr; +} + +HRESULT CpiAccountNameToSid( + LPCWSTR pwzAccountName, + PSID* ppSid + ) +{ + HRESULT hr = S_OK; + UINT er = ERROR_SUCCESS; + NTSTATUS st = 0; + + PSID pSid = NULL; + LSA_OBJECT_ATTRIBUTES loaAttributes; + LSA_HANDLE lsahPolicy = NULL; + LSA_UNICODE_STRING lusName; + PLSA_REFERENCED_DOMAIN_LIST plrdsDomains = NULL; + PLSA_TRANSLATED_SID pltsSid = NULL; + + ::ZeroMemory(&loaAttributes, sizeof(loaAttributes)); + ::ZeroMemory(&lusName, sizeof(lusName)); + + // identify well known SIDs + for (CPI_WELLKNOWN_SID* pWS = wsWellKnownSids; pWS->pwzName; pWS++) + { + if (0 == lstrcmpiW(pwzAccountName, pWS->pwzName)) + { + // allocate SID buffer + pSid = (PSID)::HeapAlloc(::GetProcessHeap(), HEAP_ZERO_MEMORY, ::GetSidLengthRequired(pWS->nSubAuthorityCount)); + ExitOnNull(pSid, hr, E_OUTOFMEMORY, "Failed to allocate buffer for SID"); + + // initialize SID + ::InitializeSid(pSid, &pWS->iaIdentifierAuthority, pWS->nSubAuthorityCount); + + // copy sub autorities + for (DWORD i = 0; i < pWS->nSubAuthorityCount; i++) + *::GetSidSubAuthority(pSid, i) = pWS->dwSubAuthority[i]; + + break; + } + } + + // lookup name + if (!pSid) + { + // open policy handle + st = ::LsaOpenPolicy(NULL, &loaAttributes, POLICY_ALL_ACCESS, &lsahPolicy); + er = ::LsaNtStatusToWinError(st); + ExitOnFailure(hr = HRESULT_FROM_WIN32(er), "Failed to open policy handle"); + + // create account name lsa unicode string + hr = InitLsaUnicodeString(&lusName, pwzAccountName, (DWORD)wcslen(pwzAccountName)); + ExitOnFailure(hr, "Failed to initialize account name string"); + + // lookup name + st = ::LsaLookupNames(lsahPolicy, 1, &lusName, &plrdsDomains, &pltsSid); + er = ::LsaNtStatusToWinError(st); + ExitOnFailure(hr = HRESULT_FROM_WIN32(er), "Failed to lookup account names"); + + if (SidTypeDomain == pltsSid->Use) + ExitOnFailure(hr = HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED), "Domain SIDs not supported"); + + // convert sid + hr = CreateSidFromDomainRidPair(plrdsDomains->Domains[pltsSid->DomainIndex].Sid, pltsSid->RelativeId, &pSid); + ExitOnFailure(hr, "Failed to convert SID"); + } + + *ppSid = pSid; + pSid = NULL; + + hr = S_OK; + +LExit: + // clean up + if (pSid) + ::HeapFree(::GetProcessHeap(), 0, pSid); + if (lsahPolicy) + ::LsaClose(lsahPolicy); + if (plrdsDomains) + ::LsaFreeMemory(plrdsDomains); + if (pltsSid) + ::LsaFreeMemory(pltsSid); + FreeLsaUnicodeString(&lusName); + + return hr; +} + +HRESULT CpiSidToAccountName( + PSID pSid, + LPWSTR* ppwzAccountName + ) +{ + HRESULT hr = S_OK; + UINT er = ERROR_SUCCESS; + NTSTATUS st = 0; + + LSA_OBJECT_ATTRIBUTES loaAttributes; + LSA_HANDLE lsahPolicy = NULL; + PLSA_REFERENCED_DOMAIN_LIST plrdsDomains = NULL; + PLSA_TRANSLATED_NAME pltnName = NULL; + + LPWSTR pwzDomain = NULL; + LPWSTR pwzName = NULL; + + ::ZeroMemory(&loaAttributes, sizeof(loaAttributes)); + + // open policy handle + st = ::LsaOpenPolicy(NULL, &loaAttributes, POLICY_ALL_ACCESS, &lsahPolicy); + er = ::LsaNtStatusToWinError(st); + ExitOnFailure(hr = HRESULT_FROM_WIN32(er), "Failed to open policy handle"); + + // lookup SID + st = ::LsaLookupSids(lsahPolicy, 1, &pSid, &plrdsDomains, &pltnName); + er = ::LsaNtStatusToWinError(st); + ExitOnFailure(hr = HRESULT_FROM_WIN32(er), "Failed lookup SID"); + + if (SidTypeDomain == pltnName->Use) + ExitOnFailure(hr = HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED), "Domain SIDs not supported"); + + // format account name string + if (SidTypeWellKnownGroup != pltnName->Use) + { + PLSA_UNICODE_STRING plusDomain = &plrdsDomains->Domains[pltnName->DomainIndex].Name; + hr = StrAllocString(&pwzDomain, plusDomain->Buffer, plusDomain->Length / sizeof(WCHAR)); + ExitOnFailure(hr, "Failed to allocate name string"); + } + + hr = StrAllocString(&pwzName, pltnName->Name.Buffer, pltnName->Name.Length / sizeof(WCHAR)); + ExitOnFailure(hr, "Failed to allocate domain string"); + + hr = StrAllocFormatted(ppwzAccountName, L"%s\\%s", pwzDomain ? pwzDomain : L"", pwzName); + ExitOnFailure(hr, "Failed to format account name string"); + + hr = S_OK; + +LExit: + // clean up + if (lsahPolicy) + ::LsaClose(lsahPolicy); + if (plrdsDomains) + ::LsaFreeMemory(plrdsDomains); + if (pltnName) + ::LsaFreeMemory(pltnName); + + ReleaseStr(pwzDomain); + ReleaseStr(pwzName); + + return hr; +} + +// helper function definitions + +static HRESULT FindUserCollectionObjectIndex( + ICatalogCollection* piColl, + PSID pSid, + int* pi + ) +{ + HRESULT hr = S_OK; + UINT er = ERROR_SUCCESS; + NTSTATUS st = 0; + + long i = 0; + long lCollCnt = 0; + + LSA_OBJECT_ATTRIBUTES loaAttributes; + LSA_HANDLE lsahPolicy = NULL; + PLSA_UNICODE_STRING plusNames = NULL; + PLSA_REFERENCED_DOMAIN_LIST plrdsDomains = NULL; + PLSA_TRANSLATED_SID pltsSids = NULL; + + IDispatch* piDisp = NULL; + ICatalogObject* piObj = NULL; + VARIANT vtVal; + + PSID pTmpSid = NULL; + + PLSA_TRANSLATED_SID pltsSid; + + ::VariantInit(&vtVal); + ::ZeroMemory(&loaAttributes, sizeof(loaAttributes)); + + // open policy handle + st = ::LsaOpenPolicy(NULL, &loaAttributes, POLICY_ALL_ACCESS, &lsahPolicy); + er = ::LsaNtStatusToWinError(st); + ExitOnFailure(hr = HRESULT_FROM_WIN32(er), "Failed to open policy handle"); + + // get number of elements in collection + hr = piColl->get_Count(&lCollCnt); + ExitOnFailure(hr, "Failed to get to number of objects in collection"); + + if (0 == lCollCnt) + ExitFunction1(hr = S_FALSE); // not found + + // allocate name buffer + plusNames = (PLSA_UNICODE_STRING)::HeapAlloc(::GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(LSA_UNICODE_STRING) * lCollCnt); + ExitOnNull(plusNames, hr, E_OUTOFMEMORY, "Failed to allocate names buffer"); + + // get accounts in collection + for (i = 0; i < lCollCnt; i++) + { + // get ICatalogObject interface + hr = piColl->get_Item(i, &piDisp); + ExitOnFailure(hr, "Failed to get object from collection"); + + hr = piDisp->QueryInterface(IID_ICatalogObject, (void**)&piObj); + ExitOnFailure(hr, "Failed to get IID_ICatalogObject interface"); + + // get value + hr = piObj->get_Key(&vtVal); + ExitOnFailure(hr, "Failed to get key"); + + hr = ::VariantChangeType(&vtVal, &vtVal, 0, VT_BSTR); + ExitOnFailure(hr, "Failed to change variant type"); + + // copy account name string + hr = InitLsaUnicodeString(&plusNames[i], vtVal.bstrVal, ::SysStringLen(vtVal.bstrVal)); + ExitOnFailure(hr, "Failed to initialize account name string"); + + // clean up + ReleaseNullObject(piDisp); + ReleaseNullObject(piObj); + ::VariantClear(&vtVal); + } + + // lookup names + st = ::LsaLookupNames(lsahPolicy, lCollCnt, plusNames, &plrdsDomains, &pltsSids); + er = ::LsaNtStatusToWinError(st); + if (ERROR_NONE_MAPPED != er && ERROR_SOME_NOT_MAPPED != er) + ExitOnFailure(hr = HRESULT_FROM_WIN32(er), "Failed to lookup account names"); + + // compare SIDs + for (i = 0; i < lCollCnt; i++) + { + // get SID + pltsSid = &pltsSids[i]; + if (SidTypeDomain == pltsSid->Use || SidTypeInvalid == pltsSid->Use || SidTypeUnknown == pltsSid->Use) + continue; // ignore... + + hr = CreateSidFromDomainRidPair(plrdsDomains->Domains[pltsSid->DomainIndex].Sid, pltsSid->RelativeId, &pTmpSid); + ExitOnFailure(hr, "Failed to convert SID"); + + // compare SIDs + if (::EqualSid(pSid, pTmpSid)) + { + *pi = i; + ExitFunction1(hr = S_OK); + } + } + + if (ERROR_NONE_MAPPED == er || ERROR_SOME_NOT_MAPPED == er) + hr = HRESULT_FROM_WIN32(er); + else + hr = S_FALSE; // not found + +LExit: + // clean up + ReleaseObject(piDisp); + ReleaseObject(piObj); + ::VariantClear(&vtVal); + + if (plusNames) + { + for (i = 0; i < lCollCnt; i++) + FreeLsaUnicodeString(&plusNames[i]); + ::HeapFree(::GetProcessHeap(), 0, plusNames); + } + + if (lsahPolicy) + ::LsaClose(lsahPolicy); + if (plrdsDomains) + ::LsaFreeMemory(plrdsDomains); + if (pltsSids) + ::LsaFreeMemory(pltsSids); + + if (pTmpSid) + ::HeapFree(::GetProcessHeap(), 0, pTmpSid); + + return hr; +} + +static HRESULT CreateSidFromDomainRidPair( + PSID pDomainSid, + DWORD dwRid, + PSID* ppSid + ) +{ + HRESULT hr = S_OK; + PSID pSid = NULL; + + // get domain SID sub authority count + UCHAR ucSubAuthorityCount = *::GetSidSubAuthorityCount(pDomainSid); + + // allocate SID buffer + DWORD dwLengthRequired = ::GetSidLengthRequired(ucSubAuthorityCount + (UCHAR)1); + if (*ppSid) + { + SIZE_T ccb = ::HeapSize(::GetProcessHeap(), 0, *ppSid); + if (-1 == ccb) + ExitOnFailure(hr = E_FAIL, "Failed to get size of SID buffer"); + + if (ccb < dwLengthRequired) + { + pSid = (PSID)::HeapReAlloc(::GetProcessHeap(), HEAP_ZERO_MEMORY, *ppSid, dwLengthRequired); + ExitOnNull(pSid, hr, E_OUTOFMEMORY, "Failed to reallocate buffer for SID, len: %d", dwLengthRequired); + *ppSid = pSid; + } + } + else + { + *ppSid = (PSID)::HeapAlloc(::GetProcessHeap(), HEAP_ZERO_MEMORY, dwLengthRequired); + ExitOnNull(*ppSid, hr, E_OUTOFMEMORY, "Failed to allocate buffer for SID, len: %d", dwLengthRequired); + } + + ::InitializeSid(*ppSid, ::GetSidIdentifierAuthority(pDomainSid), ucSubAuthorityCount + (UCHAR)1); + + // copy sub autorities + DWORD i = 0; + for (; i < ucSubAuthorityCount; i++) + *::GetSidSubAuthority(*ppSid, i) = *::GetSidSubAuthority(pDomainSid, i); + *::GetSidSubAuthority(*ppSid, i) = dwRid; + + hr = S_OK; + +LExit: + return hr; +} + +static HRESULT InitLsaUnicodeString( + PLSA_UNICODE_STRING plusStr, + LPCWSTR pwzStr, + DWORD dwLen + ) +{ + HRESULT hr = S_OK; + + plusStr->Length = (USHORT)dwLen * sizeof(WCHAR); + plusStr->MaximumLength = (USHORT)(dwLen + 1) * sizeof(WCHAR); + + plusStr->Buffer = (WCHAR*)::HeapAlloc(::GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(WCHAR) * (dwLen + 1)); + ExitOnNull(plusStr->Buffer, hr, E_OUTOFMEMORY, "Failed to allocate account name string"); + + hr = StringCchCopyW(plusStr->Buffer, dwLen + 1, pwzStr); + ExitOnFailure(hr, "Failed to copy buffer"); + + hr = S_OK; + +LExit: + return hr; +} + +static void FreeLsaUnicodeString( + PLSA_UNICODE_STRING plusStr + ) +{ + if (plusStr->Buffer) + ::HeapFree(::GetProcessHeap(), 0, plusStr->Buffer); +} + +static HRESULT WriteFileAll( + HANDLE hFile, + PBYTE pbBuffer, + DWORD dwBufferLength + ) +{ + HRESULT hr = S_OK; + + DWORD dwBytesWritten; + + while (dwBufferLength) + { + if (!::WriteFile(hFile, pbBuffer, dwBufferLength, &dwBytesWritten, NULL)) + ExitFunction1(hr = HRESULT_FROM_WIN32(::GetLastError())); + + dwBufferLength -= dwBytesWritten; + pbBuffer += dwBytesWritten; + } + + hr = S_OK; + +LExit: + return hr; +} + +static HRESULT ReadFileAll( + HANDLE hFile, + PBYTE pbBuffer, + DWORD dwBufferLength + ) +{ + HRESULT hr = S_OK; + + DWORD dwBytesRead; + + while (dwBufferLength) + { + if (!::ReadFile(hFile, pbBuffer, dwBufferLength, &dwBytesRead, NULL)) + ExitFunction1(hr = HRESULT_FROM_WIN32(::GetLastError())); + + if (0 == dwBytesRead) + ExitFunction1(hr = HRESULT_FROM_WIN32(ERROR_HANDLE_EOF)); + + dwBufferLength -= dwBytesRead; + pbBuffer += dwBytesRead; + } + + hr = S_OK; + +LExit: + return hr; +} diff --git a/src/ca/cputilexec.h b/src/ca/cputilexec.h new file mode 100644 index 00000000..51b47583 --- /dev/null +++ b/src/ca/cputilexec.h @@ -0,0 +1,193 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + + +#define CPI_MAX_GUID 38 + +enum eActionType { atNoOp = 0, atCreate, atRemove }; + + +// structs + +struct CPI_PROPERTY +{ + WCHAR wzName[MAX_DARWIN_KEY + 1]; + LPWSTR pwzValue; + + CPI_PROPERTY* pNext; +}; + +struct CPI_ROLLBACK_DATA +{ + WCHAR wzKey[MAX_DARWIN_KEY + 1]; + int iStatus; + + CPI_ROLLBACK_DATA* pNext; +}; + + +// function prototypes + +void CpiInitialize(); +void CpiFinalize(); +HRESULT CpiActionStartMessage( + LPWSTR* ppwzActionData, + BOOL fSuppress + ); +HRESULT CpiActionDataMessage( + DWORD cArgs, + ... + ); +HRESULT CpiGetAdminCatalog( + ICOMAdminCatalog** ppiCatalog + ); +HRESULT CpiLogCatalogErrorInfo(); +HRESULT CpiGetCatalogCollection( + LPCWSTR pwzName, + ICatalogCollection** ppiColl + ); +HRESULT CpiGetCatalogCollection( + ICatalogCollection* piColl, + ICatalogObject* piObj, + LPCWSTR pwzName, + ICatalogCollection** ppiColl + ); +HRESULT CpiAddCollectionObject( + ICatalogCollection* piColl, + ICatalogObject** ppiObj + ); +HRESULT CpiPutCollectionObjectValue( + ICatalogObject* piObj, + LPCWSTR pwzPropName, + LPCWSTR pwzValue + ); +HRESULT CpiPutCollectionObjectValues( + ICatalogObject* piObj, + CPI_PROPERTY* pPropList + ); +HRESULT CpiGetCollectionObjectValue( + ICatalogObject* piObj, + LPCWSTR szPropName, + LPWSTR* ppwzValue + ); +HRESULT CpiResetObjectProperty( + ICatalogCollection* piColl, + ICatalogObject* piObj, + LPCWSTR pwzPropName + ); +HRESULT CpiRemoveCollectionObject( + ICatalogCollection* piColl, + LPCWSTR pwzID, + LPCWSTR pwzName, + BOOL fResetDeleteable + ); +HRESULT CpiRemoveUserCollectionObject( + ICatalogCollection* piColl, + PSID pSid + ); +HRESULT CpiFindCollectionObjectByStringKey( + ICatalogCollection* piColl, + LPCWSTR pwzKey, + ICatalogObject** ppiObj + ); +HRESULT CpiFindCollectionObjectByIntegerKey( + ICatalogCollection* piColl, + long lKey, + ICatalogObject** ppiObj + ); +HRESULT CpiFindCollectionObjectByName( + ICatalogCollection* piColl, + LPCWSTR pwzName, + ICatalogObject** ppiObj + ); +HRESULT CpiFindUserCollectionObject( + ICatalogCollection* piColl, + PSID pSid, + ICatalogObject** ppiObj + ); +HRESULT CpiGetPartitionsCollection( + ICatalogCollection** ppiPartColl + ); +HRESULT CpiGetPartitionRolesCollection( + LPCWSTR pwzPartID, + ICatalogCollection** ppiRolesColl + ); +HRESULT CpiGetUsersInPartitionRoleCollection( + LPCWSTR pwzPartID, + LPCWSTR pwzRoleName, + ICatalogCollection** ppiUsrInRoleColl + ); +HRESULT CpiGetPartitionUsersCollection( + ICatalogCollection** ppiUserColl + ); +HRESULT CpiGetApplicationsCollection( + LPCWSTR pwzPartID, + ICatalogCollection** ppiAppColl + ); +HRESULT CpiGetRolesCollection( + LPCWSTR pwzPartID, + LPCWSTR pwzAppID, + ICatalogCollection** ppiRolesColl + ); +HRESULT CpiGetUsersInRoleCollection( + LPCWSTR pwzPartID, + LPCWSTR pwzAppID, + LPCWSTR pwzRoleName, + ICatalogCollection** ppiUsrInRoleColl + ); +HRESULT CpiGetComponentsCollection( + LPCWSTR pwzPartID, + LPCWSTR pwzAppID, + ICatalogCollection** ppiCompsColl + ); +HRESULT CpiGetInterfacesCollection( + ICatalogCollection* piCompColl, + ICatalogObject* piCompObj, + ICatalogCollection** ppiIntfColl + ); +HRESULT CpiGetMethodsCollection( + ICatalogCollection* piIntfColl, + ICatalogObject* piIntfObj, + ICatalogCollection** ppiMethColl + ); +HRESULT CpiGetSubscriptionsCollection( + LPCWSTR pwzPartID, + LPCWSTR pwzAppID, + LPCWSTR pwzCompCLSID, + ICatalogCollection** ppiCompsColl + ); +HRESULT CpiReadPropertyList( + LPWSTR* ppwzData, + CPI_PROPERTY** ppPropList + ); +void CpiFreePropertyList( + CPI_PROPERTY* pList + ); +HRESULT CpiWriteKeyToRollbackFile( + HANDLE hFile, + LPCWSTR pwzKey + ); +HRESULT CpiWriteIntegerToRollbackFile( + HANDLE hFile, + int i + ); +HRESULT CpiReadRollbackDataList( + HANDLE hFile, + CPI_ROLLBACK_DATA** pprdList + ); +void CpiFreeRollbackDataList( + CPI_ROLLBACK_DATA* pList + ); +HRESULT CpiFindRollbackStatus( + CPI_ROLLBACK_DATA* pList, + LPCWSTR pwzKey, + int* piStatus + ); +HRESULT CpiAccountNameToSid( + LPCWSTR pwzAccountName, + PSID* ppSid + ); +HRESULT CpiSidToAccountName( + PSID pSid, + LPWSTR* ppwzAccountName + ); diff --git a/src/ca/cputilsched.cpp b/src/ca/cputilsched.cpp new file mode 100644 index 00000000..9dbe21ec --- /dev/null +++ b/src/ca/cputilsched.cpp @@ -0,0 +1,885 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + + +// sql queries + +LPCWSTR vcsActionTextQuery = + L"SELECT `Description`, `Template` FROM `ActionText` WHERE `Action` = ?"; +enum eActionTextQuery { atqDescription = 1, atqTemplate }; + +LPCWSTR vcsComponentAttributesQuery = + L"SELECT `Attributes` FROM `Component` WHERE `Component` = ?"; +enum eComponentAttributesQuery { caqAttributes = 1 }; + +LPCWSTR vcsUserQuery = L"SELECT `Domain`, `Name` FROM `User` WHERE `User` = ?"; +enum eUserQuery { uqDomain = 1, uqName }; + +enum ePropertyQuery { pqName = 1, pqValue }; + + +// prototypes for private helper functions + +static HRESULT FindPropertyDefinition( + CPI_PROPERTY_DEFINITION* pPropDefList, + LPCWSTR pwzName, + CPI_PROPERTY_DEFINITION** ppPropDef + ); +static HRESULT GetUserAccountName( + LPCWSTR pwzKey, + LPWSTR* ppwzAccount + ); + + +// variables + +static ICOMAdminCatalog* gpiCatalog; +static ICatalogCollection* gpiPartColl; +static ICatalogCollection* gpiAppColl; + +static int giTables; + + +// function definitions + +void CpiInitialize() +{ + // collections + gpiCatalog = NULL; + gpiPartColl = NULL; + gpiAppColl = NULL; + + // tables + giTables = 0; + + if (S_OK == WcaTableExists(L"ComPlusPartition")) giTables |= cptComPlusPartition; + if (S_OK == WcaTableExists(L"ComPlusPartitionProperty")) giTables |= cptComPlusPartitionProperty; + if (S_OK == WcaTableExists(L"ComPlusPartitionRole")) giTables |= cptComPlusPartitionRole; + if (S_OK == WcaTableExists(L"ComPlusUserInPartitionRole")) giTables |= cptComPlusUserInPartitionRole; + if (S_OK == WcaTableExists(L"ComPlusGroupInPartitionRole")) giTables |= cptComPlusGroupInPartitionRole; + if (S_OK == WcaTableExists(L"ComPlusPartitionUser")) giTables |= cptComPlusPartitionUser; + if (S_OK == WcaTableExists(L"ComPlusApplication")) giTables |= cptComPlusApplication; + if (S_OK == WcaTableExists(L"ComPlusApplicationProperty")) giTables |= cptComPlusApplicationProperty; + if (S_OK == WcaTableExists(L"ComPlusApplicationRole")) giTables |= cptComPlusApplicationRole; + if (S_OK == WcaTableExists(L"ComPlusApplicationRoleProperty")) giTables |= cptComPlusApplicationRoleProperty; + if (S_OK == WcaTableExists(L"ComPlusUserInApplicationRole")) giTables |= cptComPlusUserInApplicationRole; + if (S_OK == WcaTableExists(L"ComPlusGroupInApplicationRole")) giTables |= cptComPlusGroupInApplicationRole; + if (S_OK == WcaTableExists(L"ComPlusAssembly")) giTables |= cptComPlusAssembly; + if (S_OK == WcaTableExists(L"ComPlusAssemblyDependency")) giTables |= cptComPlusAssemblyDependency; + if (S_OK == WcaTableExists(L"ComPlusComponent")) giTables |= cptComPlusComponent; + if (S_OK == WcaTableExists(L"ComPlusComponentProperty")) giTables |= cptComPlusComponentProperty; + if (S_OK == WcaTableExists(L"ComPlusRoleForComponent")) giTables |= cptComPlusRoleForComponent; + if (S_OK == WcaTableExists(L"ComPlusInterface")) giTables |= cptComPlusInterface; + if (S_OK == WcaTableExists(L"ComPlusInterfaceProperty")) giTables |= cptComPlusInterfaceProperty; + if (S_OK == WcaTableExists(L"ComPlusRoleForInterface")) giTables |= cptComPlusRoleForInterface; + if (S_OK == WcaTableExists(L"ComPlusMethod")) giTables |= cptComPlusMethod; + if (S_OK == WcaTableExists(L"ComPlusMethodProperty")) giTables |= cptComPlusMethodProperty; + if (S_OK == WcaTableExists(L"ComPlusRoleForMethod")) giTables |= cptComPlusRoleForMethod; + if (S_OK == WcaTableExists(L"ComPlusSubscription")) giTables |= cptComPlusSubscription; + if (S_OK == WcaTableExists(L"ComPlusSubscriptionProperty")) giTables |= cptComPlusSubscriptionProperty; +} + +void CpiFinalize() +{ + // collections + ReleaseObject(gpiCatalog); + ReleaseObject(gpiPartColl); + ReleaseObject(gpiAppColl); +} + +BOOL CpiTableExists( + int iTable + ) +{ + return (giTables & iTable) == iTable; +} + +HRESULT CpiGetAdminCatalog( + ICOMAdminCatalog** ppiCatalog + ) +{ + HRESULT hr = S_OK; + + if (!gpiCatalog) + { + // get collection + hr = ::CoCreateInstance(CLSID_COMAdminCatalog, NULL, CLSCTX_ALL, IID_ICOMAdminCatalog, (void**)&gpiCatalog); + ExitOnFailure(hr, "Failed to create COM+ admin catalog object"); + } + + // return value + gpiCatalog->AddRef(); + *ppiCatalog = gpiCatalog; + + hr = S_OK; + +LExit: + return hr; +} + +HRESULT CpiGetCatalogCollection( + LPCWSTR pwzName, + ICatalogCollection** ppiColl + ) +{ + HRESULT hr = S_OK; + + ICOMAdminCatalog* piCatalog = NULL; + IDispatch* piDisp = NULL; + BSTR bstrName = NULL; + + // copy name string + bstrName = ::SysAllocString(pwzName); + ExitOnNull(bstrName, hr, E_OUTOFMEMORY, "Failed to allocate BSTR for collection name"); + + // get catalog + hr = CpiGetAdminCatalog(&piCatalog); + ExitOnFailure(hr, "Failed to get COM+ admin catalog"); + + // get collecton from catalog + hr = piCatalog->GetCollection(bstrName, &piDisp); + ExitOnFailure(hr, "Failed to get collection"); + + hr = piDisp->QueryInterface(IID_ICatalogCollection, (void**)ppiColl); + ExitOnFailure(hr, "Failed to get IID_ICatalogCollection interface"); + + // populate collection + hr = (*ppiColl)->Populate(); + ExitOnFailure(hr, "Failed to populate collection"); + + hr = S_OK; + +LExit: + // clean up + ReleaseObject(piCatalog); + ReleaseObject(piDisp); + ReleaseBSTR(bstrName); + + return hr; +} + +HRESULT CpiGetCatalogCollection( + ICatalogCollection* piColl, + ICatalogObject* piObj, + LPCWSTR pwzName, + ICatalogCollection** ppiColl + ) +{ + HRESULT hr = S_OK; + + ICOMAdminCatalog* piCatalog = NULL; + IDispatch* piDisp = NULL; + BSTR bstrName = NULL; + + VARIANT vtKey; + ::VariantInit(&vtKey); + + // copy name string + bstrName = ::SysAllocString(pwzName); + ExitOnNull(bstrName, hr, E_OUTOFMEMORY, "Failed to allocate BSTR for collection name"); + + // get catalog + hr = CpiGetAdminCatalog(&piCatalog); + ExitOnFailure(hr, "Failed to get COM+ admin catalog"); + + // get key + hr = piObj->get_Key(&vtKey); + ExitOnFailure(hr, "Failed to get object key"); + + // get collecton from catalog + hr = piColl->GetCollection(bstrName, vtKey, &piDisp); + ExitOnFailure(hr, "Failed to get collection"); + + hr = piDisp->QueryInterface(IID_ICatalogCollection, (void**)ppiColl); + ExitOnFailure(hr, "Failed to get IID_ICatalogCollection interface"); + + // populate collection + hr = (*ppiColl)->Populate(); + ExitOnFailure(hr, "Failed to populate collection"); + + hr = S_OK; + +LExit: + // clean up + ReleaseObject(piCatalog); + ReleaseObject(piDisp); + ReleaseBSTR(bstrName); + ::VariantClear(&vtKey); + + return hr; +} + +HRESULT CpiGetKeyForObject( + ICatalogObject* piObj, + LPWSTR pwzKey, + SIZE_T cchKey + ) +{ + HRESULT hr = S_OK; + + VARIANT vtKey; + ::VariantInit(&vtKey); + + // get key + hr = piObj->get_Key(&vtKey); + ExitOnFailure(hr, "Failed to get key"); + + // change variant type + hr = ::VariantChangeType(&vtKey, &vtKey, 0, VT_BSTR); + ExitOnFailure(hr, "Failed to change variant type"); + + // copy key + hr = StringCchCopyW(pwzKey, cchKey, vtKey.bstrVal); + ExitOnFailure(hr, "Failed to copy key"); + + hr = S_OK; + +LExit: + // clean up + ::VariantClear(&vtKey); + + return hr; +} + +HRESULT CpiFindCollectionObject( + ICatalogCollection* piColl, + LPCWSTR pwzID, + LPCWSTR pwzName, + ICatalogObject** ppiObj + ) +{ + HRESULT hr = S_OK; + + IDispatch* piDisp = NULL; + ICatalogObject* piObj = NULL; + + VARIANT vtVal; + ::VariantInit(&vtVal); + + long lCnt; + hr = piColl->get_Count(&lCnt); + ExitOnFailure(hr, "Failed to get to number of items in collection"); + + for (long i = 0; i < lCnt; i++) + { + // get ICatalogObject interface + hr = piColl->get_Item(i, &piDisp); + ExitOnFailure(hr, "Failed to get object from collection"); + + hr = piDisp->QueryInterface(IID_ICatalogObject, (void**)&piObj); + ExitOnFailure(hr, "Failed to get IID_ICatalogObject interface"); + + // compare id + if (pwzID && *pwzID) + { + hr = piObj->get_Key(&vtVal); + ExitOnFailure(hr, "Failed to get key"); + + hr = ::VariantChangeType(&vtVal, &vtVal, 0, VT_BSTR); + ExitOnFailure(hr, "Failed to change variant type"); + + if (0 == lstrcmpiW(vtVal.bstrVal, pwzID)) + { + if (ppiObj) + { + *ppiObj = piObj; + piObj = NULL; + } + ExitFunction1(hr = S_OK); + } + + ::VariantClear(&vtVal); + } + + // compare name + if (pwzName && *pwzName) + { + hr = piObj->get_Name(&vtVal); + ExitOnFailure(hr, "Failed to get name"); + + hr = ::VariantChangeType(&vtVal, &vtVal, 0, VT_BSTR); + ExitOnFailure(hr, "Failed to change variant type"); + + if (0 == lstrcmpW(vtVal.bstrVal, pwzName)) + { + if (ppiObj) + { + *ppiObj = piObj; + piObj = NULL; + } + ExitFunction1(hr = S_OK); + } + + ::VariantClear(&vtVal); + } + + // release interface pointers + ReleaseNullObject(piDisp); + ReleaseNullObject(piObj); + } + + hr = S_FALSE; + +LExit: + // clean up + ReleaseObject(piDisp); + ReleaseObject(piObj); + + ::VariantClear(&vtVal); + + return hr; +} + +HRESULT CpiGetPartitionsCollection( + ICatalogCollection** ppiPartColl + ) +{ + HRESULT hr = S_OK; + + if (!gpiPartColl) + { + // get collection + hr = CpiGetCatalogCollection(L"Partitions", &gpiPartColl); + ExitOnFailure(hr, "Failed to get partitions collection"); + } + + // return value + gpiPartColl->AddRef(); + *ppiPartColl = gpiPartColl; + + hr = S_OK; + +LExit: + return hr; +} + +HRESULT CpiGetApplicationsCollection( + ICatalogCollection** ppiAppColl + ) +{ + HRESULT hr = S_OK; + + ICOMAdminCatalog* piCatalog = NULL; + ICOMAdminCatalog2* piCatalog2 = NULL; + ICatalogCollection* piPartColl = NULL; + ICatalogObject* piPartObj = NULL; + BSTR bstrGlobPartID = NULL; + + if (!gpiAppColl) + { + // get catalog + hr = CpiGetAdminCatalog(&piCatalog); + ExitOnFailure(hr, "Failed to get COM+ admin catalog"); + + // get ICOMAdminCatalog2 interface + hr = piCatalog->QueryInterface(IID_ICOMAdminCatalog2, (void**)&piCatalog2); + + // COM+ 1.5 or later + if (E_NOINTERFACE != hr) + { + ExitOnFailure(hr, "Failed to get IID_ICOMAdminCatalog2 interface"); + + // get global partition id + hr = piCatalog2->get_GlobalPartitionID(&bstrGlobPartID); + ExitOnFailure(hr, "Failed to get global partition id"); + + // get partitions collection + hr = CpiGetPartitionsCollection(&piPartColl); + ExitOnFailure(hr, "Failed to get partitions collection"); + + // find object + hr = CpiFindCollectionObject(piPartColl, bstrGlobPartID, NULL, &piPartObj); + ExitOnFailure(hr, "Failed to find collection object"); + + if (S_FALSE == hr) + ExitFunction(); // partition not found, exit with hr = S_FALSE + + // get applications collection + hr = CpiGetCatalogCollection(piPartColl, piPartObj, L"Applications", &gpiAppColl); + ExitOnFailure(hr, "Failed to get applications collection"); + } + + // COM+ pre 1.5 + else + { + // get applications collection + hr = CpiGetCatalogCollection(L"Applications", &gpiAppColl); + ExitOnFailure(hr, "Failed to get applications collection"); + } + } + + // return value + gpiAppColl->AddRef(); + *ppiAppColl = gpiAppColl; + + hr = S_OK; + +LExit: + // clean up + ReleaseObject(piCatalog); + ReleaseObject(piCatalog2); + ReleaseObject(piPartColl); + ReleaseObject(piPartObj); + ReleaseBSTR(bstrGlobPartID); + + return hr; +} + +HRESULT CpiAddActionTextToActionData( + LPCWSTR pwzAction, + LPWSTR* ppwzActionData + ) +{ + HRESULT hr = S_OK; + + PMSIHANDLE hView, hRecKey, hRec; + + LPWSTR pwzDescription = NULL; + LPWSTR pwzTemplate = NULL; + + if (S_OK == WcaTableExists(L"ActionText")) + { + // create parameter record + hRecKey = ::MsiCreateRecord(1); + ExitOnNull(hRecKey, hr, E_OUTOFMEMORY, "Failed to create record"); + hr = WcaSetRecordString(hRecKey, 1, pwzAction); + ExitOnFailure(hr, "Failed to set record string"); + + // open view + hr = WcaOpenView(vcsActionTextQuery, &hView); + ExitOnFailure(hr, "Failed to open view on ActionText table"); + hr = WcaExecuteView(hView, hRecKey); + ExitOnFailure(hr, "Failed to execute view on ActionText table"); + + // fetch record + hr = WcaFetchSingleRecord(hView, &hRec); + if (S_FALSE != hr) + { + ExitOnFailure(hr, "Failed to fetch action text record"); + + // get description + hr = WcaGetRecordString(hRec, atqDescription, &pwzDescription); + ExitOnFailure(hr, "Failed to get description"); + + // get template + hr = WcaGetRecordString(hRec, atqTemplate, &pwzTemplate); + ExitOnFailure(hr, "Failed to get template"); + } + } + + // add action name to action data + hr = WcaWriteStringToCaData(pwzAction, ppwzActionData); + ExitOnFailure(hr, "Failed to add action name to custom action data"); + + // add description to action data + hr = WcaWriteStringToCaData(pwzDescription ? pwzDescription : L"", ppwzActionData); + ExitOnFailure(hr, "Failed to add description to custom action data"); + + // add template to action data + hr = WcaWriteStringToCaData(pwzTemplate ? pwzTemplate : L"", ppwzActionData); + ExitOnFailure(hr, "Failed to add template to custom action data"); + + hr = S_OK; + +LExit: + // clean up + ReleaseStr(pwzDescription); + ReleaseStr(pwzTemplate); + + return hr; +} + +HRESULT CpiVerifyComponentArchitecure( + LPCWSTR pwzComponent, + BOOL* pfMatchingArchitecture + ) +{ + HRESULT hr = S_OK; + + PMSIHANDLE hView, hRecKey, hRec; + + int iAttributes = 0; + + if (S_OK == WcaTableExists(L"Component")) + { + // create parameter record + hRecKey = ::MsiCreateRecord(1); + ExitOnNull(hRecKey, hr, E_OUTOFMEMORY, "Failed to create record"); + hr = WcaSetRecordString(hRecKey, 1, pwzComponent); + ExitOnFailure(hr, "Failed to set record string"); + + // open view + hr = WcaOpenView(vcsComponentAttributesQuery, &hView); + ExitOnFailure(hr, "Failed to open view on ActionText table"); + hr = WcaExecuteView(hView, hRecKey); + ExitOnFailure(hr, "Failed to execute view on ActionText table"); + + // fetch record + hr = WcaFetchSingleRecord(hView, &hRec); + if (S_FALSE != hr) + { + ExitOnFailure(hr, "Failed to fetch component record"); + + hr = WcaGetRecordInteger(hRec, caqAttributes, &iAttributes); + ExitOnFailure(hr, "Failed to get component attributes"); + } + } + + // return values +#ifdef _WIN64 + *pfMatchingArchitecture = 256 == (iAttributes & 256); +#else + *pfMatchingArchitecture = 256 != (iAttributes & 256); +#endif + + hr = S_OK; + +LExit: + return hr; +} + +HRESULT CpiPropertiesRead( + LPCWSTR pwzQuery, + LPCWSTR pwzKey, + CPI_PROPERTY_DEFINITION* pPropDefList, + CPI_PROPERTY** ppPropList, + int* piCount + ) +{ + HRESULT hr = S_OK; + + PMSIHANDLE hView, hRecKey, hRec; + + CPI_PROPERTY* pItm = NULL; + LPWSTR pwzData = NULL; + + int iVersionNT = 0; + + CPI_PROPERTY_DEFINITION* pPropDef; + + *piCount = 0; + + // get NT version + hr = WcaGetIntProperty(L"VersionNT", &iVersionNT); + ExitOnFailure(hr, "Failed to set record string"); + + // create parameter record + hRecKey = ::MsiCreateRecord(1); + ExitOnNull(hRecKey, hr, E_OUTOFMEMORY, "Failed to create record"); + hr = WcaSetRecordString(hRecKey, 1, pwzKey); + ExitOnFailure(hr, "Failed to set record string"); + + // open view + hr = WcaOpenView(pwzQuery, &hView); + ExitOnFailure(hr, "Failed to open view on property table"); + hr = WcaExecuteView(hView, hRecKey); + ExitOnFailure(hr, "Failed to execute view on property table"); + + while (S_OK == (hr = WcaFetchRecord(hView, &hRec))) + { + // create entry + pItm = (CPI_PROPERTY*)::HeapAlloc(::GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(CPI_PROPERTY)); + if (!pItm) + ExitFunction1(hr = E_OUTOFMEMORY); + + // get name + hr = WcaGetRecordString(hRec, pqName, &pwzData); + ExitOnFailure(hr, "Failed to get name"); + StringCchCopyW(pItm->wzName, countof(pItm->wzName), pwzData); + + // get value + hr = WcaGetRecordFormattedString(hRec, pqValue, &pItm->pwzValue); + ExitOnFailure(hr, "Failed to get value"); + + // find property definition + hr = FindPropertyDefinition(pPropDefList, pItm->wzName, &pPropDef); + ExitOnFailure(hr, "Failed to find property definition"); + + if (S_FALSE == hr) + ExitOnFailure(hr = HRESULT_FROM_WIN32(ERROR_NOT_FOUND), "Unknown property, key: %S, property: %S", pwzKey, pItm->wzName); + + // check version, ignore if catalog version is too low + if (iVersionNT < pPropDef->iMinVersionNT) + { + WcaLog(LOGMSG_VERBOSE, "Skipping property since NT version is too low, key: %S, property: %S", pwzKey, pItm->wzName); + CpiPropertiesFreeList(pItm); + pItm = NULL; + continue; + } + + // if the property is a user, replace the User table key with a user account name + if (cpptUser == pPropDef->iType) + { + hr = GetUserAccountName(pItm->pwzValue, &pItm->pwzValue); + ExitOnFailure(hr, "Failed to get user account name"); + } + + // add entry + ++*piCount; + if (*ppPropList) + pItm->pNext = *ppPropList; + *ppPropList = pItm; + pItm = NULL; + } + + if (E_NOMOREITEMS == hr) + hr = S_OK; + +LExit: + // clean up + if (pItm) + CpiPropertiesFreeList(pItm); + + ReleaseStr(pwzData); + + return hr; +} + +void CpiPropertiesFreeList( + CPI_PROPERTY* pList + ) +{ + while (pList) + { + ReleaseStr(pList->pwzValue); + + CPI_PROPERTY* pDelete = pList; + pList = pList->pNext; + ::HeapFree(::GetProcessHeap(), 0, pDelete); + } +} + +HRESULT CpiAddPropertiesToActionData( + int iPropCount, + CPI_PROPERTY* pPropList, + LPWSTR* ppwzActionData + ) +{ + HRESULT hr = S_OK; + + hr = WcaWriteIntegerToCaData(iPropCount, ppwzActionData); + ExitOnFailure(hr, "Failed to add count to custom action data"); + + if (iPropCount) // count might be 0 event thought there are elements in the list + { + for (CPI_PROPERTY* pProp = pPropList; pProp; pProp = pProp->pNext) + { + hr = WcaWriteStringToCaData(pProp->wzName, ppwzActionData); + ExitOnFailure(hr, "Failed to add property name to custom action data, name: %S", pProp->wzName); + + hr = WcaWriteStringToCaData(pProp->pwzValue, ppwzActionData); + ExitOnFailure(hr, "Failed to add property value to custom action data, name: %S", pProp->wzName); + } + } + + hr = S_OK; + +LExit: + return hr; +} + +HRESULT CpiBuildAccountName( + LPCWSTR pwzDomain, + LPCWSTR pwzName, + LPWSTR* ppwzAccount + ) +{ + HRESULT hr = S_OK; + + WCHAR wzComputerName[MAX_COMPUTERNAME_LENGTH + 1]; + ::ZeroMemory(wzComputerName, sizeof(wzComputerName)); + + // if domain is '.', get computer name + if (0 == lstrcmpW(pwzDomain, L".")) + { + DWORD dwSize = countof(wzComputerName); + if (!::GetComputerNameW(wzComputerName, &dwSize)) + ExitOnFailure(hr = HRESULT_FROM_WIN32(::GetLastError()), "Failed to get computer name"); + } + + // build account name + hr = StrAllocFormatted(ppwzAccount, L"%s\\%s", *wzComputerName ? wzComputerName : pwzDomain, pwzName); + ExitOnFailure(hr, "Failed to build domain user name"); + + hr = S_OK; + +LExit: + return hr; +} + +HRESULT CpiGetTempFileName( + LPWSTR* ppwzTempFile + ) +{ + HRESULT hr = S_OK; + + // get temp path + WCHAR wzTempPath[MAX_PATH]; + DWORD dw = ::GetTempPathW(countof(wzTempPath), wzTempPath); + if (countof(wzTempPath) <= dw) + ExitOnFailure(hr = E_FAIL, "TEMP directory path too long"); + + // get unique number + LARGE_INTEGER liCount; + if (!::QueryPerformanceCounter(&liCount)) + ExitOnFailure(hr = HRESULT_FROM_WIN32(::GetLastError()), "Failed to query performance counter"); + + // create temp file name + hr = StrAllocFormatted(ppwzTempFile, L"%sCPI%I64X.tmp", wzTempPath, liCount.QuadPart); + ExitOnFailure(hr, "Failed to create temp file name string"); + + hr = S_OK; + +LExit: + return hr; +} + +HRESULT CpiCreateId( + LPWSTR pwzDest, + SIZE_T cchDest + ) +{ + HRESULT hr = S_OK; + + GUID guid; + + // create new guid + hr = ::CoCreateGuid(&guid); + ExitOnFailure(hr, "Failed to create new guid"); + + // convert guid to string + if (0 == ::StringFromGUID2(guid, pwzDest, (int)cchDest)) + ExitOnFailure(hr = E_FAIL, "Failed to convert guid to string"); + + hr = S_OK; + +LExit: + return hr; +} + +BOOL CpiIsInstalled( + INSTALLSTATE isInstalled + ) +{ + return INSTALLSTATE_LOCAL == isInstalled || INSTALLSTATE_SOURCE == isInstalled; +} + +BOOL CpiWillBeInstalled( + INSTALLSTATE isInstalled, + INSTALLSTATE isAction + ) +{ + return WcaIsInstalling(isInstalled, isAction) || + (CpiIsInstalled(isInstalled) && !WcaIsUninstalling(isInstalled, isAction)); +} + +HRESULT PcaGuidToRegFormat( + LPWSTR pwzGuid, + LPWSTR pwzDest, + SIZE_T cchDest + ) +{ + HRESULT hr = S_OK; + + GUID guid = GUID_NULL; + int cch = 0; + + WCHAR wz[39]; + ::ZeroMemory(wz, sizeof(wz)); + + cch = lstrlenW(pwzGuid); + + if (38 == cch && L'{' == pwzGuid[0] && L'}' == pwzGuid[37]) + StringCchCopyW(wz, countof(wz), pwzGuid); + else if (36 == cch) + StringCchPrintfW(wz, countof(wz), L"{%s}", pwzGuid); + else + ExitFunction1(hr = E_INVALIDARG); + + // convert string to guid + hr = ::CLSIDFromString(wz, &guid); + ExitOnFailure(hr, "Failed to parse guid string"); + + // convert guid to string + if (0 == ::StringFromGUID2(guid, pwzDest, (int)cchDest)) + ExitOnFailure(hr = E_FAIL, "Failed to convert guid to string"); + + hr = S_OK; + +LExit: + return hr; +} + + +// helper function definitions + +static HRESULT FindPropertyDefinition( + CPI_PROPERTY_DEFINITION* pPropDefList, + LPCWSTR pwzName, + CPI_PROPERTY_DEFINITION** ppPropDef + ) +{ + for (CPI_PROPERTY_DEFINITION* pItm = pPropDefList; pItm->pwzName; pItm++) + { + if (0 == lstrcmpW(pItm->pwzName, pwzName)) + { + *ppPropDef = pItm; + return S_OK; + } + } + + return S_FALSE; +} + +static HRESULT GetUserAccountName( + LPCWSTR pwzKey, + LPWSTR* ppwzAccount + ) +{ + HRESULT hr = S_OK; + + PMSIHANDLE hView, hRecKey, hRec; + + LPWSTR pwzDomain = NULL; + LPWSTR pwzName = NULL; + + // create parameter record + hRecKey = ::MsiCreateRecord(1); + ExitOnNull(hRecKey, hr, E_OUTOFMEMORY, "Failed to create record"); + hr = WcaSetRecordString(hRecKey, 1, pwzKey); + ExitOnFailure(hr, "Failed to set record string"); + + // open view + hr = WcaOpenView(vcsUserQuery, &hView); + ExitOnFailure(hr, "Failed to open view on User table"); + hr = WcaExecuteView(hView, hRecKey); + ExitOnFailure(hr, "Failed to execute view on User table"); + + // fetch record + hr = WcaFetchSingleRecord(hView, &hRec); + if (S_FALSE == hr) + ExitOnFailure(hr = HRESULT_FROM_WIN32(ERROR_NOT_FOUND), "User not found, key: %S", pwzKey); + ExitOnFailure(hr, "Failed to fetch user record"); + + // get user domain + hr = WcaGetRecordFormattedString(hRec, uqDomain, &pwzDomain); + ExitOnFailure(hr, "Failed to get domain"); + + // get user name + hr = WcaGetRecordFormattedString(hRec, uqName, &pwzName); + ExitOnFailure(hr, "Failed to get name"); + + // build account name + hr = CpiBuildAccountName(pwzDomain, pwzName, ppwzAccount); + ExitOnFailure(hr, "Failed to build account name"); + + hr = S_OK; + +LExit: + // clean up + ReleaseStr(pwzDomain); + ReleaseStr(pwzName); + + return hr; +} diff --git a/src/ca/cputilsched.h b/src/ca/cputilsched.h new file mode 100644 index 00000000..61aaab84 --- /dev/null +++ b/src/ca/cputilsched.h @@ -0,0 +1,144 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + + +#define CPI_MAX_GUID 38 + +enum eRunMode { rmDeferred = 1, rmCommit, rmRollback }; + +enum eActionType { atNoOp = 0, atCreate, atRemove }; + +enum eComPlusPropertyType { cpptNone = 0, cpptBoolean, cpptInteger, cpptString, cpptUser }; + +enum eComPlusTables +{ + cptComPlusPartition = (1 << 0), + cptComPlusPartitionProperty = (1 << 1), + cptComPlusPartitionRole = (1 << 2), + cptComPlusUserInPartitionRole = (1 << 3), + cptComPlusGroupInPartitionRole = (1 << 4), + cptComPlusPartitionUser = (1 << 5), + cptComPlusApplication = (1 << 6), + cptComPlusApplicationProperty = (1 << 7), + cptComPlusApplicationRole = (1 << 8), + cptComPlusApplicationRoleProperty = (1 << 9), + cptComPlusUserInApplicationRole = (1 << 10), + cptComPlusGroupInApplicationRole = (1 << 11), + cptComPlusAssembly = (1 << 12), + cptComPlusAssemblyDependency = (1 << 13), + cptComPlusComponent = (1 << 14), + cptComPlusComponentProperty = (1 << 15), + cptComPlusRoleForComponent = (1 << 16), + cptComPlusInterface = (1 << 17), + cptComPlusInterfaceProperty = (1 << 18), + cptComPlusRoleForInterface = (1 << 19), + cptComPlusMethod = (1 << 20), + cptComPlusMethodProperty = (1 << 21), + cptComPlusRoleForMethod = (1 << 22), + cptComPlusSubscription = (1 << 23), + cptComPlusSubscriptionProperty = (1 << 24) +}; + + +// structs + +struct CPI_PROPERTY +{ + WCHAR wzName[MAX_DARWIN_KEY + 1]; + LPWSTR pwzValue; + + CPI_PROPERTY* pNext; +}; + +struct CPI_PROPERTY_DEFINITION +{ + LPCWSTR pwzName; + int iType; + int iMinVersionNT; +}; + + +// function prototypes + +void CpiInitialize(); +void CpiFinalize(); +BOOL CpiTableExists( + int iTable + ); +HRESULT CpiGetAdminCatalog( + ICOMAdminCatalog** ppiCatalog + ); +HRESULT CpiGetCatalogCollection( + LPCWSTR pwzName, + ICatalogCollection** ppiColl + ); +HRESULT CpiGetCatalogCollection( + ICatalogCollection* piColl, + ICatalogObject* piObj, + LPCWSTR pwzName, + ICatalogCollection** ppiColl + ); +HRESULT CpiGetKeyForObject( + ICatalogObject* piObj, + LPWSTR pwzKey, + SIZE_T cchKey + ); +HRESULT CpiFindCollectionObject( + ICatalogCollection* piColl, + LPCWSTR pwzID, + LPCWSTR pwzName, + ICatalogObject** ppiObj + ); +HRESULT CpiGetPartitionsCollection( + ICatalogCollection** ppiPartColl + ); +HRESULT CpiGetApplicationsCollection( + ICatalogCollection** ppiAppColl + ); +HRESULT CpiAddActionTextToActionData( + LPCWSTR pwzAction, + LPWSTR* ppwzActionData + ); +HRESULT CpiVerifyComponentArchitecure( + LPCWSTR pwzComponent, + BOOL* pfMatchingArchitecture + ); +HRESULT CpiPropertiesRead( + LPCWSTR pwzQuery, + LPCWSTR pwzKey, + CPI_PROPERTY_DEFINITION* pPropDefList, + CPI_PROPERTY** ppPropList, + int* piCount + ); +void CpiPropertiesFreeList( + CPI_PROPERTY* pList + ); +HRESULT CpiAddPropertiesToActionData( + int iPropCount, + CPI_PROPERTY* pPropList, + LPWSTR* ppwzActionData + ); +HRESULT CpiBuildAccountName( + LPCWSTR pwzDomain, + LPCWSTR pwzName, + LPWSTR* ppwzAccount + ); +HRESULT CpiGetTempFileName( + LPWSTR* ppwzTempFile + ); +HRESULT CpiCreateId( + LPWSTR pwzDest, + SIZE_T cchDest + ); +BOOL CpiIsInstalled( + INSTALLSTATE isInstalled + ); +BOOL CpiWillBeInstalled( + INSTALLSTATE isInstalled, + INSTALLSTATE isAction + ); +HRESULT PcaGuidToRegFormat( + LPWSTR pwzGuid, + LPWSTR pwzDest, + SIZE_T cchDest + ); -- cgit v1.2.3-55-g6feb