From 7c8e34de56b3348c5a421cd0cced183e1394c5c7 Mon Sep 17 00:00:00 2001 From: Rob Mensching Date: Tue, 4 May 2021 22:48:12 -0700 Subject: Move Iis.wixext into ext --- src/ext/Iis/CSharp.Build.props | 11 + src/ext/Iis/Cpp.Build.props | 86 + src/ext/Iis/Directory.Build.props | 29 + src/ext/Iis/Directory.Build.targets | 48 + src/ext/Iis/Iis.wixext.sln | 58 + src/ext/Iis/README.md | 2 + src/ext/Iis/appveyor.cmd | 14 + src/ext/Iis/appveyor.yml | 40 + src/ext/Iis/ca/CustomMsiErrors.h | 48 + src/ext/Iis/ca/caDecor.h | 13 + src/ext/Iis/ca/dllmain.cpp | 26 + src/ext/Iis/ca/iisca.cpp | 3 + src/ext/Iis/ca/iisca.def | 30 + src/ext/Iis/ca/iisca.vcxproj | 147 + src/ext/Iis/ca/packages.config | 5 + src/ext/Iis/ca/precomp.h | 66 + src/ext/Iis/ca/sca.h | 124 + src/ext/Iis/ca/scaapppool.cpp | 594 +++ src/ext/Iis/ca/scaapppool.h | 88 + src/ext/Iis/ca/scaapppool7.cpp | 401 ++ src/ext/Iis/ca/scaapppool7.h | 36 + src/ext/Iis/ca/scacert.cpp | 1482 +++++++ src/ext/Iis/ca/scacert.h | 23 + src/ext/Iis/ca/scacertexec.cpp | 431 ++ src/ext/Iis/ca/scacost.h | 15 + src/ext/Iis/ca/scaexec.cpp | 1184 ++++++ src/ext/Iis/ca/scaexecIIS7.cpp | 4205 ++++++++++++++++++++ src/ext/Iis/ca/scaexecIIS7.h | 5 + src/ext/Iis/ca/scafilter.cpp | 510 +++ src/ext/Iis/ca/scafilter.h | 46 + src/ext/Iis/ca/scafilter7.cpp | 284 ++ src/ext/Iis/ca/scafilter7.h | 21 + src/ext/Iis/ca/scahttpheader.cpp | 323 ++ src/ext/Iis/ca/scahttpheader.h | 40 + src/ext/Iis/ca/scahttpheader7.cpp | 130 + src/ext/Iis/ca/scahttpheader7.h | 15 + src/ext/Iis/ca/scaiis.cpp | 481 +++ src/ext/Iis/ca/scaiis.h | 33 + src/ext/Iis/ca/scaiis7.cpp | 74 + src/ext/Iis/ca/scaiis7.h | 17 + src/ext/Iis/ca/scamimemap.cpp | 200 + src/ext/Iis/ca/scamimemap.h | 33 + src/ext/Iis/ca/scamimemap7.cpp | 68 + src/ext/Iis/ca/scamimemap7.h | 10 + src/ext/Iis/ca/scaproperty.cpp | 252 ++ src/ext/Iis/ca/scaproperty.h | 54 + src/ext/Iis/ca/scaproperty7.cpp | 108 + src/ext/Iis/ca/scaproperty7.h | 26 + src/ext/Iis/ca/scasched.cpp | 823 ++++ src/ext/Iis/ca/scassl.cpp | 115 + src/ext/Iis/ca/scassl.h | 36 + src/ext/Iis/ca/scassl7.cpp | 34 + src/ext/Iis/ca/scassl7.h | 8 + src/ext/Iis/ca/scauser.cpp | 91 + src/ext/Iis/ca/scauser.h | 39 + src/ext/Iis/ca/scavdir.cpp | 331 ++ src/ext/Iis/ca/scavdir.h | 71 + src/ext/Iis/ca/scavdir7.cpp | 380 ++ src/ext/Iis/ca/scavdir7.h | 66 + src/ext/Iis/ca/scaweb.cpp | 1187 ++++++ src/ext/Iis/ca/scaweb.h | 123 + src/ext/Iis/ca/scaweb7.cpp | 953 +++++ src/ext/Iis/ca/scaweb7.h | 97 + src/ext/Iis/ca/scawebapp.cpp | 194 + src/ext/Iis/ca/scawebapp.h | 42 + src/ext/Iis/ca/scawebapp7.cpp | 120 + src/ext/Iis/ca/scawebapp7.h | 10 + src/ext/Iis/ca/scawebappext.cpp | 207 + src/ext/Iis/ca/scawebappext.h | 32 + src/ext/Iis/ca/scawebappext7.cpp | 61 + src/ext/Iis/ca/scawebappext7.h | 9 + src/ext/Iis/ca/scawebdir.cpp | 241 ++ src/ext/Iis/ca/scawebdir.h | 57 + src/ext/Iis/ca/scawebdir7.cpp | 219 + src/ext/Iis/ca/scawebdir7.h | 51 + src/ext/Iis/ca/scaweberr.cpp | 371 ++ src/ext/Iis/ca/scaweberr.h | 30 + src/ext/Iis/ca/scaweberr7.cpp | 88 + src/ext/Iis/ca/scaweberr7.h | 10 + src/ext/Iis/ca/scaweblog.cpp | 177 + src/ext/Iis/ca/scaweblog.h | 27 + src/ext/Iis/ca/scaweblog7.cpp | 120 + src/ext/Iis/ca/scaweblog7.h | 14 + src/ext/Iis/ca/scawebprop.cpp | 301 ++ src/ext/Iis/ca/scawebprop.h | 60 + src/ext/Iis/ca/scawebprop7.cpp | 155 + src/ext/Iis/ca/scawebprop7.h | 12 + src/ext/Iis/ca/scawebsvcext.cpp | 343 ++ src/ext/Iis/ca/scawebsvcext.h | 35 + src/ext/Iis/ca/scawebsvcext7.cpp | 106 + src/ext/Iis/ca/scawebsvcext7.h | 8 + src/ext/Iis/nuget.config | 17 + .../test/WixToolsetTest.Iis/IisExtensionFixture.cs | 32 + .../TestData/UsingIis/Package.en-us.wxl | 11 + .../TestData/UsingIis/Package.wxs | 17 + .../TestData/UsingIis/PackageComponents.wxs | 16 + .../TestData/UsingIis/example.txt | 1 + .../WixToolsetTest.Iis/WixToolsetTest.Iis.csproj | 41 + .../WixToolsetTest.Iis.v3.ncrunchproject | 5 + src/ext/Iis/wix.snk | Bin 0 -> 596 bytes src/ext/Iis/wixext/IIsCompiler.cs | 2620 ++++++++++++ src/ext/Iis/wixext/IIsDecompiler.cs | 1549 +++++++ src/ext/Iis/wixext/IIsExtensionData.cs | 30 + src/ext/Iis/wixext/IisErrors.cs | 78 + src/ext/Iis/wixext/IisExtensionFactory.cs | 18 + src/ext/Iis/wixext/IisTableDefinitions.cs | 324 ++ .../IisWindowsInstallerBackendBinderExtension.cs | 13 + .../Iis/wixext/Symbols/CertificateHashSymbol.cs | 55 + src/ext/Iis/wixext/Symbols/CertificateSymbol.cs | 103 + src/ext/Iis/wixext/Symbols/IIsAppPoolSymbol.cs | 159 + src/ext/Iis/wixext/Symbols/IIsFilterSymbol.cs | 95 + src/ext/Iis/wixext/Symbols/IIsHttpHeaderSymbol.cs | 95 + src/ext/Iis/wixext/Symbols/IIsMimeMapSymbol.cs | 71 + src/ext/Iis/wixext/Symbols/IIsPropertySymbol.cs | 63 + src/ext/Iis/wixext/Symbols/IIsWebAddressSymbol.cs | 79 + .../Symbols/IIsWebApplicationExtensionSymbol.cs | 79 + .../Iis/wixext/Symbols/IIsWebApplicationSymbol.cs | 127 + .../wixext/Symbols/IIsWebDirPropertiesSymbol.cs | 151 + src/ext/Iis/wixext/Symbols/IIsWebDirSymbol.cs | 79 + src/ext/Iis/wixext/Symbols/IIsWebErrorSymbol.cs | 87 + src/ext/Iis/wixext/Symbols/IIsWebLogSymbol.cs | 47 + .../wixext/Symbols/IIsWebServiceExtensionSymbol.cs | 79 + .../wixext/Symbols/IIsWebSiteCertificatesSymbol.cs | 55 + src/ext/Iis/wixext/Symbols/IIsWebSiteSymbol.cs | 135 + .../Iis/wixext/Symbols/IIsWebVirtualDirSymbol.cs | 87 + src/ext/Iis/wixext/Symbols/IisSymbolDefinitions.cs | 107 + src/ext/Iis/wixext/WixToolset.Iis.wixext.csproj | 31 + src/ext/Iis/wixext/WixToolset.Iis.wixext.targets | 11 + src/ext/Iis/wixlib/IIsExtension.wxs | 58 + src/ext/Iis/wixlib/IIsExtension_Platform.wxi | 66 + src/ext/Iis/wixlib/IIsExtension_arm64.wxs | 7 + src/ext/Iis/wixlib/IIsExtension_x64.wxs | 7 + src/ext/Iis/wixlib/IIsExtension_x86.wxs | 7 + src/ext/Iis/wixlib/caDecor.wxi | 39 + src/ext/Iis/wixlib/caerr.wxi | 96 + src/ext/Iis/wixlib/de-de.wxl | 55 + src/ext/Iis/wixlib/en-us.wxl | 55 + src/ext/Iis/wixlib/iis.v3.ncrunchproject | 5 + src/ext/Iis/wixlib/iis.wixproj | 26 + src/ext/Iis/wixlib/ja-jp.wxl | 47 + src/ext/Iis/wixlib/pt-br.wxl | 50 + src/ext/global.json | 5 + 142 files changed, 26483 insertions(+) create mode 100644 src/ext/Iis/CSharp.Build.props create mode 100644 src/ext/Iis/Cpp.Build.props create mode 100644 src/ext/Iis/Directory.Build.props create mode 100644 src/ext/Iis/Directory.Build.targets create mode 100644 src/ext/Iis/Iis.wixext.sln create mode 100644 src/ext/Iis/README.md create mode 100644 src/ext/Iis/appveyor.cmd create mode 100644 src/ext/Iis/appveyor.yml create mode 100644 src/ext/Iis/ca/CustomMsiErrors.h create mode 100644 src/ext/Iis/ca/caDecor.h create mode 100644 src/ext/Iis/ca/dllmain.cpp create mode 100644 src/ext/Iis/ca/iisca.cpp create mode 100644 src/ext/Iis/ca/iisca.def create mode 100644 src/ext/Iis/ca/iisca.vcxproj create mode 100644 src/ext/Iis/ca/packages.config create mode 100644 src/ext/Iis/ca/precomp.h create mode 100644 src/ext/Iis/ca/sca.h create mode 100644 src/ext/Iis/ca/scaapppool.cpp create mode 100644 src/ext/Iis/ca/scaapppool.h create mode 100644 src/ext/Iis/ca/scaapppool7.cpp create mode 100644 src/ext/Iis/ca/scaapppool7.h create mode 100644 src/ext/Iis/ca/scacert.cpp create mode 100644 src/ext/Iis/ca/scacert.h create mode 100644 src/ext/Iis/ca/scacertexec.cpp create mode 100644 src/ext/Iis/ca/scacost.h create mode 100644 src/ext/Iis/ca/scaexec.cpp create mode 100644 src/ext/Iis/ca/scaexecIIS7.cpp create mode 100644 src/ext/Iis/ca/scaexecIIS7.h create mode 100644 src/ext/Iis/ca/scafilter.cpp create mode 100644 src/ext/Iis/ca/scafilter.h create mode 100644 src/ext/Iis/ca/scafilter7.cpp create mode 100644 src/ext/Iis/ca/scafilter7.h create mode 100644 src/ext/Iis/ca/scahttpheader.cpp create mode 100644 src/ext/Iis/ca/scahttpheader.h create mode 100644 src/ext/Iis/ca/scahttpheader7.cpp create mode 100644 src/ext/Iis/ca/scahttpheader7.h create mode 100644 src/ext/Iis/ca/scaiis.cpp create mode 100644 src/ext/Iis/ca/scaiis.h create mode 100644 src/ext/Iis/ca/scaiis7.cpp create mode 100644 src/ext/Iis/ca/scaiis7.h create mode 100644 src/ext/Iis/ca/scamimemap.cpp create mode 100644 src/ext/Iis/ca/scamimemap.h create mode 100644 src/ext/Iis/ca/scamimemap7.cpp create mode 100644 src/ext/Iis/ca/scamimemap7.h create mode 100644 src/ext/Iis/ca/scaproperty.cpp create mode 100644 src/ext/Iis/ca/scaproperty.h create mode 100644 src/ext/Iis/ca/scaproperty7.cpp create mode 100644 src/ext/Iis/ca/scaproperty7.h create mode 100644 src/ext/Iis/ca/scasched.cpp create mode 100644 src/ext/Iis/ca/scassl.cpp create mode 100644 src/ext/Iis/ca/scassl.h create mode 100644 src/ext/Iis/ca/scassl7.cpp create mode 100644 src/ext/Iis/ca/scassl7.h create mode 100644 src/ext/Iis/ca/scauser.cpp create mode 100644 src/ext/Iis/ca/scauser.h create mode 100644 src/ext/Iis/ca/scavdir.cpp create mode 100644 src/ext/Iis/ca/scavdir.h create mode 100644 src/ext/Iis/ca/scavdir7.cpp create mode 100644 src/ext/Iis/ca/scavdir7.h create mode 100644 src/ext/Iis/ca/scaweb.cpp create mode 100644 src/ext/Iis/ca/scaweb.h create mode 100644 src/ext/Iis/ca/scaweb7.cpp create mode 100644 src/ext/Iis/ca/scaweb7.h create mode 100644 src/ext/Iis/ca/scawebapp.cpp create mode 100644 src/ext/Iis/ca/scawebapp.h create mode 100644 src/ext/Iis/ca/scawebapp7.cpp create mode 100644 src/ext/Iis/ca/scawebapp7.h create mode 100644 src/ext/Iis/ca/scawebappext.cpp create mode 100644 src/ext/Iis/ca/scawebappext.h create mode 100644 src/ext/Iis/ca/scawebappext7.cpp create mode 100644 src/ext/Iis/ca/scawebappext7.h create mode 100644 src/ext/Iis/ca/scawebdir.cpp create mode 100644 src/ext/Iis/ca/scawebdir.h create mode 100644 src/ext/Iis/ca/scawebdir7.cpp create mode 100644 src/ext/Iis/ca/scawebdir7.h create mode 100644 src/ext/Iis/ca/scaweberr.cpp create mode 100644 src/ext/Iis/ca/scaweberr.h create mode 100644 src/ext/Iis/ca/scaweberr7.cpp create mode 100644 src/ext/Iis/ca/scaweberr7.h create mode 100644 src/ext/Iis/ca/scaweblog.cpp create mode 100644 src/ext/Iis/ca/scaweblog.h create mode 100644 src/ext/Iis/ca/scaweblog7.cpp create mode 100644 src/ext/Iis/ca/scaweblog7.h create mode 100644 src/ext/Iis/ca/scawebprop.cpp create mode 100644 src/ext/Iis/ca/scawebprop.h create mode 100644 src/ext/Iis/ca/scawebprop7.cpp create mode 100644 src/ext/Iis/ca/scawebprop7.h create mode 100644 src/ext/Iis/ca/scawebsvcext.cpp create mode 100644 src/ext/Iis/ca/scawebsvcext.h create mode 100644 src/ext/Iis/ca/scawebsvcext7.cpp create mode 100644 src/ext/Iis/ca/scawebsvcext7.h create mode 100644 src/ext/Iis/nuget.config create mode 100644 src/ext/Iis/test/WixToolsetTest.Iis/IisExtensionFixture.cs create mode 100644 src/ext/Iis/test/WixToolsetTest.Iis/TestData/UsingIis/Package.en-us.wxl create mode 100644 src/ext/Iis/test/WixToolsetTest.Iis/TestData/UsingIis/Package.wxs create mode 100644 src/ext/Iis/test/WixToolsetTest.Iis/TestData/UsingIis/PackageComponents.wxs create mode 100644 src/ext/Iis/test/WixToolsetTest.Iis/TestData/UsingIis/example.txt create mode 100644 src/ext/Iis/test/WixToolsetTest.Iis/WixToolsetTest.Iis.csproj create mode 100644 src/ext/Iis/test/WixToolsetTest.Iis/WixToolsetTest.Iis.v3.ncrunchproject create mode 100644 src/ext/Iis/wix.snk create mode 100644 src/ext/Iis/wixext/IIsCompiler.cs create mode 100644 src/ext/Iis/wixext/IIsDecompiler.cs create mode 100644 src/ext/Iis/wixext/IIsExtensionData.cs create mode 100644 src/ext/Iis/wixext/IisErrors.cs create mode 100644 src/ext/Iis/wixext/IisExtensionFactory.cs create mode 100644 src/ext/Iis/wixext/IisTableDefinitions.cs create mode 100644 src/ext/Iis/wixext/IisWindowsInstallerBackendBinderExtension.cs create mode 100644 src/ext/Iis/wixext/Symbols/CertificateHashSymbol.cs create mode 100644 src/ext/Iis/wixext/Symbols/CertificateSymbol.cs create mode 100644 src/ext/Iis/wixext/Symbols/IIsAppPoolSymbol.cs create mode 100644 src/ext/Iis/wixext/Symbols/IIsFilterSymbol.cs create mode 100644 src/ext/Iis/wixext/Symbols/IIsHttpHeaderSymbol.cs create mode 100644 src/ext/Iis/wixext/Symbols/IIsMimeMapSymbol.cs create mode 100644 src/ext/Iis/wixext/Symbols/IIsPropertySymbol.cs create mode 100644 src/ext/Iis/wixext/Symbols/IIsWebAddressSymbol.cs create mode 100644 src/ext/Iis/wixext/Symbols/IIsWebApplicationExtensionSymbol.cs create mode 100644 src/ext/Iis/wixext/Symbols/IIsWebApplicationSymbol.cs create mode 100644 src/ext/Iis/wixext/Symbols/IIsWebDirPropertiesSymbol.cs create mode 100644 src/ext/Iis/wixext/Symbols/IIsWebDirSymbol.cs create mode 100644 src/ext/Iis/wixext/Symbols/IIsWebErrorSymbol.cs create mode 100644 src/ext/Iis/wixext/Symbols/IIsWebLogSymbol.cs create mode 100644 src/ext/Iis/wixext/Symbols/IIsWebServiceExtensionSymbol.cs create mode 100644 src/ext/Iis/wixext/Symbols/IIsWebSiteCertificatesSymbol.cs create mode 100644 src/ext/Iis/wixext/Symbols/IIsWebSiteSymbol.cs create mode 100644 src/ext/Iis/wixext/Symbols/IIsWebVirtualDirSymbol.cs create mode 100644 src/ext/Iis/wixext/Symbols/IisSymbolDefinitions.cs create mode 100644 src/ext/Iis/wixext/WixToolset.Iis.wixext.csproj create mode 100644 src/ext/Iis/wixext/WixToolset.Iis.wixext.targets create mode 100644 src/ext/Iis/wixlib/IIsExtension.wxs create mode 100644 src/ext/Iis/wixlib/IIsExtension_Platform.wxi create mode 100644 src/ext/Iis/wixlib/IIsExtension_arm64.wxs create mode 100644 src/ext/Iis/wixlib/IIsExtension_x64.wxs create mode 100644 src/ext/Iis/wixlib/IIsExtension_x86.wxs create mode 100644 src/ext/Iis/wixlib/caDecor.wxi create mode 100644 src/ext/Iis/wixlib/caerr.wxi create mode 100644 src/ext/Iis/wixlib/de-de.wxl create mode 100644 src/ext/Iis/wixlib/en-us.wxl create mode 100644 src/ext/Iis/wixlib/iis.v3.ncrunchproject create mode 100644 src/ext/Iis/wixlib/iis.wixproj create mode 100644 src/ext/Iis/wixlib/ja-jp.wxl create mode 100644 src/ext/Iis/wixlib/pt-br.wxl create mode 100644 src/ext/global.json (limited to 'src/ext') diff --git a/src/ext/Iis/CSharp.Build.props b/src/ext/Iis/CSharp.Build.props new file mode 100644 index 00000000..b12f4c6e --- /dev/null +++ b/src/ext/Iis/CSharp.Build.props @@ -0,0 +1,11 @@ + + + + + true + $([System.IO.Path]::GetFullPath($(MSBuildThisFileDirectory)wix.snk)) + + diff --git a/src/ext/Iis/Cpp.Build.props b/src/ext/Iis/Cpp.Build.props new file mode 100644 index 00000000..9b7a1bb5 --- /dev/null +++ b/src/ext/Iis/Cpp.Build.props @@ -0,0 +1,86 @@ + + + + + + Win32 + $(BaseIntermediateOutputPath)$(Configuration)\$(Platform)\ + $(OutputPath)$(Platform)\ + + + + $([Microsoft.Build.Utilities.ToolLocationHelper]::GetLatestSDKTargetPlatformVersion('Windows', '10.0')) + + + + + $(DisableSpecificCompilerWarnings) + Level4 + $(ProjectDir)inc;$(MSBuildProjectDirectory);$(IntDir);$(SqlCESdkIncludePath);$(ProjectAdditionalIncludeDirectories);%(AdditionalIncludeDirectories) + WIN32;_WINDOWS;_WIN32_MSI=500;_WIN32_WINNT=0x0501;$(ArmPreprocessorDefinitions);$(UnicodePreprocessorDefinitions);_CRT_STDIO_LEGACY_WIDE_SPECIFIERS;_WINSOCK_DEPRECATED_NO_WARNINGS;%(PreprocessorDefinitions) + Use + precomp.h + StdCall + true + false + -YlprecompDefine + /Zc:threadSafeInit- %(AdditionalOptions) + true + + + $(ArmPreprocessorDefinitions);%(PreprocessorDefinitions) + $(ProjectAdditionalResourceIncludeDirectories);%(AdditionalIncludeDirectories) + + + $(OutDir);$(AdditionalMultiTargetLibraryPath);$(ProjectAdditionalLibraryDirectories);%(AdditionalLibraryDirectories) + + + $(ProjectSubSystem) + $(ProjectModuleDefinitionFile) + $(ResourceOnlyDll) + true + $(ProjectAdditionalLinkLibraries);advapi32.lib;comdlg32.lib;user32.lib;oleaut32.lib;gdi32.lib;shell32.lib;ole32.lib;version.lib;%(AdditionalDependencies) + $(OutDir);$(AdditionalMultiTargetLibraryPath);$(ArmLibraryDirectories);$(ProjectAdditionalLinkLibraryDirectories);%(AdditionalLibraryDirectories) + /IGNORE:4099 %(AdditionalOptions) + + + + + + NoExtensions + + + + + CDecl + + + + + OldStyle + true + true + + + + + Disabled + EnableFastChecks + _DEBUG;DEBUG;%(PreprocessorDefinitions) + MultiThreadedDebug + + + + + MinSpace + NDEBUG;%(PreprocessorDefinitions) + true + true + MultiThreaded + + + true + true + + + diff --git a/src/ext/Iis/Directory.Build.props b/src/ext/Iis/Directory.Build.props new file mode 100644 index 00000000..f83cc154 --- /dev/null +++ b/src/ext/Iis/Directory.Build.props @@ -0,0 +1,29 @@ + + + + + + Debug + false + MSB3246 + + $(MSBuildProjectName) + $([System.IO.Path]::GetFullPath($(MSBuildThisFileDirectory)..\build\)) + $(BaseOutputPath)obj\$(ProjectName)\ + $(BaseOutputPath)$(Configuration)\ + + WiX Toolset Team + WiX Toolset + Copyright (c) .NET Foundation and contributors. All rights reserved. + MS-RL + WiX Toolset + + + + + + + diff --git a/src/ext/Iis/Directory.Build.targets b/src/ext/Iis/Directory.Build.targets new file mode 100644 index 00000000..dac7452a --- /dev/null +++ b/src/ext/Iis/Directory.Build.targets @@ -0,0 +1,48 @@ + + + + + + + true + $(SolutionPath) + $(NCrunchOriginalSolutionPath) + + + + + + + $([System.IO.File]::ReadAllText($(TheSolutionPath))) + $([System.IO.Path]::GetDirectoryName( $(TheSolutionPath) )) + (?<="[PackageName]", ")(.*)(?=", ") + + + + + + %(Identity) + $(SolutionFileContent.Contains('\%(Identity).csproj')) + + + + + $(RegexPattern.Replace('[PackageName]','%(PackageName)') ) + $([System.Text.RegularExpressions.Regex]::Match('$(SolutionFileContent)', '%(Pattern)')) + + + + + + + + + + + diff --git a/src/ext/Iis/Iis.wixext.sln b/src/ext/Iis/Iis.wixext.sln new file mode 100644 index 00000000..85323fde --- /dev/null +++ b/src/ext/Iis/Iis.wixext.sln @@ -0,0 +1,58 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.30204.135 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "iisca", "src\ca\iisca.vcxproj", "{CB3FB8C4-14BF-4EA6-9F01-7FB258E5AEF3}" +EndProject +Project("{930C7802-8A8C-48F9-8165-68863BCCD9DD}") = "iis", "src\wixlib\iis.wixproj", "{92FE99D2-355D-4F52-A7C1-10EECB4A5BB1}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WixToolset.Iis.wixext", "src\wixext\WixToolset.Iis.wixext.csproj", "{612029FB-B5D4-4D7E-B794-A0E202BFE493}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WixToolsetTest.Iis", "src\test\WixToolsetTest.Iis\WixToolsetTest.Iis.csproj", "{E62712D7-31A1-49E4-B1F4-0084FAD14193}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Debug|x86 = Debug|x86 + Release|Any CPU = Release|Any CPU + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {CB3FB8C4-14BF-4EA6-9F01-7FB258E5AEF3}.Debug|Any CPU.ActiveCfg = Debug|Win32 + {CB3FB8C4-14BF-4EA6-9F01-7FB258E5AEF3}.Debug|Any CPU.Build.0 = Debug|Win32 + {CB3FB8C4-14BF-4EA6-9F01-7FB258E5AEF3}.Debug|x86.ActiveCfg = Debug|Win32 + {CB3FB8C4-14BF-4EA6-9F01-7FB258E5AEF3}.Debug|x86.Build.0 = Debug|Win32 + {CB3FB8C4-14BF-4EA6-9F01-7FB258E5AEF3}.Release|Any CPU.ActiveCfg = Release|Win32 + {CB3FB8C4-14BF-4EA6-9F01-7FB258E5AEF3}.Release|x86.ActiveCfg = Release|Win32 + {CB3FB8C4-14BF-4EA6-9F01-7FB258E5AEF3}.Release|x86.Build.0 = Release|Win32 + {92FE99D2-355D-4F52-A7C1-10EECB4A5BB1}.Debug|Any CPU.ActiveCfg = Debug|x86 + {92FE99D2-355D-4F52-A7C1-10EECB4A5BB1}.Debug|x86.ActiveCfg = Debug|x86 + {92FE99D2-355D-4F52-A7C1-10EECB4A5BB1}.Debug|x86.Build.0 = Debug|x86 + {92FE99D2-355D-4F52-A7C1-10EECB4A5BB1}.Release|Any CPU.ActiveCfg = Release|x86 + {92FE99D2-355D-4F52-A7C1-10EECB4A5BB1}.Release|x86.ActiveCfg = Release|x86 + {92FE99D2-355D-4F52-A7C1-10EECB4A5BB1}.Release|x86.Build.0 = Release|x86 + {612029FB-B5D4-4D7E-B794-A0E202BFE493}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {612029FB-B5D4-4D7E-B794-A0E202BFE493}.Debug|Any CPU.Build.0 = Debug|Any CPU + {612029FB-B5D4-4D7E-B794-A0E202BFE493}.Debug|x86.ActiveCfg = Debug|Any CPU + {612029FB-B5D4-4D7E-B794-A0E202BFE493}.Debug|x86.Build.0 = Debug|Any CPU + {612029FB-B5D4-4D7E-B794-A0E202BFE493}.Release|Any CPU.ActiveCfg = Release|Any CPU + {612029FB-B5D4-4D7E-B794-A0E202BFE493}.Release|Any CPU.Build.0 = Release|Any CPU + {612029FB-B5D4-4D7E-B794-A0E202BFE493}.Release|x86.ActiveCfg = Release|Any CPU + {612029FB-B5D4-4D7E-B794-A0E202BFE493}.Release|x86.Build.0 = Release|Any CPU + {E62712D7-31A1-49E4-B1F4-0084FAD14193}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E62712D7-31A1-49E4-B1F4-0084FAD14193}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E62712D7-31A1-49E4-B1F4-0084FAD14193}.Debug|x86.ActiveCfg = Debug|Any CPU + {E62712D7-31A1-49E4-B1F4-0084FAD14193}.Debug|x86.Build.0 = Debug|Any CPU + {E62712D7-31A1-49E4-B1F4-0084FAD14193}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E62712D7-31A1-49E4-B1F4-0084FAD14193}.Release|Any CPU.Build.0 = Release|Any CPU + {E62712D7-31A1-49E4-B1F4-0084FAD14193}.Release|x86.ActiveCfg = Release|Any CPU + {E62712D7-31A1-49E4-B1F4-0084FAD14193}.Release|x86.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {89256463-14A3-4968-8688-F75EC230B021} + EndGlobalSection +EndGlobal diff --git a/src/ext/Iis/README.md b/src/ext/Iis/README.md new file mode 100644 index 00000000..ddb21a82 --- /dev/null +++ b/src/ext/Iis/README.md @@ -0,0 +1,2 @@ +# Iis.wixext +WixToolset.Iis.wixext - Iis WiX Toolset Extension diff --git a/src/ext/Iis/appveyor.cmd b/src/ext/Iis/appveyor.cmd new file mode 100644 index 00000000..af993a8f --- /dev/null +++ b/src/ext/Iis/appveyor.cmd @@ -0,0 +1,14 @@ +@setlocal +@pushd %~dp0 + +nuget restore || exit /b + +msbuild -p:Configuration=Release -t:Restore || exit /b + +msbuild -p:Configuration=Release src\test\WixToolsetTest.Iis\WixToolsetTest.Iis.csproj || exit /b +dotnet test -c Release --no-build src\test\WixToolsetTest.Iis || exit /b + +msbuild -p:Configuration=Release -t:Pack src\wixext\WixToolset.Iis.wixext.csproj || exit /b + +@popd +@endlocal \ No newline at end of file diff --git a/src/ext/Iis/appveyor.yml b/src/ext/Iis/appveyor.yml new file mode 100644 index 00000000..7c686b04 --- /dev/null +++ b/src/ext/Iis/appveyor.yml @@ -0,0 +1,40 @@ +# 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. +# +# Do NOT modify this file. Update the canonical version in Home\repo-template\src\appveyor.yml +# then update all of the repos. + +branches: + only: + - master + - develop + +image: Visual Studio 2019 + +version: 0.0.0.{build} +configuration: Release + +environment: + DOTNET_SKIP_FIRST_TIME_EXPERIENCE: true + DOTNET_CLI_TELEMETRY_OPTOUT: 1 + NUGET_XMLDOC_MODE: skip + +build_script: + - appveyor.cmd + +pull_requests: + do_not_increment_build_number: true + +nuget: + disable_publish_on_pr: true + +skip_branch_with_pr: true +skip_tags: true + +artifacts: +- path: build\Release\**\*.nupkg + name: nuget + +notifications: +- provider: Slack + incoming_webhook: + secure: p5xuu+4x2JHfwGDMDe5KcG1k7gZxqYc4jWVwvyNZv5cvkubPD2waJs5yXMAXZNN7Z63/3PWHb7q4KoY/99AjauYa1nZ4c5qYqRPFRBKTHfA= diff --git a/src/ext/Iis/ca/CustomMsiErrors.h b/src/ext/Iis/ca/CustomMsiErrors.h new file mode 100644 index 00000000..1fd8d050 --- /dev/null +++ b/src/ext/Iis/ca/CustomMsiErrors.h @@ -0,0 +1,48 @@ +#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 msierrIISCannotConnect 26001 +#define msierrIISFailedReadWebSite 26002 +#define msierrIISFailedReadWebDirs 26003 +#define msierrIISFailedReadVDirs 26004 +#define msierrIISFailedReadFilters 26005 +#define msierrIISFailedReadAppPool 26006 +#define msierrIISFailedReadMimeMap 26007 +#define msierrIISFailedReadProp 26008 +#define msierrIISFailedReadWebSvcExt 26009 +#define msierrIISFailedReadWebError 26010 +#define msierrIISFailedReadHttpHeader 26011 + +#define msierrIISFailedSchedTransaction 26031 +#define msierrIISFailedSchedInstallWebs 26032 +#define msierrIISFailedSchedInstallWebDirs 26033 +#define msierrIISFailedSchedInstallVDirs 26034 +#define msierrIISFailedSchedInstallFilters 26035 +#define msierrIISFailedSchedInstallAppPool 26036 +#define msierrIISFailedSchedInstallProp 26037 +#define msierrIISFailedSchedInstallWebSvcExt 26038 + +#define msierrIISFailedSchedUninstallWebs 26051 +#define msierrIISFailedSchedUninstallWebDirs 26052 +#define msierrIISFailedSchedUninstallVDirs 26053 +#define msierrIISFailedSchedUninstallFilters 26054 +#define msierrIISFailedSchedUninstallAppPool 26055 +#define msierrIISFailedSchedUninstallProp 26056 +#define msierrIISFailedSchedUninstallWebSvcExt 26057 + +#define msierrIISFailedStartTransaction 26101 +#define msierrIISFailedOpenKey 26102 +#define msierrIISFailedCreateKey 26103 +#define msierrIISFailedWriteData 26104 +#define msierrIISFailedCreateApp 26105 +#define msierrIISFailedDeleteKey 26106 +#define msierrIISFailedDeleteApp 26107 +#define msierrIISFailedDeleteValue 26108 +#define msierrIISFailedCommitInUse 26109 + +// Last available value 26200 + +#define msierrCERTFailedOpen 26351 +#define msierrCERTFailedAdd 26352 + +// Last available value 26450 diff --git a/src/ext/Iis/ca/caDecor.h b/src/ext/Iis/ca/caDecor.h new file mode 100644 index 00000000..da274650 --- /dev/null +++ b/src/ext/Iis/ca/caDecor.h @@ -0,0 +1,13 @@ +#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. + + +#if defined(_M_ARM64) +#define CUSTOM_ACTION_DECORATION(f) L"Wix4" f L"_A64" +#elif defined(_M_AMD64) +#define CUSTOM_ACTION_DECORATION(f) L"Wix4" f L"_X64" +#elif defined(_M_ARM) +#define CUSTOM_ACTION_DECORATION(f) L"Wix4" f L"_ARM" +#else +#define CUSTOM_ACTION_DECORATION(f) L"Wix4" f L"_X86" +#endif diff --git a/src/ext/Iis/ca/dllmain.cpp b/src/ext/Iis/ca/dllmain.cpp new file mode 100644 index 00000000..35ae6d1c --- /dev/null +++ b/src/ext/Iis/ca/dllmain.cpp @@ -0,0 +1,26 @@ +// 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 custom actions + +********************************************************************/ +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; +} diff --git a/src/ext/Iis/ca/iisca.cpp b/src/ext/Iis/ca/iisca.cpp new file mode 100644 index 00000000..37664a1c --- /dev/null +++ b/src/ext/Iis/ca/iisca.cpp @@ -0,0 +1,3 @@ +// 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" diff --git a/src/ext/Iis/ca/iisca.def b/src/ext/Iis/ca/iisca.def new file mode 100644 index 00000000..b9127da6 --- /dev/null +++ b/src/ext/Iis/ca/iisca.def @@ -0,0 +1,30 @@ +; 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. + + +LIBRARY "iisca" + +EXPORTS +;scacert.cpp + InstallCertificates + UninstallCertificates +;scacertexec.cpp + AddUserCertificate + AddMachineCertificate + DeleteUserCertificate + DeleteMachineCertificate +;scaexec.cpp + StartMetabaseTransaction + RollbackMetabaseTransaction + CommitMetabaseTransaction + + WriteMetabaseChanges + + StartIIS7ConfigTransaction + RollbackIIS7ConfigTransaction + CommitIIS7ConfigTransaction + + WriteIIS7ConfigChanges +;scasched.cpp + ConfigureIIs + ConfigureIIsExec + ConfigureIIs7Exec diff --git a/src/ext/Iis/ca/iisca.vcxproj b/src/ext/Iis/ca/iisca.vcxproj new file mode 100644 index 00000000..eb1174be --- /dev/null +++ b/src/ext/Iis/ca/iisca.vcxproj @@ -0,0 +1,147 @@ + + + + + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + Debug + ARM64 + + + Release + ARM64 + + + + + {CB3FB8C4-14BF-4EA6-9F01-7FB258E5AEF3} + DynamicLibrary + iisca + v142 + Unicode + iisca.def + WiX Toolset Iis CustomAction + + + + + + + crypt32.lib;msi.lib;Ws2_32.lib + + + + + Create + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. + + + + + \ No newline at end of file diff --git a/src/ext/Iis/ca/packages.config b/src/ext/Iis/ca/packages.config new file mode 100644 index 00000000..e3dc0e43 --- /dev/null +++ b/src/ext/Iis/ca/packages.config @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/src/ext/Iis/ca/precomp.h b/src/ext/Iis/ca/precomp.h new file mode 100644 index 00000000..85ee0890 --- /dev/null +++ b/src/ext/Iis/ca/precomp.h @@ -0,0 +1,66 @@ +#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. + + +#if _WIN32_MSI < 150 +#define _WIN32_MSI 150 +#endif + +#include +#include +#include +#include + +#include // NetApi32.lib + +#include +#include +#include // IIS 7 config + +#define MAXUINT USHRT_MAX + +#include "wcautil.h" +#include "wcawow64.h" +#include "wcawrapquery.h" + +#include "certutil.h" +#include "cryputil.h" +#include "fileutil.h" +#include "iis7util.h" +#include "memutil.h" +#include "metautil.h" +#include "strutil.h" +#include "userutil.h" +#include "wiutil.h" + +#include "CustomMsiErrors.h" +#include "sca.h" +#include "scacost.h" +#include "scacert.h" +#include "scafilter.h" + +#include "scaiis.h" +#include "scaiis7.h" +#include "scaproperty.h" +#include "scaweb.h" +#include "scawebdir.h" +#include "scawebsvcext.h" +#include "scavdir.h" +#include "scaweb7.h" +#include "scaapppool7.h" +#include "scavdir7.h" +#include "scawebapp7.h" +#include "scawebappext7.h" +#include "scamimemap7.h" +#include "scawebprop7.h" +#include "scaweblog7.h" +#include "scafilter7.h" +#include "scahttpheader7.h" +#include "scaweberr7.h" +#include "scawebsvcext7.h" +#include "scaproperty7.h" +#include "scawebdir7.h" +#include "scassl7.h" +#include "scaexecIIS7.h" + +#include "caDecor.h" diff --git a/src/ext/Iis/ca/sca.h b/src/ext/Iis/ca/sca.h new file mode 100644 index 00000000..64567dcb --- /dev/null +++ b/src/ext/Iis/ca/sca.h @@ -0,0 +1,124 @@ +#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 MAGIC_MULTISZ_CHAR 127 + +// Generic action enum. +enum SCA_ACTION +{ + SCA_ACTION_NONE, + SCA_ACTION_INSTALL, + SCA_ACTION_UNINSTALL +}; + + +// IIS Metabase actions +enum METABASE_ACTION +{ + MBA_UNKNOWNACTION = 0, + MBA_CREATEKEY, + MBA_DELETEKEY, + MBA_WRITEVALUE, + MBA_DELETEVALUE, + MBA_CREATEAPP, + MBA_DELETEAPP, +}; + +// IIS 7 Config actions +enum IIS_CONFIG_ACTION +{ + IIS_CREATE, + IIS_DELETE, + IIS_SITE, + IIS_APPLICATION, + IIS_APPPOOL, + IIS_APPPOOL_RECYCLE_MIN, + IIS_APPPOOL_RECYCLE_REQ, + IIS_APPPOOL_RECYCLE_TIMES, + IIS_APPPOOL_RECYCLE_VIRMEM, + IIS_APPPOOL_RECYCLE_PRIVMEM, + IIS_APPPOOL_RECYCLE_IDLTIMEOUT, + IIS_APPPOOL_RECYCLE_QUEUELIMIT, + IIS_APPPOOL_RECYCLE_CPU_PCT, + IIS_APPPOOL_RECYCLE_CPU_REFRESH, + IIS_APPPOOL_RECYCLE_CPU_ACTION, + IIS_APPPOOL_MAXPROCESS, + IIS_APPPOOL_IDENTITY, + IIS_APPPOOL_USER, + IIS_APPPOOL_PWD, + IIS_APPPOOL_32BIT, + IIS_APPPOOL_MANAGED_PIPELINE_MODE, + IIS_APPPOOL_MANAGED_RUNTIME_VERSION, + IIS_APPPOOL_END, + IIS_APPEXT_BEGIN, + IIS_APPEXT, + IIS_APPEXT_END, + IIS_VDIR, + IIS_BINDING, + IIS_MIMEMAP_BEGIN, + IIS_MIMEMAP, + IIS_MIMEMAP_END, + IIS_DIRPROP_BEGIN, + IIS_DIRPROP_ACCESS, + IIS_DIRPROP_AUTH, + IIS_DIRPROP_USER, + IIS_DIRPROP_PWD, + IIS_DIRPROP_PWDCTRL, + IIS_DIRPROP_LOG, + IIS_DIRPROP_DEFDOCS, + IIS_DIRPROP_SSLFLAGS, + IIS_DIRPROP_AUTHPROVID, + IIS_DIRPROP_ASPERROR, + IIS_DIRPROP_HTTPEXPIRES, + IIS_DIRPROP_MAXAGE, + IIS_DIRPROP_CACHECUST, + IIS_DIRPROP_NOCUSTERROR, + IIS_DIRPROP_LOGVISITS, + IIS_DIRPROP_END, + IIS_WEBLOG, + IIS_FILTER_BEGIN, + IIS_FILTER_GLOBAL_BEGIN, + IIS_FILTER, + IIS_FILTER_END, + IIS_HTTP_HEADER_BEGIN, + IIS_HTTP_HEADER, + IIS_HTTP_HEADER_END, + IIS_WEBERROR_BEGIN, + IIS_WEBERROR, + IIS_WEBERROR_END, + IIS_WEB_SVC_EXT, + IIS_PROPERTY, + IIS_PROPERTY_MAXBAND, + IIS_PROPERTY_LOGUTF8, + IIS_WEBDIR, + IIS_ASP_BEGIN, + IIS_ASP_SESSIONSTATE, + IIS_ASP_SESSIONTIMEOUT, + IIS_ASP_BUFFER, + IIS_ASP_PARENTPATHS, + IIS_ASP_SCRIPTLANG, + IIS_ASP_SCRIPTTIMEOUT, + IIS_ASP_SCRIPTSERVERDEBUG, + IIS_ASP_SCRIPTCLIENTDEBUG, + IIS_ASP_END, + IIS_SSL_BINDING +}; + + +// user creation attributes definitions +enum SCAU_ATTRIBUTES +{ + SCAU_DONT_EXPIRE_PASSWRD = 0x00000001, + SCAU_PASSWD_CANT_CHANGE = 0x00000002, + SCAU_PASSWD_CHANGE_REQD_ON_LOGIN = 0x00000004, + SCAU_DISABLE_ACCOUNT = 0x00000008, + SCAU_FAIL_IF_EXISTS = 0x00000010, + SCAU_UPDATE_IF_EXISTS = 0x00000020, + SCAU_ALLOW_LOGON_AS_SERVICE = 0x00000040, + SCAU_ALLOW_LOGON_AS_BATCH = 0x00000080, + + SCAU_DONT_REMOVE_ON_UNINSTALL = 0x00000100, + SCAU_DONT_CREATE_USER = 0x00000200, + SCAU_NON_VITAL = 0x00000400, +}; diff --git a/src/ext/Iis/ca/scaapppool.cpp b/src/ext/Iis/ca/scaapppool.cpp new file mode 100644 index 00000000..781c55ca --- /dev/null +++ b/src/ext/Iis/ca/scaapppool.cpp @@ -0,0 +1,594 @@ +// 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" + +/*------------------------------------------------------------------ +AppPool table: + +Column Type Nullable Example Value +AppPool s72 No TestPool +Name s72 No "TestPool" +Component_ s72 No ComponentName +Attributes i2 No 8 (APATTR_OTHERUSER) +User_ s72 Yes UserKey +RecycleMinutes i2 Yes 500 +RecycleRequests i2 Yes 5000 +RecycleTimes s72 Yes "1:45,13:30,22:00" +IdleTimeout i2 Yes 15 +QueueLimit i2 Yes 500 +CPUMon s72 Yes "65,500,1" (65% CPU usage, 500 minutes, Shutdown Action) +MaxProc i2 Yes 5 +ManagedRuntimeVersion s72 Yes "v2.0" +ManagedPipelineMode s72 Yes "Integrated" + +Notes: +RecycleTimes is a comma delimeted list of times. CPUMon is a +comma delimeted list of the following format: +,,. The values for +Action are 1 (Shutdown) and 0 (No Action). + +------------------------------------------------------------------*/ + +enum eAppPoolQuery { apqAppPool = 1, apqName, apqComponent, apqAttributes, apqUser, apqRecycleMinutes, apqRecycleRequests, apqRecycleTimes, apqVirtualMemory, apqPrivateMemory, apqIdleTimeout, apqQueueLimit, apqCpuMon, apqMaxProc, apqManagedRuntimeVersion, apqManagedPipelineMode, apqInstalled, apqAction }; + +enum eComponentAttrQuery { caqComponent = 1, caqAttributes }; + +// prototypes +static HRESULT AppPoolExists( + __in IMSAdminBase* piMetabase, + __in LPCWSTR wzAppPool + ); + +// functions + +void ScaAppPoolFreeList( + __in SCA_APPPOOL* psapList + ) +{ + SCA_APPPOOL* psapDelete = psapList; + while (psapList) + { + psapDelete = psapList; + psapList = psapList->psapNext; + + MemFree(psapDelete); + } +} + + +HRESULT ScaAppPoolRead( + __inout SCA_APPPOOL** ppsapList, + __in WCA_WRAPQUERY_HANDLE hUserQuery, + __inout LPWSTR *ppwzCustomActionData + ) +{ + Assert(ppsapList); + + HRESULT hr = S_OK; + + MSIHANDLE hRec, hRecComp; + LPWSTR pwzData = NULL; + SCA_APPPOOL* psap = NULL; + WCA_WRAPQUERY_HANDLE hAppPoolQuery = NULL; + WCA_WRAPQUERY_HANDLE hComponentQuery = NULL; + + hr = WcaBeginUnwrapQuery(&hAppPoolQuery, ppwzCustomActionData); + ExitOnFailure(hr, "Failed to unwrap query for ScaAppPoolRead"); + + if (0 == WcaGetQueryRecords(hAppPoolQuery)) + { + WcaLog(LOGMSG_VERBOSE, "Skipping ScaAppPoolRead() - required table not present"); + ExitFunction1(hr = S_FALSE); + } + + hr = WcaBeginUnwrapQuery(&hComponentQuery, ppwzCustomActionData); + ExitOnFailure(hr, "Failed to unwrap query for ScaAppPoolRead"); + + // loop through all the AppPools + while (S_OK == (hr = WcaFetchWrappedRecord(hAppPoolQuery, &hRec))) + { + // Add this record's information into the list of things to process. + hr = AddAppPoolToList(ppsapList); + ExitOnFailure(hr, "failed to add app pool to app pool list"); + + psap = *ppsapList; + + hr = WcaGetRecordString(hRec, apqComponent, &pwzData); + ExitOnFailure(hr, "failed to get AppPool.Component"); + + if (pwzData && *pwzData) + { + psap->fHasComponent = TRUE; + + hr = ::StringCchCopyW(psap->wzComponent, countof(psap->wzComponent), pwzData); + ExitOnFailure(hr, "failed to copy component name: %ls", pwzData); + + hr = WcaGetRecordInteger(hRec, apqInstalled, (int *)&psap->isInstalled); + ExitOnFailure(hr, "Failed to get Component installed state for app pool"); + + hr = WcaGetRecordInteger(hRec, apqAction, (int *)&psap->isAction); + ExitOnFailure(hr, "Failed to get Component action state for app pool"); + + WcaFetchWrappedReset(hComponentQuery); + hr = WcaFetchWrappedRecordWhereString(hComponentQuery, caqComponent, psap->wzComponent, &hRecComp); + ExitOnFailure(hr, "Failed to fetch Component.Attributes for Component '%ls'", psap->wzComponent); + + hr = WcaGetRecordInteger(hRecComp, caqAttributes, &psap->iCompAttributes); + ExitOnFailure(hr, "failed to get Component.Attributes"); + } + + hr = WcaGetRecordString(hRec, apqAppPool, &pwzData); + ExitOnFailure(hr, "failed to get AppPool.AppPool"); + hr = ::StringCchCopyW(psap->wzAppPool, countof(psap->wzAppPool), pwzData); + ExitOnFailure(hr, "failed to copy AppPool name: %ls", pwzData); + + hr = WcaGetRecordString(hRec, apqName, &pwzData); + ExitOnFailure(hr, "failed to get AppPool.Name"); + hr = ::StringCchCopyW(psap->wzName, countof(psap->wzName), pwzData); + ExitOnFailure(hr, "failed to copy app pool name: %ls", pwzData); + hr = ::StringCchPrintfW(psap->wzKey, countof(psap->wzKey), L"/LM/W3SVC/AppPools/%s", pwzData); + ExitOnFailure(hr, "failed to format app pool key name"); + + hr = WcaGetRecordInteger(hRec, apqAttributes, &psap->iAttributes); + ExitOnFailure(hr, "failed to get AppPool.Attributes"); + + hr = WcaGetRecordString(hRec, apqUser, &pwzData); + ExitOnFailure(hr, "failed to get AppPool.User"); + hr = ScaGetUserDeferred(pwzData, hUserQuery, &psap->suUser); + ExitOnFailure(hr, "failed to get user: %ls", pwzData); + + hr = WcaGetRecordInteger(hRec, apqRecycleRequests, &psap->iRecycleRequests); + ExitOnFailure(hr, "failed to get AppPool.RecycleRequests"); + + hr = WcaGetRecordInteger(hRec, apqRecycleMinutes, &psap->iRecycleMinutes); + ExitOnFailure(hr, "failed to get AppPool.Minutes"); + + hr = WcaGetRecordString(hRec, apqRecycleTimes, &pwzData); + ExitOnFailure(hr, "failed to get AppPool.RecycleTimes"); + hr = ::StringCchCopyW(psap->wzRecycleTimes, countof(psap->wzRecycleTimes), pwzData); + ExitOnFailure(hr, "failed to copy recycle value: %ls", pwzData); + + hr = WcaGetRecordInteger(hRec, apqVirtualMemory, &psap->iVirtualMemory); + ExitOnFailure(hr, "failed to get AppPool.VirtualMemory"); + + hr = WcaGetRecordInteger(hRec, apqPrivateMemory, &psap->iPrivateMemory); + ExitOnFailure(hr, "failed to get AppPool.PrivateMemory"); + + hr = WcaGetRecordInteger(hRec, apqIdleTimeout, &psap->iIdleTimeout); + ExitOnFailure(hr, "failed to get AppPool.IdleTimeout"); + + hr = WcaGetRecordInteger(hRec, apqQueueLimit, &psap->iQueueLimit); + ExitOnFailure(hr, "failed to get AppPool.QueueLimit"); + + hr = WcaGetRecordString(hRec, apqCpuMon, &pwzData); + ExitOnFailure(hr, "failed to get AppPool.CPUMon"); + hr = ::StringCchCopyW(psap->wzCpuMon, countof(psap->wzCpuMon), pwzData); + ExitOnFailure(hr, "failed to copy cpu monitor value: %ls", pwzData); + + hr = WcaGetRecordInteger(hRec, apqMaxProc, &psap->iMaxProcesses); + ExitOnFailure(hr, "failed to get AppPool.MaxProc"); + + hr = WcaGetRecordString(hRec, apqManagedRuntimeVersion, &pwzData); + ExitOnFailure(hr, "failed to get AppPool.ManagedRuntimeVersion"); + hr = ::StringCchCopyW(psap->wzManagedRuntimeVersion, countof(psap->wzManagedRuntimeVersion), pwzData); + ExitOnFailure(hr, "failed to copy ManagedRuntimeVersion value: %ls", pwzData); + + hr = WcaGetRecordString(hRec, apqManagedPipelineMode, &pwzData); + ExitOnFailure(hr, "failed to get AppPool.ManagedPipelineMode"); + hr = ::StringCchCopyW(psap->wzManagedPipelineMode, countof(psap->wzManagedPipelineMode), pwzData); + ExitOnFailure(hr, "failed to copy ManagedPipelineMode value: %ls", pwzData); + + } + + if (E_NOMOREITEMS == hr) + { + hr = S_OK; + } + ExitOnFailure(hr, "failure while processing AppPools"); + +LExit: + WcaFinishUnwrapQuery(hAppPoolQuery); + WcaFinishUnwrapQuery(hComponentQuery); + + ReleaseStr(pwzData); + return hr; +} + + +HRESULT ScaFindAppPool( + __in IMSAdminBase* piMetabase, + __in LPCWSTR wzAppPool, + __out_ecount(cchName) LPWSTR wzName, + __in DWORD cchName, + __in SCA_APPPOOL *psapList + ) +{ + Assert(piMetabase && wzAppPool && *wzAppPool && wzName && *wzName); + + HRESULT hr = S_OK; + + // check memory first + SCA_APPPOOL* psap = psapList; + for (; psap; psap = psap->psapNext) + { + if (0 == lstrcmpW(psap->wzAppPool, wzAppPool)) + { + break; + } + } + ExitOnNull(psap, hr, HRESULT_FROM_WIN32(ERROR_NOT_FOUND), "Could not find the app pool: %ls", wzAppPool); + + // copy the web app pool name + hr = ::StringCchCopyW(wzName, cchName, psap->wzName); + ExitOnFailure(hr, "failed to copy app pool name while finding app pool: %ls", psap->wzName); + + // if it's not being installed now, check if it exists already + if (!psap->fHasComponent) + { + hr = AppPoolExists(piMetabase, psap->wzName); + ExitOnFailure(hr, "failed to check for existence of app pool: %ls", psap->wzName); + } + +LExit: + return hr; +} + + +static HRESULT AppPoolExists( + __in IMSAdminBase* piMetabase, + __in LPCWSTR wzAppPool + ) +{ + Assert(piMetabase && wzAppPool && *wzAppPool); + + HRESULT hr = S_OK; + WCHAR wzSubKey[METADATA_MAX_NAME_LEN]; + + for (DWORD dwIndex = 0; SUCCEEDED(hr); ++dwIndex) + { + hr = piMetabase->EnumKeys(METADATA_MASTER_ROOT_HANDLE, L"/LM/W3SVC/AppPools", wzSubKey, dwIndex); + if (SUCCEEDED(hr) && 0 == lstrcmpW(wzSubKey, wzAppPool)) + { + hr = S_OK; + break; + } + } + + if (E_NOMOREITEMS == hr || HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND) == hr || HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND) == hr) + { + hr = S_FALSE; + } + + return hr; +} + + +HRESULT ScaAppPoolInstall( + __in IMSAdminBase* piMetabase, + __in SCA_APPPOOL* psapList + ) +{ + Assert(piMetabase); + + HRESULT hr = S_OK; + + for (SCA_APPPOOL* psap = psapList; psap; psap = psap->psapNext) + { + // if we are installing the app pool + if (psap->fHasComponent && WcaIsInstalling(psap->isInstalled, psap->isAction)) + { + hr = ScaWriteAppPool(piMetabase, psap); + ExitOnFailure(hr, "failed to write AppPool '%ls' to metabase", psap->wzAppPool); + } + } + +LExit: + return hr; +} + + +HRESULT ScaAppPoolUninstall( + __in IMSAdminBase* piMetabase, + __in SCA_APPPOOL* psapList + ) +{ + Assert(piMetabase); + + HRESULT hr = S_OK; + + for (SCA_APPPOOL* psap = psapList; psap; psap = psap->psapNext) + { + // if we are uninstalling the app pool + if (psap->fHasComponent && WcaIsUninstalling(psap->isInstalled, psap->isAction)) + { + hr = ScaRemoveAppPool(piMetabase, psap); + ExitOnFailure(hr, "Failed to remove AppPool '%ls' from metabase", psap->wzAppPool); + } + } + +LExit: + return hr; +} + + +HRESULT ScaWriteAppPool( + __in IMSAdminBase* piMetabase, + __in SCA_APPPOOL* psap + ) +{ + Assert(piMetabase && psap); + + HRESULT hr = S_OK; + DWORD dwIdentity = 0xFFFFFFFF; + BOOL fExists = FALSE; + LPWSTR pwzValue = NULL; + LPWSTR wz = NULL; + + hr = AppPoolExists(piMetabase, psap->wzName); + ExitOnFailure(hr, "failed to check if app pool already exists"); + if (S_FALSE == hr) + { + // didn't find the AppPool key, so we need to create it + hr = ScaCreateMetabaseKey(piMetabase, psap->wzKey, L""); + ExitOnFailure(hr, "failed to create AppPool key: %ls", psap->wzKey); + + // mark it as an AppPool + hr = ScaWriteMetabaseValue(piMetabase, psap->wzKey, NULL, MD_KEY_TYPE, METADATA_NO_ATTRIBUTES, IIS_MD_UT_SERVER, STRING_METADATA, (LPVOID)L"IIsApplicationPool"); + ExitOnFailure(hr, "failed to mark key as AppPool key: %ls", psap->wzKey); + + // TODO: Make this an Attribute? + // set autostart value + hr = ScaWriteMetabaseValue(piMetabase, psap->wzKey, NULL, MD_APPPOOL_AUTO_START, METADATA_NO_ATTRIBUTES, IIS_MD_UT_SERVER, DWORD_METADATA, (LPVOID)1); + ExitOnFailure(hr, "failed to mark key as AppPool key: %ls", psap->wzKey); + } + else + { + fExists = TRUE; + } + + // + // Set the AppPool Recycling Tab + // + if (MSI_NULL_INTEGER != psap->iRecycleMinutes) + { + hr = ScaWriteMetabaseValue(piMetabase, psap->wzKey, NULL, MD_APPPOOL_PERIODIC_RESTART_TIME, METADATA_INHERIT, IIS_MD_UT_SERVER, DWORD_METADATA, (LPVOID)((DWORD_PTR)psap->iRecycleMinutes)); + ExitOnFailure(hr, "failed to set periodic restart time"); + } + + if (MSI_NULL_INTEGER != psap->iRecycleRequests) + { + hr = ScaWriteMetabaseValue(piMetabase, psap->wzKey, NULL, MD_APPPOOL_PERIODIC_RESTART_REQUEST_COUNT, METADATA_INHERIT, IIS_MD_UT_SERVER, DWORD_METADATA, (LPVOID)((DWORD_PTR)psap->iRecycleRequests)); + ExitOnFailure(hr, "failed to set periodic restart request count"); + } + + if (*psap->wzRecycleTimes) + { + // Add another NULL' onto pwz since it's a 'MULTISZ' + hr = StrAllocString(&pwzValue, psap->wzRecycleTimes, 0); + ExitOnFailure(hr, "failed to allocate string for MULTISZ"); + hr = StrAllocConcat(&pwzValue, L"\0", 1); + ExitOnFailure(hr, "failed to add second null to RecycleTime multisz"); + + // Replace the commas with NULLs + wz = pwzValue; + while (NULL != (wz = wcschr(wz, L','))) + { + *wz = L'\0'; + ++wz; + } + + hr = ScaWriteMetabaseValue(piMetabase, psap->wzKey, NULL, MD_APPPOOL_PERIODIC_RESTART_SCHEDULE, METADATA_INHERIT, IIS_MD_UT_SERVER, MULTISZ_METADATA, (LPVOID)pwzValue); + ExitOnFailure(hr, "failed to set periodic restart schedule"); + } + + if (MSI_NULL_INTEGER != psap->iVirtualMemory) + { + hr = ScaWriteMetabaseValue(piMetabase, psap->wzKey, NULL, MD_APPPOOL_PERIODIC_RESTART_MEMORY, METADATA_INHERIT, IIS_MD_UT_SERVER, DWORD_METADATA, (LPVOID)((DWORD_PTR)psap->iVirtualMemory)); + ExitOnFailure(hr, "failed to set periodic restart memory count"); + } + + if (MSI_NULL_INTEGER != psap->iPrivateMemory) + { + hr = ScaWriteMetabaseValue(piMetabase, psap->wzKey, NULL, MD_APPPOOL_PERIODIC_RESTART_PRIVATE_MEMORY, METADATA_INHERIT, IIS_MD_UT_SERVER, DWORD_METADATA, (LPVOID)((DWORD_PTR)psap->iPrivateMemory)); + ExitOnFailure(hr, "failed to set periodic restart private memory count"); + } + + + // + // Set AppPool Performance Tab + // + if (MSI_NULL_INTEGER != psap->iIdleTimeout) + { + hr = ScaWriteMetabaseValue(piMetabase, psap->wzKey, NULL, MD_APPPOOL_IDLE_TIMEOUT, METADATA_INHERIT, IIS_MD_UT_SERVER, DWORD_METADATA, (LPVOID)((DWORD_PTR)psap->iIdleTimeout)); + ExitOnFailure(hr, "failed to set idle timeout value"); + } + + if (MSI_NULL_INTEGER != psap->iQueueLimit) + { + hr = ScaWriteMetabaseValue(piMetabase, psap->wzKey, NULL, MD_APPPOOL_UL_APPPOOL_QUEUE_LENGTH, METADATA_INHERIT, IIS_MD_UT_SERVER, DWORD_METADATA, (LPVOID)((DWORD_PTR)psap->iQueueLimit)); + ExitOnFailure(hr, "failed to set request queue limit value"); + } + + if (*psap->wzCpuMon) + { + hr = StrAllocString(&pwzValue, psap->wzCpuMon, 0); + ExitOnFailure(hr, "failed to allocate CPUMonitor string"); + + DWORD dwPercent = 0; + DWORD dwRefreshMinutes = 0; + DWORD dwAction = 0; + + dwPercent = wcstoul(pwzValue, &wz, 10); + if (100 < dwPercent) + { + ExitOnFailure(hr = E_INVALIDARG, "invalid maximum cpu percentage value: %d", dwPercent); + } + if (wz && L',' == *wz) + { + ++wz; + dwRefreshMinutes = wcstoul(wz, &wz, 10); + if (wz && L',' == *wz) + { + ++wz; + dwAction = wcstoul(wz, &wz, 10); + } + } + + if (dwPercent) + { + hr = ScaWriteMetabaseValue(piMetabase, psap->wzKey, NULL, MD_CPU_LIMIT, METADATA_INHERIT, IIS_MD_UT_SERVER, DWORD_METADATA, (LPVOID)((DWORD_PTR)(dwPercent * 1000))); + ExitOnFailure(hr, "failed to set CPU percentage max"); + } + if (dwRefreshMinutes) + { + hr = ScaWriteMetabaseValue(piMetabase, psap->wzKey, NULL, MD_CPU_RESET_INTERVAL, METADATA_INHERIT, IIS_MD_UT_SERVER, DWORD_METADATA, (LPVOID)((DWORD_PTR)dwRefreshMinutes)); + ExitOnFailure(hr, "failed to set refresh CPU minutes"); + } + if (dwAction) + { + // 0 = No Action + // 1 = Shutdown + hr = ScaWriteMetabaseValue(piMetabase, psap->wzKey, NULL, MD_CPU_ACTION, METADATA_INHERIT, IIS_MD_UT_SERVER, DWORD_METADATA, (LPVOID)((DWORD_PTR)dwAction)); + ExitOnFailure(hr, "failed to set CPU action"); + } + } + + if (MSI_NULL_INTEGER != psap->iMaxProcesses) + { + hr = ScaWriteMetabaseValue(piMetabase, psap->wzKey, NULL, MD_APPPOOL_MAX_PROCESS_COUNT, METADATA_INHERIT, IIS_MD_UT_SERVER, DWORD_METADATA, (LPVOID)((DWORD_PTR)psap->iMaxProcesses)); + ExitOnFailure(hr, "failed to set web garden maximum worker processes"); + } + + // TODO: Health Tab if anyone wants it? + + // + // Set the AppPool Identity tab + // + if (psap->iAttributes & APATTR_NETSERVICE) + { + dwIdentity = MD_APPPOOL_IDENTITY_TYPE_NETWORKSERVICE; + } + else if (psap->iAttributes & APATTR_LOCSERVICE) + { + dwIdentity = MD_APPPOOL_IDENTITY_TYPE_LOCALSERVICE; + } + else if (psap->iAttributes & APATTR_LOCSYSTEM) + { + dwIdentity = MD_APPPOOL_IDENTITY_TYPE_LOCALSYSTEM; + } + else if (psap->iAttributes & APATTR_OTHERUSER) + { + if (!*psap->suUser.wzDomain || CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, psap->suUser.wzDomain, -1, L".", -1)) + { + if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, psap->suUser.wzName, -1, L"NetworkService", -1)) + { + dwIdentity = MD_APPPOOL_IDENTITY_TYPE_NETWORKSERVICE; + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, psap->suUser.wzName, -1, L"LocalService", -1)) + { + dwIdentity = MD_APPPOOL_IDENTITY_TYPE_LOCALSERVICE; + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, psap->suUser.wzName, -1, L"LocalSystem", -1)) + { + dwIdentity = MD_APPPOOL_IDENTITY_TYPE_LOCALSYSTEM; + } + else + { + dwIdentity = MD_APPPOOL_IDENTITY_TYPE_SPECIFICUSER; + } + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, psap->suUser.wzDomain, -1, L"NT AUTHORITY", -1)) + { + if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, psap->suUser.wzName, -1, L"NETWORK SERVICE", -1)) + { + dwIdentity = MD_APPPOOL_IDENTITY_TYPE_NETWORKSERVICE; + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, psap->suUser.wzName, -1, L"SERVICE", -1)) + { + dwIdentity = MD_APPPOOL_IDENTITY_TYPE_LOCALSERVICE; + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, psap->suUser.wzName, -1, L"SYSTEM", -1)) + { + dwIdentity = MD_APPPOOL_IDENTITY_TYPE_LOCALSYSTEM; + } + else + { + dwIdentity = MD_APPPOOL_IDENTITY_TYPE_SPECIFICUSER; + } + } + else + { + dwIdentity = MD_APPPOOL_IDENTITY_TYPE_SPECIFICUSER; + } + } + + if (-1 != dwIdentity) + { + hr = ScaWriteMetabaseValue(piMetabase, psap->wzKey, NULL, MD_APPPOOL_IDENTITY_TYPE, METADATA_INHERIT , IIS_MD_UT_SERVER, DWORD_METADATA, (LPVOID)((DWORD_PTR)dwIdentity)); + ExitOnFailure(hr, "failed to set app pool identity"); + + if (MD_APPPOOL_IDENTITY_TYPE_SPECIFICUSER == dwIdentity) + { + if (*psap->suUser.wzDomain) + { + hr = StrAllocFormatted(&pwzValue, L"%s\\%s", psap->suUser.wzDomain, psap->suUser.wzName); + ExitOnFailure(hr, "failed to format user name: %ls domain: %ls", psap->suUser.wzName, psap->suUser.wzDomain); + } + else + { + hr = StrAllocFormatted(&pwzValue, L"%s", psap->suUser.wzName); + ExitOnFailure(hr, "failed to format user name: %ls", psap->suUser.wzName); + } + + hr = ScaWriteMetabaseValue(piMetabase, psap->wzKey, NULL, MD_WAM_USER_NAME, METADATA_INHERIT , IIS_MD_UT_FILE, STRING_METADATA, (LPVOID)pwzValue); + ExitOnFailure(hr, "failed to set app pool identity name"); + + hr = ScaWriteMetabaseValue(piMetabase, psap->wzKey, NULL, MD_WAM_PWD, METADATA_INHERIT | METADATA_SECURE, IIS_MD_UT_FILE, STRING_METADATA, (LPVOID)psap->suUser.wzPassword); + ExitOnFailure(hr, "failed to set app pool identity password"); + } + } + +LExit: + ReleaseStr(pwzValue); + + return hr; +} + + +HRESULT ScaRemoveAppPool( + __in IMSAdminBase* piMetabase, + __in SCA_APPPOOL* psap + ) +{ + Assert(piMetabase && psap); + + HRESULT hr = S_OK; + + // simply remove the root key and everything else is pulled at the same time + if (0 != lstrlenW(psap->wzKey)) + { + hr = ScaDeleteMetabaseKey(piMetabase, psap->wzKey, L""); + ExitOnFailure(hr, "failed to delete AppPool key: %ls", psap->wzKey); + } + + // TODO: Maybe check to make sure any web sites that are using this AppPool are put back in the 'DefaultAppPool' + +LExit: + return hr; +} + + +HRESULT AddAppPoolToList( + __in SCA_APPPOOL** ppsapList + ) +{ + HRESULT hr = S_OK; + SCA_APPPOOL* psap = static_cast(MemAlloc(sizeof(SCA_APPPOOL), TRUE)); + ExitOnNull(psap, hr, E_OUTOFMEMORY, "failed to allocate memory for new element in app pool list"); + + psap->psapNext = *ppsapList; + *ppsapList = psap; + +LExit: + return hr; +} diff --git a/src/ext/Iis/ca/scaapppool.h b/src/ext/Iis/ca/scaapppool.h new file mode 100644 index 00000000..68575fcb --- /dev/null +++ b/src/ext/Iis/ca/scaapppool.h @@ -0,0 +1,88 @@ +#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. + + +#include "scauser.h" + +// Identity +#define APATTR_NETSERVICE 0x0001 // Network Service +#define APATTR_LOCSERVICE 0x0002 // Local Service +#define APATTR_LOCSYSTEM 0x0004 // Local System +#define APATTR_OTHERUSER 0x0008 // Other User + +struct SCA_APPPOOL +{ + // iis app pool configuation information + WCHAR wzAppPool[MAX_DARWIN_KEY + 1]; + WCHAR wzName[METADATA_MAX_NAME_LEN + 1]; + WCHAR wzKey[METADATA_MAX_NAME_LEN + 1]; + WCHAR wzComponent[METADATA_MAX_NAME_LEN + 1]; + BOOL fHasComponent; + INSTALLSTATE isInstalled; + INSTALLSTATE isAction; + INT iAttributes; + + SCA_USER suUser; + + INT iRecycleRequests; + INT iRecycleMinutes; + WCHAR wzRecycleTimes[MAX_DARWIN_KEY + 1]; + INT iVirtualMemory; + INT iPrivateMemory; + + INT iIdleTimeout; + INT iQueueLimit; + WCHAR wzCpuMon[MAX_DARWIN_KEY + 1]; + INT iMaxProcesses; + WCHAR wzManagedPipelineMode[MAX_DARWIN_KEY + 1]; + WCHAR wzManagedRuntimeVersion[MAX_DARWIN_KEY + 1]; + + int iCompAttributes; + + SCA_APPPOOL *psapNext; +}; + + +// prototypes + +HRESULT ScaAppPoolRead( + __inout SCA_APPPOOL** ppsapList, + __in WCA_WRAPQUERY_HANDLE hUserQuery, + __inout LPWSTR *ppwzCustomActionData + ); + +void ScaAppPoolFreeList( + __in SCA_APPPOOL* psapList + ); + +HRESULT ScaFindAppPool( + __in IMSAdminBase* piMetabase, + __in LPCWSTR wzAppPool, + __out_ecount(cchName) LPWSTR wzName, + __in DWORD cchName, + __in SCA_APPPOOL *psapList + ); + +HRESULT ScaAppPoolInstall( + __in IMSAdminBase* piMetabase, + __in SCA_APPPOOL* psapList + ); + +HRESULT ScaAppPoolUninstall( + __in IMSAdminBase* piMetabase, + __in SCA_APPPOOL* psapList + ); + +HRESULT ScaWriteAppPool( + __in IMSAdminBase* piMetabase, + __in SCA_APPPOOL* psap + ); + +HRESULT ScaRemoveAppPool( + __in IMSAdminBase* piMetabase, + __in SCA_APPPOOL* psap + ); + +HRESULT AddAppPoolToList( + __in SCA_APPPOOL** ppsapList + ); diff --git a/src/ext/Iis/ca/scaapppool7.cpp b/src/ext/Iis/ca/scaapppool7.cpp new file mode 100644 index 00000000..0fac4346 --- /dev/null +++ b/src/ext/Iis/ca/scaapppool7.cpp @@ -0,0 +1,401 @@ +// 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" + +// prototypes +static HRESULT AppPoolExists( + __in LPCWSTR wzAppPool + ); + +// functions +HRESULT ScaFindAppPool7( + __in LPCWSTR wzAppPool, + __out_ecount(cchName) LPWSTR wzName, + __in DWORD cchName, + __in SCA_APPPOOL *psapList + ) +{ + Assert(wzAppPool && *wzAppPool && wzName && *wzName); + + HRESULT hr = S_OK; + + // check memory first + SCA_APPPOOL* psap = psapList; + for (; psap; psap = psap->psapNext) + { + if (0 == wcscmp(psap->wzAppPool, wzAppPool)) + { + break; + } + } + ExitOnNull(psap, hr, HRESULT_FROM_WIN32(ERROR_NOT_FOUND), "Could not find the app pool: %ls", wzAppPool); + + // copy the web app pool name +#pragma prefast(suppress:26037, "Source string is null terminated - it is populated as target of ::StringCchCopyW") + hr = ::StringCchCopyW(wzName, cchName, psap->wzName); + ExitOnFailure(hr, "failed to copy app pool name while finding app pool: %ls", psap->wzName); + + // if it's not being installed now, check if it exists already + if (!psap->fHasComponent) + { + hr = AppPoolExists(psap->wzName); + ExitOnFailure(hr, "failed to check for existence of app pool: %ls", psap->wzName); + } + +LExit: + return hr; +} + + +static HRESULT AppPoolExists( + __in LPCWSTR /*wzAppPool*/ + ) +{ + HRESULT hr = S_OK; + + //this function checks for existance of app pool in IIS7 config + //at schedule time, we will defer this to execute time. + + return hr; +} + + +HRESULT ScaAppPoolInstall7( + __in SCA_APPPOOL* psapList + ) +{ + HRESULT hr = S_OK; + + for (SCA_APPPOOL* psap = psapList; psap; psap = psap->psapNext) + { + // if we are installing the app pool + if (psap->fHasComponent && WcaIsInstalling(psap->isInstalled, psap->isAction)) + { + hr = ScaWriteAppPool7(psap); + ExitOnFailure(hr, "failed to write AppPool '%ls' to metabase", psap->wzAppPool); + } + } + +LExit: + return hr; +} + + +HRESULT ScaAppPoolUninstall7( + __in SCA_APPPOOL* psapList + ) +{ + + HRESULT hr = S_OK; + + for (SCA_APPPOOL* psap = psapList; psap; psap = psap->psapNext) + { + // if we are uninstalling the app pool + if (psap->fHasComponent && WcaIsUninstalling(psap->isInstalled, psap->isAction)) + { + hr = ScaRemoveAppPool7(psap); + ExitOnFailure(hr, "Failed to remove AppPool '%ls' from metabase", psap->wzAppPool); + } + } + +LExit: + return hr; +} + + +HRESULT ScaWriteAppPool7( + __in const SCA_APPPOOL* psap + ) +{ + Assert(psap); + + HRESULT hr = S_OK; + DWORD dwIdentity = 0xFFFFFFFF; + LPWSTR pwzValue = NULL; + LPWSTR wz = NULL; + + //create the app pool + hr = ScaWriteConfigID(IIS_APPPOOL); + ExitOnFailure(hr, "failed to write AppPool key."); + + hr = ScaWriteConfigID(IIS_CREATE); + ExitOnFailure(hr, "failed to write AppPool create action."); + + hr = ScaWriteConfigString(psap->wzName); + ExitOnFailure(hr, "failed to write AppPool name: %ls", psap->wzName); + + // Now do all the optional stuff + + // Set the AppPool Recycling Tab + if (MSI_NULL_INTEGER != psap->iRecycleMinutes) + { + hr = ScaWriteConfigID(IIS_APPPOOL_RECYCLE_MIN); + ExitOnFailure(hr, "failed to set periodic restart time id"); + hr = ScaWriteConfigInteger(psap->iRecycleMinutes); + ExitOnFailure(hr, "failed to set periodic restart time"); + } + + if (MSI_NULL_INTEGER != psap->iRecycleRequests) + { + hr = ScaWriteConfigID(IIS_APPPOOL_RECYCLE_REQ); + ExitOnFailure(hr, "failed to set periodic restart request count id"); + hr = ScaWriteConfigInteger(psap->iRecycleRequests); + ExitOnFailure(hr, "failed to set periodic restart request count"); + } + + if (*psap->wzRecycleTimes) + { + hr = ScaWriteConfigID(IIS_APPPOOL_RECYCLE_TIMES); + ExitOnFailure(hr, "failed to set periodic restart schedule id"); + hr = ScaWriteConfigString(psap->wzRecycleTimes); + ExitOnFailure(hr, "failed to set periodic restart schedule"); + } + + if (MSI_NULL_INTEGER != psap->iVirtualMemory) + { + hr = ScaWriteConfigID(IIS_APPPOOL_RECYCLE_VIRMEM); + ExitOnFailure(hr, "failed to set periodic restart memory count id"); + hr = ScaWriteConfigInteger(psap->iVirtualMemory); + ExitOnFailure(hr, "failed to set periodic restart memory count"); + } + + if (MSI_NULL_INTEGER != psap->iPrivateMemory) + { + hr = ScaWriteConfigID(IIS_APPPOOL_RECYCLE_PRIVMEM); + ExitOnFailure(hr, "failed to set periodic restart private memory count id"); + hr = ScaWriteConfigInteger(psap->iPrivateMemory); + ExitOnFailure(hr, "failed to set periodic restart private memory count"); + } + + // Set AppPool Performance Tab + if (MSI_NULL_INTEGER != psap->iIdleTimeout) + { + hr = ScaWriteConfigID(IIS_APPPOOL_RECYCLE_IDLTIMEOUT); + ExitOnFailure(hr, "failed to set idle timeout value id"); + hr = ScaWriteConfigInteger(psap->iIdleTimeout); + ExitOnFailure(hr, "failed to set idle timeout value"); + } + + if (MSI_NULL_INTEGER != psap->iQueueLimit) + { + hr = ScaWriteConfigID(IIS_APPPOOL_RECYCLE_QUEUELIMIT); + ExitOnFailure(hr, "failed to set request queue limit value id"); + hr = ScaWriteConfigInteger(psap->iQueueLimit); + ExitOnFailure(hr, "failed to set request queue limit value"); + } + if (*psap->wzCpuMon) + { +#pragma prefast(suppress:26037, "Source string is null terminated - it is populated as target of ::StringCchCopyW") + hr = ::StrAllocString(&pwzValue, psap->wzCpuMon, 0); + ExitOnFailure(hr, "failed to allocate CPUMonitor string"); + + DWORD dwPercent = 0; + DWORD dwRefreshMinutes = 0; + DWORD dwAction = 0; + + dwPercent = wcstoul(pwzValue, &wz, 10); + if (100 < dwPercent) + { + ExitOnFailure(hr = E_INVALIDARG, "invalid maximum cpu percentage value: %d", dwPercent); + } + if (wz && L',' == *wz) + { + ++wz; + dwRefreshMinutes = wcstoul(wz, &wz, 10); + if (wz && L',' == *wz) + { + ++wz; + dwAction = wcstoul(wz, &wz, 10); + } + } + if (dwPercent) + { + hr = ScaWriteConfigID(IIS_APPPOOL_RECYCLE_CPU_PCT); + ExitOnFailure(hr, "failed to set recycle pct id"); + hr = ScaWriteConfigInteger(dwPercent); + ExitOnFailure(hr, "failed to set CPU percentage max"); + } + if (dwRefreshMinutes) + { + hr = ScaWriteConfigID(IIS_APPPOOL_RECYCLE_CPU_REFRESH); + ExitOnFailure(hr, "failed to set recycle refresh id"); + hr = ScaWriteConfigInteger(dwRefreshMinutes); + ExitOnFailure(hr, "failed to set refresh CPU minutes"); + } + if (dwAction) + { + // 0 = No Action + // 1 = Shutdown + hr = ScaWriteConfigID(IIS_APPPOOL_RECYCLE_CPU_ACTION); + ExitOnFailure(hr, "failed to set recycle refresh id"); + hr = ScaWriteConfigInteger(dwAction); + ExitOnFailure(hr, "failed to set CPU action"); + } + } + + if (MSI_NULL_INTEGER != psap->iMaxProcesses) + { + hr = ScaWriteConfigID(IIS_APPPOOL_MAXPROCESS); + ExitOnFailure(hr, "Failed to write max processes config ID"); + + hr = ScaWriteConfigInteger(psap->iMaxProcesses); + ExitOnFailure(hr, "failed to set web garden maximum worker processes"); + } + + hr = ScaWriteConfigID(IIS_APPPOOL_32BIT); + ExitOnFailure(hr, "Failed to write 32 bit app pool config ID"); + hr = ScaWriteConfigInteger(psap->iCompAttributes & msidbComponentAttributes64bit ? 0 : 1); + ExitOnFailure(hr, "Failed to write 32 bit app pool config value"); + + // + // Set the AppPool Identity tab + // + if (psap->iAttributes & APATTR_APPPOOLIDENTITY) + { + dwIdentity = 4; + } + else if (psap->iAttributes & APATTR_NETSERVICE) + { + dwIdentity = 2; + } + else if (psap->iAttributes & APATTR_LOCSERVICE) + { + dwIdentity = 1; + } + else if (psap->iAttributes & APATTR_LOCSYSTEM) + { + dwIdentity = 0; + } + else if (psap->iAttributes & APATTR_OTHERUSER) + { + if (!*psap->suUser.wzDomain || 0 == _wcsicmp(psap->suUser.wzDomain, L".")) + { + if (0 == _wcsicmp(psap->suUser.wzName, L"NetworkService")) + { + dwIdentity = 2; + } + else if (0 == _wcsicmp(psap->suUser.wzName, L"LocalService")) + { + dwIdentity = 1; + } + else if (0 == _wcsicmp(psap->suUser.wzName, L"LocalSystem")) + { + dwIdentity = 0; + } + else + { + dwIdentity = 3; + } + } + else if (0 == _wcsicmp(psap->suUser.wzDomain, L"NT AUTHORITY")) + { + if (0 == _wcsicmp(psap->suUser.wzName, L"NETWORK SERVICE")) + { + dwIdentity = 2; + } + else if (0 == _wcsicmp(psap->suUser.wzName, L"SERVICE")) + { + dwIdentity = 1; + } + else if (0 == _wcsicmp(psap->suUser.wzName, L"SYSTEM")) + { + dwIdentity = 0; + } + else + { + dwIdentity = 3; + } + } + else + { + dwIdentity = 3; + } + } + + if (-1 != dwIdentity) + { + hr = ScaWriteConfigID(IIS_APPPOOL_IDENTITY); + ExitOnFailure(hr, "failed to set app pool identity id"); + hr = ScaWriteConfigInteger(dwIdentity); + ExitOnFailure(hr, "failed to set app pool identity"); + + if (3 == dwIdentity) + { + if (*psap->suUser.wzDomain) + { + hr = StrAllocFormatted(&pwzValue, L"%s\\%s", psap->suUser.wzDomain, psap->suUser.wzName); + ExitOnFailure(hr, "failed to format user name: %ls domain: %ls", psap->suUser.wzName, psap->suUser.wzDomain); + } + else + { + hr = StrAllocFormatted(&pwzValue, L"%s", psap->suUser.wzName); + ExitOnFailure(hr, "failed to format user name: %ls", psap->suUser.wzName); + } + + hr = ScaWriteConfigID(IIS_APPPOOL_USER); + ExitOnFailure(hr, "failed to set app pool identity name id"); + hr = ScaWriteConfigString(pwzValue); + ExitOnFailure(hr, "failed to set app pool identity name"); + + hr = ScaWriteConfigID(IIS_APPPOOL_PWD); + ExitOnFailure(hr, "failed to set app pool identity password id"); + hr = ScaWriteConfigString(psap->suUser.wzPassword); + ExitOnFailure(hr, "failed to set app pool identity password"); + } + } + + if (*psap->wzManagedPipelineMode) + { + hr = ScaWriteConfigID(IIS_APPPOOL_MANAGED_PIPELINE_MODE); + ExitOnFailure(hr, "failed to set app pool integrated mode"); + hr = ScaWriteConfigString(psap->wzManagedPipelineMode); + ExitOnFailure(hr, "failed to set app pool managed pipeline mode value"); + } + + if (*psap->wzManagedRuntimeVersion) + { + hr = ScaWriteConfigID(IIS_APPPOOL_MANAGED_RUNTIME_VERSION); + ExitOnFailure(hr, "failed to set app pool managed runtime version mode"); + hr = ScaWriteConfigString(psap->wzManagedRuntimeVersion); + ExitOnFailure(hr, "failed to set app pool managed runtime version value"); + } + + // + //The number of properties above is variable so we put an end tag in so the + //execute CA will know when to stop looking for AppPool properties + // + hr = ScaWriteConfigID(IIS_APPPOOL_END); + ExitOnFailure(hr, "failed to set app pool end of properties id"); + +LExit: + ReleaseStr(pwzValue); + + return hr; +} + + +HRESULT ScaRemoveAppPool7( + __in const SCA_APPPOOL* psap + ) +{ + Assert(psap); + + HRESULT hr = S_OK; + + //do not delete the default App Pool + if (0 != _wcsicmp(psap->wzAppPool, L"DefaultAppPool")) + { + //delete the app pool + hr = ScaWriteConfigID(IIS_APPPOOL); + ExitOnFailure(hr, "failed to write AppPool key."); + + hr = ScaWriteConfigID(IIS_DELETE); + ExitOnFailure(hr, "failed to write AppPool delete action."); + + hr = ScaWriteConfigString(psap->wzName); + ExitOnFailure(hr, "failed to delete AppPool: %ls", psap->wzName); + } + +LExit: + return hr; +} diff --git a/src/ext/Iis/ca/scaapppool7.h b/src/ext/Iis/ca/scaapppool7.h new file mode 100644 index 00000000..1f49d899 --- /dev/null +++ b/src/ext/Iis/ca/scaapppool7.h @@ -0,0 +1,36 @@ +#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. + + +#include "scauser.h" + +// Identity +#define APATTR_NETSERVICE 0x0001 // Network Service +#define APATTR_LOCSERVICE 0x0002 // Local Service +#define APATTR_LOCSYSTEM 0x0004 // Local System +#define APATTR_OTHERUSER 0x0008 // Other User +#define APATTR_APPPOOLIDENTITY 0x0010 // ApplicationPoolIdentity + +// prototypes +HRESULT ScaFindAppPool7( + __in LPCWSTR wzAppPool, + __out_ecount(cchName) LPWSTR wzName, + __in DWORD cchName, + __in SCA_APPPOOL *psapList + ); + +HRESULT ScaAppPoolInstall7( + __in SCA_APPPOOL* psapList + ); + +HRESULT ScaAppPoolUninstall7( + __in SCA_APPPOOL* psapList + ); + +HRESULT ScaWriteAppPool7( + __in const SCA_APPPOOL* psap + ); + +HRESULT ScaRemoveAppPool7( + __in const SCA_APPPOOL* psap + ); diff --git a/src/ext/Iis/ca/scacert.cpp b/src/ext/Iis/ca/scacert.cpp new file mode 100644 index 00000000..5eae905a --- /dev/null +++ b/src/ext/Iis/ca/scacert.cpp @@ -0,0 +1,1482 @@ +// 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" + +// prototypes +static HRESULT ConfigureCertificates( + __in SCA_ACTION saAction + ); + +static LPCWSTR StoreMapping( + __in int iStore + ); + +static HRESULT FindExistingCertificate( + __in LPCWSTR wzName, + __in DWORD dwStoreLocation, + __in LPCWSTR wzStore, + __out BYTE** prgbCertificate, + __out DWORD* pcbCertificate + ); + +static HRESULT ResolveCertificate( + __in LPCWSTR wzId, + __in LPCWSTR wzName, + __in DWORD dwStoreLocation, + __in LPCWSTR wzStoreName, + __in DWORD dwAttributess, + __in LPCWSTR wzData, + __in LPCWSTR wzPFXPassword, + __out BYTE** ppbCertificate, + __out DWORD* pcbCertificate + ); + +static HRESULT ReadCertificateFile( + __in LPCWSTR wzPath, + __out BYTE** prgbData, + __out DWORD* pcbData + ); + +static HRESULT CertificateToHash( + __in BYTE* pbCertificate, + __in DWORD cbCertificate, + __in DWORD dwStoreLocation, + __in LPCWSTR wzPFXPassword, + __in BYTE rgbHash[], + __in DWORD cbHash + ); + +/* +HRESULT ScaGetCertificateByPath(LPCWSTR pwzName, BOOL fIsInstalling, + BOOL fIsUninstalling, INT iStore, + INT iStoreLocation, LPCWSTR wzSslCertificate, + LPCWSTR wzPFXPassword, BSTR* pbstrCertificate, + DWORD* pcbCertificate, BYTE* pbaHashBuffer); + +HRESULT ScaGetCertificateByRequest(LPCWSTR pwzName, BOOL fIsInstalling, + BOOL fIsUninstalling, INT iStore, + INT iStoreLocation, LPCWSTR wzDistinguishedName, + LPCWSTR wzCA, BSTR* pbstrCertificate, + DWORD* pcbCertificate, BYTE* pbaHashBuffer); + +HRESULT ScaSslNewCertificate(LPCWSTR pwzName, INT iStore, + INT iStoreLocation, LPCWSTR wzComputerName, + LPCWSTR wzDistinguishedName, LPCWSTR wzCertificateAuthorityOrig, + BSTR* pbstrCertificate, DWORD* pcbCertificate, + BYTE* pbaHashBuffer); + +HRESULT ScaSslExistingCertificateByName(LPCWSTR pwzName, INT iStore, + INT iStoreLocation, BSTR* pbstrCertificate, + DWORD* pcbCertificate, BYTE* pbaHashBuffer); + +HRESULT ScaSslExistingCertificateByBinaryData(INT iStore, INT iStoreLocation, + BYTE* pwzData, DWORD cchData); + +HRESULT CreateEnroll(ICEnroll2 **hEnroll, INT iStore, + INT iStoreLocation); + +HRESULT RequestCertificate(LPCWSTR pwzName, INT iStore, + INT iStoreLocation, LPCWSTR wzComputerName, + LPCWSTR wzDistinguishedName, LPCWSTR wzCertificateAuthority, + BSTR *pbstrCertificate); + +VOID ParseCertificateAuthority(__in LPCWSTR wzCertificateAuthorityOrig, __out LPWSTR *pwzBuffer, + __out LPWSTR **hwzCAArray, __out int *piCAArray); +*/ + + +LPCWSTR vcsCertQuery = L"SELECT `Certificate`, `Name`, `Component_`, `StoreLocation`, `StoreName`, `Attributes`, `Binary_`, `CertificatePath`, `PFXPassword` FROM `Certificate`"; +enum eCertQuery { cqCertificate = 1, cqName, cqComponent, cqStoreLocation, cqStoreName, cqAttributes, cqCertificateBinary, cqCertificatePath, cqPFXPassword }; + + +/******************************************************************** +InstallCertificates - CUSTOM ACTION ENTRY POINT for installing + certificates + +********************************************************************/ +extern "C" UINT __stdcall InstallCertificates( + __in MSIHANDLE hInstall + ) +{ + HRESULT hr = S_OK; + UINT er = ERROR_SUCCESS; + + // initialize + hr = WcaInitialize(hInstall, "InstallCertificates"); + ExitOnFailure(hr, "Failed to initialize"); + + hr = ConfigureCertificates(SCA_ACTION_INSTALL); + +LExit: + er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE; + return WcaFinalize(er); +} + + +/******************************************************************** +UninstallCertificates - CUSTOM ACTION ENTRY POINT for uninstalling + certificates + +********************************************************************/ +extern "C" UINT __stdcall UninstallCertificates( + __in MSIHANDLE hInstall + ) +{ + HRESULT hr = S_OK; + UINT er = ERROR_SUCCESS; + + // initialize + hr = WcaInitialize(hInstall, "UninstallCertificates"); + ExitOnFailure(hr, "Failed to initialize"); + + hr = ConfigureCertificates(SCA_ACTION_UNINSTALL); + +LExit: + er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE; + return WcaFinalize(er); +} + + +static HRESULT ConfigureCertificates( + __in SCA_ACTION saAction + ) +{ + //AssertSz(FALSE, "debug ConfigureCertificates()."); + + HRESULT hr = S_OK; + DWORD er = ERROR_SUCCESS; + + PMSIHANDLE hViewCertificate; + PMSIHANDLE hRecCertificate; + INSTALLSTATE isInstalled = INSTALLSTATE_UNKNOWN; + INSTALLSTATE isAction = INSTALLSTATE_UNKNOWN; + + WCHAR* pwzId = NULL; + WCHAR* pwzName = NULL; + WCHAR* pwzComponent = NULL; + int iData = 0; + DWORD dwStoreLocation = 0; + LPWSTR pwzStoreName = 0; + DWORD dwAttributes = 0; + WCHAR* pwzData = NULL; + WCHAR* pwzPFXPassword = NULL; + WCHAR* pwzCaData = NULL; + WCHAR* pwzRollbackCaData = NULL; + + BYTE* pbCertificate = NULL; + DWORD cbCertificate = 0; + DWORD_PTR cbPFXPassword = 0; + + // Bail quickly if the Certificate table isn't around. + if (S_OK != WcaTableExists(L"Certificate")) + { + WcaLog(LOGMSG_VERBOSE, "Skipping ConfigureCertificates() - required table not present."); + ExitFunction1(hr = S_FALSE); + } + + // Process the Certificate table. + hr = WcaOpenExecuteView(vcsCertQuery, &hViewCertificate); + ExitOnFailure(hr, "failed to open view on Certificate table"); + + while (SUCCEEDED(hr = WcaFetchRecord(hViewCertificate, &hRecCertificate))) + { + hr = WcaGetRecordString(hRecCertificate, cqCertificate, &pwzId); // the id is just useful to have up front + ExitOnFailure(hr, "failed to get Certificate.Certificate"); + + hr = WcaGetRecordString(hRecCertificate, cqComponent, &pwzComponent); + ExitOnFailure(hr, "failed to get Certificate.Component_"); + + er = ::MsiGetComponentStateW(WcaGetInstallHandle(), pwzComponent, &isInstalled, &isAction); + hr = HRESULT_FROM_WIN32(er); + ExitOnFailure(hr, "failed to get state for component: %ls", pwzComponent); + + if (!(WcaIsInstalling(isInstalled, isAction) && SCA_ACTION_INSTALL == saAction) && + !(WcaIsUninstalling(isInstalled, isAction) && SCA_ACTION_UNINSTALL == saAction) && + !(WcaIsReInstalling(isInstalled, isAction))) + { + WcaLog(LOGMSG_VERBOSE, "Skipping non-action certificate: %ls", pwzId); + continue; + } + + // extract the rest of the data from the Certificate table + hr = WcaGetRecordFormattedString(hRecCertificate, cqName, &pwzName); + ExitOnFailure(hr, "failed to get Certificate.Name"); + + hr = WcaGetRecordInteger(hRecCertificate, cqStoreLocation, &iData); + ExitOnFailure(hr, "failed to get Certificate.StoreLocation"); + + switch (iData) + { + case SCA_CERTSYSTEMSTORE_CURRENTUSER: + dwStoreLocation = CERT_SYSTEM_STORE_CURRENT_USER; + break; + case SCA_CERTSYSTEMSTORE_LOCALMACHINE: + dwStoreLocation = CERT_SYSTEM_STORE_LOCAL_MACHINE; + break; + default: + hr = E_INVALIDARG; + ExitOnFailure(hr, "Invalid store location value: %d", iData); + } + + hr = WcaGetRecordString(hRecCertificate, cqStoreName, &pwzStoreName); + ExitOnFailure(hr, "failed to get Certificate.StoreName"); + + hr = WcaGetRecordInteger(hRecCertificate, cqAttributes, reinterpret_cast(&dwAttributes)); + ExitOnFailure(hr, "failed to get Certificate.Attributes"); + + if (dwAttributes & SCA_CERT_ATTRIBUTE_BINARYDATA) + { + hr = WcaGetRecordString(hRecCertificate, cqCertificateBinary, &pwzData); + ExitOnFailure(hr, "failed to get Certificate.Binary_"); + } + else + { + hr = WcaGetRecordFormattedString(hRecCertificate, cqCertificatePath, &pwzData); + ExitOnFailure(hr, "failed to get Certificate.CertificatePath"); + } + + hr = WcaGetRecordFormattedString(hRecCertificate, cqPFXPassword, &pwzPFXPassword); + ExitOnFailure(hr, "failed to get Certificate.PFXPassword"); + + // Write the common data (for both install and uninstall) to the CustomActionData + // to pass data to the deferred CustomAction. + hr = StrAllocString(&pwzCaData, pwzName, 0); + ExitOnFailure(hr, "Failed to pass Certificate.Certificate to deferred CustomAction."); + hr = WcaWriteStringToCaData(pwzStoreName, &pwzCaData); + ExitOnFailure(hr, "Failed to pass Certificate.StoreName to deferred CustomAction."); + hr = WcaWriteIntegerToCaData(dwAttributes, &pwzCaData); + ExitOnFailure(hr, "Failed to pass Certificate.Attributes to deferred CustomAction."); + + // Copy the rollback data from the deferred data because it's the same up to this point. + hr = StrAllocString(&pwzRollbackCaData, pwzCaData, 0); + ExitOnFailure(hr, "Failed to allocate string for rollback CustomAction."); + + // Finally, schedule the correct deferred CustomAction to actually do work. + LPCWSTR wzAction = NULL; + LPCWSTR wzRollbackAction = NULL; + DWORD dwCost = 0; + if (SCA_ACTION_UNINSTALL == saAction) + { + // Find an existing certificate one (if there is one) to so we have it for rollback. + hr = FindExistingCertificate(pwzName, dwStoreLocation, pwzStoreName, &pbCertificate, &cbCertificate); + ExitOnFailure(hr, "Failed to search for existing certificate with friendly name: %ls", pwzName); + + if (pbCertificate) + { + hr = WcaWriteStreamToCaData(pbCertificate, cbCertificate, &pwzRollbackCaData); + ExitOnFailure(hr, "Failed to pass Certificate.Data to rollback CustomAction."); + + hr = WcaWriteStringToCaData(pwzPFXPassword, &pwzRollbackCaData); + ExitOnFailure(hr, "Failed to pass Certificate.PFXPassword to rollback CustomAction."); + + hr = WcaWriteIntegerToCaData(dwAttributes, &pwzCaData); + ExitOnFailure(hr, "Failed to pass Certificate.Attributes to deferred CustomAction."); + } + + // Pick the right action to run based on what store we're uninstalling from. + if (CERT_SYSTEM_STORE_LOCAL_MACHINE == dwStoreLocation) + { + wzAction = CUSTOM_ACTION_DECORATION(L"DeleteMachineCertificate"); + if (pbCertificate) + { + wzRollbackAction = L"RollbackDeleteMachineCertificate"; + } + } + else + { + wzAction = CUSTOM_ACTION_DECORATION(L"DeleteUserCertificate"); + if (pbCertificate) + { + wzRollbackAction = L"RollbackDeleteUserCertificate"; + } + } + dwCost = COST_CERT_DELETE; + } + else + { + // Actually get the certificate, resolve it to a blob, and get the blob's hash. + hr = ResolveCertificate(pwzId, pwzName, dwStoreLocation, pwzStoreName, dwAttributes, pwzData, pwzPFXPassword, &pbCertificate, &cbCertificate); + ExitOnFailure(hr, "Failed to resolve certificate: %ls", pwzId); + + hr = WcaWriteStreamToCaData(pbCertificate, cbCertificate, &pwzCaData); + ExitOnFailure(hr, "Failed to pass Certificate.Data to deferred CustomAction."); + + hr = WcaWriteStringToCaData(pwzPFXPassword, &pwzCaData); + ExitOnFailure(hr, "Failed to pass Certificate.PFXPassword to deferred CustomAction."); + + // Pick the right action to run based on what store we're installing into. + if (CERT_SYSTEM_STORE_LOCAL_MACHINE == dwStoreLocation) + { + wzAction = CUSTOM_ACTION_DECORATION(L"AddMachineCertificate"); + wzRollbackAction = CUSTOM_ACTION_DECORATION(L"RollbackAddMachineCertificate"); + } + else + { + wzAction = CUSTOM_ACTION_DECORATION(L"AddUserCertificate"); + wzRollbackAction = CUSTOM_ACTION_DECORATION(L"RollbackAddUserCertificate"); + } + dwCost = COST_CERT_ADD; + } + + if (wzRollbackAction) + { + hr = WcaDoDeferredAction(wzRollbackAction, pwzRollbackCaData, dwCost); + ExitOnFailure(hr, "Failed to schedule rollback certificate action '%ls' for: %ls", wzRollbackAction, pwzId); + } + + hr = WcaDoDeferredAction(wzAction, pwzCaData, dwCost); + ExitOnFailure(hr, "Failed to schedule certificate action '%ls' for: %ls", wzAction, pwzId); + + // Clean up for the next certificate. + ReleaseNullMem(pbCertificate); + } + + if (E_NOMOREITEMS == hr) + { + hr = S_OK; + } + +LExit: + if (NULL != pwzPFXPassword && SUCCEEDED(StrSize(pwzPFXPassword, &cbPFXPassword))) + { + SecureZeroMemory(pwzPFXPassword, cbPFXPassword); + } + + ReleaseMem(pbCertificate); + ReleaseStr(pwzCaData); + ReleaseStr(pwzPFXPassword); + ReleaseStr(pwzData); + ReleaseStr(pwzName); + ReleaseStr(pwzStoreName); + ReleaseStr(pwzComponent); + ReleaseStr(pwzId); + + return hr; +} + + +static HRESULT ResolveCertificate( + __in LPCWSTR wzId, + __in LPCWSTR /*wzName*/, + __in DWORD dwStoreLocation, + __in LPCWSTR /*wzStoreName*/, + __in DWORD dwAttributes, + __in LPCWSTR wzData, + __in LPCWSTR wzPFXPassword, + __out BYTE** ppbCertificate, + __out DWORD* pcbCertificate + ) +{ + HRESULT hr = S_OK; + + LPWSTR pwzSql = NULL; + PMSIHANDLE hView; + PMSIHANDLE hRec; + MSIHANDLE hCertificateHashView = NULL; + MSIHANDLE hCertificateHashColumns = NULL; + + BYTE rgbCertificateHash[CB_CERTIFICATE_HASH] = { 0 }; + WCHAR wzEncodedCertificateHash[CB_CERTIFICATE_HASH * 2 + 1] = { 0 }; + + PMSIHANDLE hViewCertificateRequest, hRecCertificateRequest; + + WCHAR* pwzDistinguishedName = NULL; + WCHAR* pwzCA = NULL; + + BYTE* pbData = NULL; + DWORD cbData = 0; + + if (dwAttributes & SCA_CERT_ATTRIBUTE_REQUEST) + { + hr = E_NOTIMPL; + ExitOnFailure(hr, "Installing certificates by requesting them from a certificate authority is not currently supported"); + //if (dwAttributes & SCA_CERT_ATTRIBUTE_OVERWRITE) + //{ + // // try to overwrite with the patch to a cert file + // WcaLog(LOGMSG_VERBOSE, "ConfigureCertificates - Overwrite with SSLCERTIFICATE"); + // hr = ScaGetCertificateByPath(pwzName, fIsInstalling, fIsUninstalling, + // iStore, iStoreLocation, pwzData, wzPFXPassword, pbstrCertificate, pcbCertificate, pbaHashBuffer); + //} + //if (hr != S_OK) + //{ + // if (fIsUninstalling && !fIsInstalling) + // { + // // for uninstall, we just want to find the existing certificate + // hr = ScaSslExistingCertificateByName(pwzName, iStore, iStoreLocation, pbstrCertificate, pcbCertificate, pbaHashBuffer); + // ExitOnFailure(hr, "Failed Retrieving existing certificate during uninstall"); + // // ok if no existing cert + // if (S_OK != hr) + // hr = S_OK; + // } + // else + // { + // // still no certificate + // // user has request this certificate, try to locate DistinguishedName and CA + // hr = WcaTableExists(L"CertificateRequest"); + // ExitOnFailure(hr, "CertificateRequest is referenced but not found"); + // WcaLog(LOGMSG_VERBOSE, "ConfigureCertificates - CertificateRequest table present"); + // cchSQLView = 255 + lstrlenW(pwzName); + // pwzSQLView = new WCHAR[cchSQLView]; + // if (pwzSQLView) + // { + // hr = ::StringCchPrintfW(pwzSQLView, cchSQLView, L"SELECT `DistinguishedName`, `CA` FROM `CertificateRequest` WHERE `Certificate_`=\'%s\'", pwzName); + // ExitOnFailure(hr, "::StringCchPrintfW failed"); + // hr = WcaOpenExecuteView(pwzSQLView, &hViewCertificateRequest); + // ExitOnFailure(hr, "failed to open view on CertificateRequest table"); + // hr = WcaFetchSingleRecord(hViewCertificateRequest, &hRecCertificateRequest); + // ExitOnFailure(hr, "failed to retrieve request from CertificateRequest table"); + // hr = WcaGetRecordString(hRecCertificateRequest, 1, &pwzDistinguishedName); + // ExitOnFailure(hr, "failed to get DistinguishedName"); + // hr = WcaGetRecordString(hRecCertificateRequest, 2, &pwzCA); + // ExitOnFailure(hr, "failed to get CA"); + // if (pwzDistinguishedName && pwzCA && *pwzDistinguishedName && *pwzCA) + // { + // hr = ScaGetCertificateByRequest(pwzName, fIsInstalling, fIsUninstalling, iStore, iStoreLocation, pwzDistinguishedName, pwzCA, pbstrCertificate, pcbCertificate, pbaHashBuffer); + // } + // else + // { + // hr = E_FAIL; + // ExitOnFailure(hr, "CertificateRequest entry is empty"); + // } + // } + // else + // { + // hr = E_FAIL; + // ExitOnFailure(hr, "Out of memory"); + // } + // } + //} + } + else if (dwAttributes & SCA_CERT_ATTRIBUTE_BINARYDATA) + { + // get the binary stream in Binary + hr = WcaTableExists(L"Binary"); + if (S_OK != hr) + { + if (SUCCEEDED(hr)) + { + hr = E_UNEXPECTED; + } + ExitOnFailure(hr, "Binary was referenced but there is no Binary table."); + } + + hr = StrAllocFormatted(&pwzSql, L"SELECT `Data` FROM `Binary` WHERE `Name`=\'%s\'", wzData); + ExitOnFailure(hr, "Failed to allocate Binary table query."); + + hr = WcaOpenExecuteView(pwzSql, &hView); + ExitOnFailure(hr, "Failed to open view on Binary table"); + + hr = WcaFetchSingleRecord(hView, &hRec); + ExitOnFailure(hr, "Failed to retrieve request from Binary table"); + + hr = WcaGetRecordStream(hRec, 1, &pbData, &cbData); + ExitOnFailure(hr, "Failed to ready Binary.Data for certificate."); + } + else if (dwAttributes == SCA_CERT_ATTRIBUTE_DEFAULT) + { + hr = ReadCertificateFile(wzData, &pbData, &cbData); + ExitOnFailure(hr, "Failed to read certificate from file path."); + } + else + { + hr = E_INVALIDARG; + ExitOnFailure(hr, "Invalid Certificate.Attributes."); + } + + // If we have loaded a certificate, update the Certificate.Hash column. + if (pbData) + { + hr = CertificateToHash(pbData, cbData, dwStoreLocation, wzPFXPassword, rgbCertificateHash, countof(rgbCertificateHash)); + ExitOnFailure(hr, "Failed to get SHA1 hash of certificate."); + + hr = StrHexEncode(rgbCertificateHash, countof(rgbCertificateHash), wzEncodedCertificateHash, countof(wzEncodedCertificateHash)); + ExitOnFailure(hr, "Failed to hex encode SHA1 hash of certificate."); + + // Update the CertificateHash table. + hr = WcaAddTempRecord(&hCertificateHashView, &hCertificateHashColumns, L"CertificateHash", NULL, 0, 2, wzId, wzEncodedCertificateHash); + ExitOnFailure(hr, "Failed to add encoded has for certificate: %ls", wzId); + } + + *ppbCertificate = pbData; + *pcbCertificate = cbData; + pbData = NULL; + +LExit: + if (hCertificateHashColumns) + { + ::MsiCloseHandle(hCertificateHashColumns); + } + + if (hCertificateHashView) + { + ::MsiCloseHandle(hCertificateHashView); + } + + ReleaseStr(pwzDistinguishedName); + ReleaseStr(pwzCA); + ReleaseMem(pbData); + ReleaseStr(pwzSql); + + return hr; +} + + +static HRESULT ReadCertificateFile( + __in LPCWSTR wzPath, + __out BYTE** prgbData, + __out DWORD* pcbData + ) +{ + HRESULT hr = S_OK; + + PCCERT_CONTEXT pCertContext = NULL; + DWORD dwContentType; + BYTE* pbData = NULL; + DWORD cbData = 0; + + if (!::CryptQueryObject(CERT_QUERY_OBJECT_FILE, reinterpret_cast(wzPath), CERT_QUERY_CONTENT_FLAG_ALL, CERT_QUERY_FORMAT_FLAG_ALL, 0, NULL, &dwContentType, NULL, NULL, NULL, (LPCVOID*)&pCertContext)) + { + ExitOnFailure(hr, "Failed to read certificate from file: %ls", wzPath); + } + + if (pCertContext) + { + cbData = pCertContext->cbCertEncoded; + pbData = static_cast(MemAlloc(cbData, FALSE)); + ExitOnNull(pbData, hr, E_OUTOFMEMORY, "Failed to allocate memory to read certificate from file: %ls", wzPath); + + CopyMemory(pbData, pCertContext->pbCertEncoded, pCertContext->cbCertEncoded); + } + else + { + // If we have a PFX blob, get the first certificate out of the PFX and use that instead of the PFX. + if (dwContentType & CERT_QUERY_CONTENT_PFX) + { + hr = FileRead(&pbData, &cbData, wzPath); + ExitOnFailure(hr, "Failed to read PFX file: %ls", wzPath); + } + else + { + hr = E_UNEXPECTED; + ExitOnFailure(hr, "Unexpected certificate type read from disk."); + } + } + + *pcbData = cbData; + *prgbData = pbData; + pbData = NULL; + +LExit: + ReleaseMem(pbData); + return hr; +} + + +static HRESULT CertificateToHash( + __in BYTE* pbCertificate, + __in DWORD cbCertificate, + __in DWORD dwStoreLocation, + __in LPCWSTR wzPFXPassword, + __in BYTE rgbHash[], + __in DWORD cbHash + ) +{ + HRESULT hr = S_OK; + + HCERTSTORE hPfxCertStore = NULL; + PCCERT_CONTEXT pCertContext = NULL; + PCCERT_CONTEXT pCertContextEnum = NULL; + CRYPT_DATA_BLOB blob = { 0 }; + CRYPT_KEY_PROV_INFO* pPfxInfo = NULL; + DWORD dwKeyset = (CERT_SYSTEM_STORE_CURRENT_USER == dwStoreLocation) ? CRYPT_USER_KEYSET : CRYPT_MACHINE_KEYSET; + DWORD dwEncodingType; + DWORD dwContentType; + DWORD dwFormatType; + + blob.pbData = pbCertificate; + blob.cbData = cbCertificate; + + if (!::CryptQueryObject(CERT_QUERY_OBJECT_BLOB, &blob, CERT_QUERY_CONTENT_FLAG_ALL, CERT_QUERY_FORMAT_FLAG_ALL, 0, &dwEncodingType, &dwContentType, &dwFormatType, NULL, NULL, (LPCVOID*)&pCertContext)) + { + ExitWithLastError(hr, "Failed to process certificate as a valid certificate."); + } + + if (!pCertContext) + { + // If we have a PFX blob, get the first certificate out of the PFX and use that instead of the PFX. + if (dwContentType & CERT_QUERY_CONTENT_PFX) + { + // If we fail and our password is blank, also try passing in NULL for the password (according to the docs) + hPfxCertStore = ::PFXImportCertStore((CRYPT_DATA_BLOB*)&blob, wzPFXPassword, dwKeyset); + if (NULL == hPfxCertStore && !*wzPFXPassword) + { + hPfxCertStore = ::PFXImportCertStore((CRYPT_DATA_BLOB*)&blob, NULL, dwKeyset); + } + ExitOnNullWithLastError(hPfxCertStore, hr, "Failed to open PFX file."); + + // Find the first cert with a private key, or just use the last one + for (pCertContextEnum = ::CertEnumCertificatesInStore(hPfxCertStore, pCertContextEnum); + pCertContextEnum; + pCertContextEnum = ::CertEnumCertificatesInStore(hPfxCertStore, pCertContextEnum)) + { + pCertContext = pCertContextEnum; + + if (pCertContext && CertHasPrivateKey(pCertContext, NULL)) + { + break; + } + } + + ExitOnNullWithLastError(pCertContext, hr, "Failed to read first certificate out of PFX file."); + + // Ignore failures, the worst that happens is some parts of the PFX get left behind. + CertReadProperty(pCertContext, CERT_KEY_PROV_INFO_PROP_ID, &pPfxInfo, NULL); + } + else + { + hr = E_UNEXPECTED; + ExitOnFailure(hr, "Unexpected certificate type processed."); + } + } + + DWORD cb = cbHash; + if (!::CertGetCertificateContextProperty(pCertContext, CERT_SHA1_HASH_PROP_ID, static_cast(rgbHash), &cb)) + { + ExitWithLastError(hr, "Failed to get certificate SHA1 hash property."); + } + AssertSz(cb == cbHash, "Did not correctly read certificate SHA1 hash."); + +LExit: + if (pCertContext) + { + ::CertFreeCertificateContext(pCertContext); + } + + if (hPfxCertStore) + { + ::CertCloseStore(hPfxCertStore, 0); + } + + if (pPfxInfo) + { + HCRYPTPROV hProvIgnored = NULL; // ignored on deletes. + ::CryptAcquireContextW(&hProvIgnored, pPfxInfo->pwszContainerName, pPfxInfo->pwszProvName, pPfxInfo->dwProvType, dwKeyset | CRYPT_DELETEKEYSET | CRYPT_SILENT); + + MemFree(pPfxInfo); + } + + return hr; +} + + +static HRESULT FindExistingCertificate( + __in LPCWSTR wzName, + __in DWORD dwStoreLocation, + __in LPCWSTR wzStore, + __out BYTE** prgbCertificate, + __out DWORD* pcbCertificate + ) +{ + HRESULT hr = S_OK; + HCERTSTORE hCertStore = NULL; + PCCERT_CONTEXT pCertContext = NULL; + BYTE* pbCertificate = NULL; + DWORD cbCertificate = 0; + + hCertStore = ::CertOpenStore(CERT_STORE_PROV_SYSTEM, 0, NULL, dwStoreLocation | CERT_STORE_READONLY_FLAG, wzStore); + MessageExitOnNullWithLastError(hCertStore, hr, msierrCERTFailedOpen, "Failed to open certificate store."); + + // Loop through the certificate, looking for certificates that match our friendly name. + pCertContext = CertFindCertificateInStore(hCertStore, PKCS_7_ASN_ENCODING | X509_ASN_ENCODING, 0, CERT_FIND_ANY, NULL, NULL); + while (pCertContext) + { + WCHAR wzFriendlyName[256] = { 0 }; + DWORD cbFriendlyName = sizeof(wzFriendlyName); + + if (::CertGetCertificateContextProperty(pCertContext, CERT_FRIENDLY_NAME_PROP_ID, reinterpret_cast(wzFriendlyName), &cbFriendlyName) && + CSTR_EQUAL == ::CompareStringW(LOCALE_SYSTEM_DEFAULT, 0, wzName, -1, wzFriendlyName, -1)) + { + // If the certificate with matching friendly name is valid, let's use that. + long lVerify = ::CertVerifyTimeValidity(NULL, pCertContext->pCertInfo); + if (0 == lVerify) + { + cbCertificate = pCertContext->cbCertEncoded; + pbCertificate = static_cast(MemAlloc(cbCertificate, FALSE)); + ExitOnNull(pbCertificate, hr, E_OUTOFMEMORY, "Failed to allocate memory to copy out exist certificate."); + + CopyMemory(pbCertificate, pCertContext->pbCertEncoded, cbCertificate); + break; // found a matching certificate, no more searching necessary + } + } + + // Next certificate in the store. + PCCERT_CONTEXT pNext = ::CertFindCertificateInStore(hCertStore, PKCS_7_ASN_ENCODING | X509_ASN_ENCODING, 0, CERT_FIND_ANY, NULL, pCertContext); + // old pCertContext is freed by CertFindCertificateInStore + pCertContext = pNext; + } + + *prgbCertificate = pbCertificate; + *pcbCertificate = cbCertificate; + pbCertificate = NULL; + +LExit: + ReleaseMem(pbCertificate); + + if (pCertContext) + { + ::CertFreeCertificateContext(pCertContext); + } + + if (hCertStore) + { + ::CertCloseStore(hCertStore, 0); + } + + return hr; +} + +/* +HRESULT CreateEnroll(ICEnroll2 **hEnroll, INT iStore, INT iStoreLocation) +{ + ICEnroll2 *pEnroll = NULL; + HRESULT hr = S_OK; + LONG lFlags; + DWORD dwFlags = iStoreLocation << CERT_SYSTEM_STORE_LOCATION_SHIFT; + + // create IEntroll + hr = CoCreateInstance( CLSID_CEnroll, NULL, CLSCTX_INPROC_SERVER, IID_ICEnroll2, (void **)&pEnroll ); + if (FAILED(hr)) + return hr; + + switch (iStore) + { + case SCA_CERT_STORENAME_MY: + pEnroll->get_MyStoreFlags(&lFlags); + lFlags &= ~CERT_SYSTEM_STORE_LOCATION_MASK; + lFlags |= dwFlags; + // following call will change Request store flags also + pEnroll->put_MyStoreFlags(lFlags); + break; + case SCA_CERT_STORENAME_CA: + pEnroll->get_CAStoreFlags(&lFlags); + lFlags &= ~CERT_SYSTEM_STORE_LOCATION_MASK; + lFlags |= dwFlags; + // following call will change Request store flags also + pEnroll->put_CAStoreFlags(lFlags); + break; + case SCA_CERT_STORENAME_REQUEST: + pEnroll->get_RequestStoreFlags(&lFlags); + lFlags &= ~CERT_SYSTEM_STORE_LOCATION_MASK; + lFlags |= dwFlags; + // following call will change Request store flags also + pEnroll->put_RequestStoreFlags(lFlags); + break; + case SCA_CERT_STORENAME_ROOT: + pEnroll->get_RootStoreFlags(&lFlags); + lFlags &= ~CERT_SYSTEM_STORE_LOCATION_MASK; + lFlags |= dwFlags; + // following call will change Request store flags also + pEnroll->put_RootStoreFlags(lFlags); + break; + default: + hr = E_FAIL; + return hr; + } + + pEnroll->get_GenKeyFlags(&lFlags); + lFlags |= CRYPT_EXPORTABLE; + pEnroll->put_GenKeyFlags(lFlags); + + pEnroll->put_KeySpec(AT_KEYEXCHANGE); + pEnroll->put_ProviderType(PROV_RSA_SCHANNEL); + pEnroll->put_DeleteRequestCert(TRUE); + + *hEnroll = pEnroll; + return hr; +} + + +HRESULT RequestCertificate(LPCWSTR pwzName, INT iStore, INT iStoreLocation, + LPCWSTR wzComputerName, LPCWSTR wzDistinguishedName, LPCWSTR wzCertificateAuthority, + BSTR *pbstrCertificate) +{ + if (pbstrCertificate == NULL) + return E_INVALIDARG; + + HRESULT hr; + ICEnroll2 *pEnroll = NULL; + ICertRequest *pCertRequest = NULL; + BSTR bstrRequest = NULL; + LONG nDisposition; + + BSTR bstrCertificateUsage = NULL; + BSTR bstrCertificateAttributes = NULL; + BSTR bstrCertificateAuthority = NULL; + + // equivalent to: sprintf(bstrDistinguishedName, L"%s,CN=%s", wzDistinguishedName, wzComputerName); + DWORD cchComputerName = lstrlenW(wzComputerName); + DWORD cchDistinguishedName = lstrlenW(wzDistinguishedName); + CONST DWORD cchbstrDistinguishedName = 5 + cchComputerName + cchDistinguishedName; + BSTR bstrDistinguishedName = SysAllocStringLen(NULL, cchbstrDistinguishedName); + ExitOnNull(bstrDistinguishedName, hr, E_OUTOFMEMORY, "Failed to allocate space for distinguished name."); + ::StringCchCopyW((WCHAR*) bstrDistinguishedName, cchbstrDistinguishedName, wzDistinguishedName); + ::StringCchCatW((WCHAR*) bstrDistinguishedName, cchbstrDistinguishedName, L",CN="); + ::StringCchCatW((WCHAR*) bstrDistinguishedName, cchbstrDistinguishedName, wzComputerName); + + bstrCertificateUsage = SysAllocString(WIDE(szOID_PKIX_KP_SERVER_AUTH)); + ExitOnNull(bstrCertificateUsage, hr, E_OUTOFMEMORY, "Failed to allocate space for Certificate Usage."); + bstrCertificateAttributes = SysAllocString(L"CertificateTemplate:WebServer"); + bstrCertificateAuthority = SysAllocString(wzCertificateAuthority); + ExitOnNull(bstrCertificateAuthority, hr, E_OUTOFMEMORY, "Failed to allocate space for Certificate Authority."); + + hr = CreateEnroll(&pEnroll, iStore, iStoreLocation); + ExitOnFailure(hr, "failed CoCreateInstance IEnroll"); + + hr = pEnroll->createPKCS10(bstrDistinguishedName, bstrCertificateUsage, &bstrRequest); + ExitOnFailure(hr, "failed createPKCS10"); + + hr = CoCreateInstance(CLSID_CCertRequest, NULL, CLSCTX_INPROC_SERVER, IID_ICertRequest, (void **)&pCertRequest); + ExitOnFailure(hr, "failed CoCreateInstance ICertRequest"); + + hr = pCertRequest->Submit(CR_IN_BASE64 | CR_IN_PKCS10, bstrRequest, bstrCertificateAttributes, bstrCertificateAuthority, &nDisposition); + ExitOnFailure(hr, "failed ICertRequest.Submit"); + + hr = (nDisposition == CR_DISP_ISSUED) ? S_OK : E_FAIL; + ExitOnFailure(hr, "failed CR_DISP_ISSUED"); + + hr = pCertRequest->GetCertificate(CR_OUT_BASE64, pbstrCertificate); + ExitOnFailure(hr, "failed ICertRequest.GetCertificate"); + + // save the certificate in place, cannot be passed to a deferred custom action + hr = pEnroll->acceptPKCS7(*pbstrCertificate); + ExitOnFailure(hr, "failed accept certificate into MY store"); + +LExit: + ReleaseObject(pCertRequest); + ReleaseBSTR(bstrRequest); + ReleaseObject(pEnroll); + ReleaseBSTR(bstrCertificateAuthority); + ReleaseBSTR(bstrCertificateAttributes); + ReleaseBSTR(bstrDistinguishedName); + + return hr; +} + + +VOID ParseCertificateAuthority(__in LPCWSTR wzCertificateAuthorityOrig, __out LPWSTR *pwzBuffer, __out LPWSTR **hwzCAArray, __out int *piCAArray) +{ + // @asAuthorities = split /;/, $sAuthority; + CONST WCHAR wchDelimiter = L';'; + + // copy constant into a buffer + Assert(wzCertificateAuthorityOrig); + + INT cchCA = lstrlenW(wzCertificateAuthorityOrig) + 1; + WCHAR* wzBuffer = new WCHAR[cchCA]; + if (!wzBuffer) + return; + + ::StringCchCopyW(wzBuffer, cchCA, wzCertificateAuthorityOrig); + + // determine the number of strings in the field + int iCAArray = 1; + int i; + for (i = 0; i < cchCA; ++i) + { + if (wzBuffer[i] == wchDelimiter) + ++iCAArray; + } + LPWSTR *pwzCAArray = (LPWSTR*) new BYTE[iCAArray * sizeof(LPWSTR)]; + if (!pwzCAArray) + { + return; + } + + pwzCAArray[0] = wzBuffer; + iCAArray = 0; + for (i = 0; i < cchCA; ++i) + { + if (wzBuffer[i] != wchDelimiter) + continue; + wzBuffer[i] = 0; // convert buffer into MULTISZ + pwzCAArray[iCAArray] = &wzBuffer[i+1]; + ++iCAArray; + } + + *pwzBuffer = wzBuffer; + *hwzCAArray = pwzCAArray; + *piCAArray = iCAArray; +} + + +HRESULT ScaSslExistingCertificateByBinaryData(INT iStore, INT iStoreLocation, BYTE* pwzData, DWORD cchData) +{ + HRESULT hr = S_FALSE; + HCERTSTORE hCertStore = NULL; + PCCERT_CONTEXT pCertCtx = NULL, pCertCtxExisting = NULL; + DWORD dwFlags = 0; + LPCWSTR wzStore = StoreMapping(iStore); + CERT_BLOB blob; + + dwFlags = iStoreLocation << CERT_SYSTEM_STORE_LOCATION_SHIFT; + hCertStore = CertOpenStore(CERT_STORE_PROV_SYSTEM, 0, NULL, dwFlags, wzStore); + MessageExitOnNullWithLastError(hCertStore, hr, msierrCERTFailedOpen, "failed to open certificate store, OK on uninstall"); + + blob.pbData = pwzData; + blob.cbData = cchData; + + if (!::CryptQueryObject(CERT_QUERY_OBJECT_BLOB, &blob, CERT_QUERY_CONTENT_FLAG_ALL, CERT_QUERY_FORMAT_FLAG_ALL, + 0, NULL, NULL, NULL, NULL, NULL, (LPCVOID*)&pCertCtx)) + ExitOnLastError(hr, "failed to parse the certificate blob, OK on uninstall"); + + pCertCtxExisting = CertFindCertificateInStore( + hCertStore, + PKCS_7_ASN_ENCODING | X509_ASN_ENCODING, + 0, + CERT_FIND_EXISTING, + pCertCtx, + NULL); + + if (pCertCtxExisting) + { + hr = S_OK; + } + +LExit: + if (pCertCtx) + { + CertFreeCertificateContext(pCertCtx); + pCertCtx = NULL; + } + if (pCertCtxExisting) + { + CertFreeCertificateContext(pCertCtxExisting); + pCertCtxExisting = NULL; + } + if (hCertStore) + { + CertCloseStore(hCertStore, 0); + hCertStore = NULL; + } + + return hr; +} + + +HRESULT ScaSslExistingCertificateByName(LPCWSTR pwzName, INT iStore, INT iStoreLocation, + BSTR* pbstrCertificate, DWORD* pcbCertificate, BYTE* pbaHashBuffer) +{ + HRESULT hr = S_FALSE; + HCERTSTORE hSystemStore = NULL; + PCCERT_CONTEXT pTargetCert = NULL; + WCHAR wzFriendlyName[MAX_PATH] = {0}; + DWORD dwFriendlyNameLen = sizeof(wzFriendlyName); + + // Call CertOpenStore to open the CA store. + hSystemStore = CertOpenStore( + CERT_STORE_PROV_SYSTEM_REGISTRY, + 0, + NULL, + (iStoreLocation << CERT_SYSTEM_STORE_LOCATION_SHIFT) | CERT_STORE_OPEN_EXISTING_FLAG, + StoreMapping(iStore)); + if (hSystemStore == NULL) + ExitFunction(); + + // Get a particular certificate using CertFindCertificateInStore. + pTargetCert = CertFindCertificateInStore( + hSystemStore, + PKCS_7_ASN_ENCODING | X509_ASN_ENCODING, + 0, + CERT_FIND_ANY, + NULL, + NULL); + while (pTargetCert != NULL) + { + if ((CertGetCertificateContextProperty(pTargetCert, CERT_FRIENDLY_NAME_PROP_ID, + (BYTE*)wzFriendlyName, &dwFriendlyNameLen)) && + lstrcmpW(wzFriendlyName, pwzName) == 0) + { + // pTargetCert is a pointer to the desired certificate. + // Check the certificate's validity. + switch (CertVerifyTimeValidity( + NULL, + pTargetCert->pCertInfo)) + { + case 1: + // Certificate is expired + WcaLog(LOGMSG_STANDARD, "The SSL certificate has expired"); + // always remove it + { + PCCERT_CONTEXT pDupCertContext = CertDuplicateCertificateContext(pTargetCert); + if (pDupCertContext && CertDeleteCertificateFromStore(pDupCertContext)) + { + WcaLog(LOGMSG_STANDARD, "A SSL certificate has removed"); + } + } + break; + case 0: + // Certificate is valid + WcaLog(LOGMSG_STANDARD, "The SSL certificate is valid"); + hr = S_OK; + if (pbaHashBuffer) + { + // if the certificate already exists and is valid, use that one + DWORD dwHashSize = CB_CERTIFICATE_HASH; + hr = CertGetCertificateContextProperty(pTargetCert, CERT_SHA1_HASH_PROP_ID, (VOID*)pbaHashBuffer, &dwHashSize) + ? S_OK : E_FAIL; + ExitOnFailure(hr, "failed CertGetCertificateContextProperty CERT_SHA1_HASH_PROP_ID"); + Assert(pbstrCertificate); + Assert(pcbCertificate); + ReleaseBSTR(*pbstrCertificate); + + *pbstrCertificate = SysAllocStringByteLen((LPCSTR)(pTargetCert->pbCertEncoded), pTargetCert->cbCertEncoded); + *pcbCertificate = pTargetCert->cbCertEncoded; + } + ExitFunction(); + break; + default: + // Certificate not valid yet, ignore it + WcaLog(LOGMSG_STANDARD, "The SSL certificate is not valid"); + break; + } + } + pTargetCert = CertFindCertificateInStore( + hSystemStore, + PKCS_7_ASN_ENCODING | X509_ASN_ENCODING, + 0, + CERT_FIND_ANY, + NULL, + pTargetCert); + wzFriendlyName[0] = 0; + dwFriendlyNameLen = sizeof(wzFriendlyName); + } + +LExit: + // Clean up memory and quit. + if (pTargetCert) + { + CertFreeCertificateContext(pTargetCert); + pTargetCert = NULL; + } + if (hSystemStore) + { + CertCloseStore(hSystemStore, CERT_CLOSE_STORE_CHECK_FLAG); + hSystemStore = NULL; + } + + return hr; +} + + +HRESULT ScaSslNewCertificate(LPCWSTR pwzName, INT iStore, INT iStoreLocation, LPCWSTR wzComputerName, LPCWSTR wzDistinguishedName, LPCWSTR wzCertificateAuthorityOrig, + BSTR* pbstrCertificate, DWORD* pcbCertificate, BYTE* pbaHashBuffer) +{ + + if (pbstrCertificate == NULL) + return E_INVALIDARG; + + HRESULT hr = S_OK; + LPWSTR wzCABuffer = NULL; + LPWSTR *wzCAArray = NULL; + int iCAArray = 0; + + // otherwise call the CA for one + ParseCertificateAuthority(wzCertificateAuthorityOrig, &wzCABuffer, &wzCAArray, &iCAArray); + + // try each authority three times + for (int i = 0; i < 3 * iCAArray; ++i) + { + LPCWSTR wzCA = wzCAArray[i % iCAArray]; + if (NULL == wzCA || NULL == wzCA[0]) continue; + WcaLog(LOGMSG_STANDARD, "Requesting SSL certificate from %ls", wzCA); + hr = RequestCertificate(pwzName, iStore, iStoreLocation, wzComputerName, wzDistinguishedName, wzCA, pbstrCertificate); + if (hr == S_OK && pbstrCertificate) + { + // set the friendly name + CRYPT_HASH_BLOB hblob; + CERT_BLOB blob; + HCERTSTORE hCertStore = NULL; + PCCERT_CONTEXT pCertCtxExisting = NULL; + + blob.pbData = (BYTE*)pwzName; + blob.cbData = (lstrlenW(pwzName) + 1) * sizeof(pwzName[0]); // including terminating null + + *pcbCertificate = SysStringByteLen(*pbstrCertificate); + hr = CertificateToHash(*pbstrCertificate, pbaHashBuffer); + ExitOnFailure(hr, "failed to CertificateToHash for an existing certificate"); + + hblob.pbData = pbaHashBuffer; + hblob.cbData = CB_CERTIFICATE_HASH; + + hCertStore = CertOpenStore(CERT_STORE_PROV_SYSTEM, 0, NULL, (iStoreLocation << CERT_SYSTEM_STORE_LOCATION_SHIFT), StoreMapping(iStore)); + MessageExitOnNullWithLastError(hCertStore, hr, msierrCERTFailedOpen, "failed to open certificate store"); + + pCertCtxExisting = CertFindCertificateInStore( + hCertStore, + PKCS_7_ASN_ENCODING | X509_ASN_ENCODING, + 0, + CERT_FIND_HASH, + &hblob, + NULL); + + if (pCertCtxExisting) + { + CertSetCertificateContextProperty( + pCertCtxExisting, + CERT_FRIENDLY_NAME_PROP_ID, + 0, + &blob); + } + + if (pCertCtxExisting) + { + CertFreeCertificateContext(pCertCtxExisting); + pCertCtxExisting = NULL; + } + if (hCertStore) + { + CertCloseStore(hCertStore, 0); + hCertStore = NULL; + } + ExitFunction(); + } + if (pbstrCertificate && *pbstrCertificate) + { + SysFreeString(*pbstrCertificate); + pbstrCertificate = NULL; + } + } + hr = E_FAIL; + ExitOnFailure(hr, "failed to RequestCertificate"); + +LExit: + if (wzCABuffer) + { + delete wzCABuffer; + } + if (wzCAArray) + { + delete wzCAArray; + } + + return hr; +} + + +HRESULT ScaGetCertificateByRequest(LPCWSTR pwzName, BOOL fIsInstalling, BOOL fIsUninstalling, + INT iStore, INT iStoreLocation, + LPCWSTR wzDistinguishedName, LPCWSTR wzCA, + BSTR* pbstrCertificate, DWORD* pcbCertificate, BYTE* pbaHashBuffer) +{ + HRESULT hr = S_OK; + WCHAR wzComputerName[MAX_COMPUTER_NAME] = {0}; + WCHAR* pwzData = NULL; + DWORD cchData = 0; + + // override %COMPUTERNAME% with DOMAINNAME property + hr = WcaGetProperty( L"DOMAINNAME", &pwzData); + ExitOnFailure(hr, "Failed to get Property DOMAINNAME"); + if (*pwzData) + { + // if DOMAINNAME is set, use it + ::StringCchCopyW(wzComputerName, MAX_COMPUTER_NAME, pwzData); + } + else + { + // otherwise get the intranet name given by %COMPUTERNAME% + GetEnvironmentVariableW(L"COMPUTERNAME", wzComputerName, MAX_COMPUTER_NAME); + } + + hr = ScaSslExistingCertificateByName(pwzName, iStore, iStoreLocation, pbstrCertificate, pcbCertificate, pbaHashBuffer); + ExitOnFailure(hr, "Failed ScaSslExistingCertificateByName"); + if (S_OK != hr) + { + if (!fIsUninstalling && fIsInstalling) + { + // if no existing cert and not on uninstall, hit the authority + WcaLog(LOGMSG_STANDARD, "Adding certificate: requested, %ls", wzDistinguishedName); + hr = ScaSslNewCertificate(pwzName, iStore, iStoreLocation, wzComputerName, wzDistinguishedName, wzCA, + pbstrCertificate, pcbCertificate, pbaHashBuffer); + ExitOnFailure(hr, "Failed ScaSslNewCertificate"); + } + else + { + // if no existing cert and uninstall + hr = S_OK; + } + } + +LExit: + ReleaseStr(pwzData); + + return hr; +} + + +HRESULT ScaInstallCertificateByContext(LPCWSTR pwzName, INT iStore, INT iStoreLocation, + PCCERT_CONTEXT pCertContext) +{ + HRESULT hr = S_OK; + HCERTSTORE hCertStore = NULL; + DWORD dwFlags = iStoreLocation << CERT_SYSTEM_STORE_LOCATION_SHIFT; + CERT_BLOB blob; + + hCertStore = CertOpenStore(CERT_STORE_PROV_SYSTEM, 0, NULL, dwFlags, StoreMapping(iStore)); + if (hCertStore == NULL) + MessageExitOnLastError(hr, msierrCERTFailedOpen, "failed to open certificate store"); + + blob.pbData = (BYTE*)pwzName; + blob.cbData = (lstrlenW(pwzName) + 1) * sizeof(pwzName[0]); // including terminating null + CertSetCertificateContextProperty( + pCertContext, + CERT_FRIENDLY_NAME_PROP_ID, + 0, + &blob); + + if (!CertAddCertificateContextToStore( + hCertStore, + pCertContext, + CERT_STORE_ADD_REPLACE_EXISTING, + NULL)) + { + hr = E_FAIL; + MessageExitOnLastError(hr, msierrCERTFailedAdd, "failed to add certificate to the store"); + } + +LExit: + if (hCertStore) + { + CertCloseStore(hCertStore, 0); + hCertStore = NULL; + } + + return hr; +} + + +HRESULT ScaGetCertificateByPath(LPCWSTR pwzName, BOOL fIsInstalling, BOOL fIsUninstalling, + INT iStore, INT iStoreLocation, LPCWSTR wzSslCertificate, LPCWSTR wzPFXPassword, + BSTR* pbstrCertificate, DWORD* pcbCertificate, BYTE* pbaHashBuffer) +{ + Assert(wzSslCertificate); + HRESULT hr = S_OK; + PCCERT_CONTEXT pCertContext = NULL; + DWORD dwEncodingType = 0; + DWORD dwContentType = 0; + DWORD dwFormatType = 0; + DWORD dwHashSize = CB_CERTIFICATE_HASH; + HANDLE hPfxFile = INVALID_HANDLE_VALUE; + CRYPT_DATA_BLOB blob; + + blob.pbData = NULL; + blob.cbData = 0; + + if (wzSslCertificate && wzSslCertificate[0] != 0) + { + if (!::CryptQueryObject(CERT_QUERY_OBJECT_FILE, (LPVOID)wzSslCertificate, CERT_QUERY_CONTENT_FLAG_ALL, CERT_QUERY_FORMAT_FLAG_ALL, + 0, &dwEncodingType, &dwContentType, &dwFormatType, NULL, NULL, (LPCVOID*)&pCertContext)) + hr = fIsUninstalling ? S_FALSE : HRESULT_FROM_WIN32(::GetLastError()); // don't fail on uninstall + ExitOnFailure(hr, "failed CryptQueryObject"); + } + else + { + hr = S_FALSE; + ExitFunction(); + } + + if (!pCertContext) + { + // this is a pfx? + // make sure to exit this block of code properly for clean up blob.pbData + if (dwContentType & CERT_QUERY_CONTENT_PFX) + { + DWORD iSize = 0, iReadSize = 0; + HCERTSTORE hPfxCertStore = NULL; + + hPfxFile = ::CreateFileW(wzSslCertificate, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, + NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); + hr = (hPfxFile != INVALID_HANDLE_VALUE) ? S_OK : E_FAIL; + ExitOnFailure(hr, "failed CryptQueryObject, file handle is null"); + iSize = ::GetFileSize(hPfxFile, NULL); + hr = (iSize > 0) ? S_OK : E_FAIL; + ExitOnFailure(hr, "failed CryptQueryObject, file size is 0"); + blob.pbData = new BYTE[iSize]; + blob.cbData = iSize; + hr = (blob.pbData) ? S_OK : E_FAIL; + ExitOnFailure(hr, "out of memory for blob"); + + if (::ReadFile(hPfxFile, (LPVOID)blob.pbData, iSize, &iReadSize, NULL)) + { + hPfxCertStore = PFXImportCertStore((CRYPT_DATA_BLOB*)&blob, wzPFXPassword, + (iStoreLocation == SCA_CERTSYSTEMSTORE_CURRENTUSER) ? CRYPT_USER_KEYSET : CRYPT_MACHINE_KEYSET); + if (hPfxCertStore) + { + pCertContext = CertEnumCertificatesInStore(hPfxCertStore, NULL); + // work only with the first certificate in pfx + if (pCertContext) + { + hr = CertGetCertificateContextProperty(pCertContext, CERT_SHA1_HASH_PROP_ID, (VOID*)pbaHashBuffer, &dwHashSize) + ? S_OK : E_FAIL; + ExitOnFailure(hr, "failed CertGetCertificateContextProperty CERT_SHA1_HASH_PROP_ID"); + ReleaseBSTR(*pbstrCertificate); + + *pbstrCertificate = SysAllocStringByteLen((LPCSTR)(pCertContext->pbCertEncoded), pCertContext->cbCertEncoded); + *pcbCertificate = pCertContext->cbCertEncoded; + if (fIsInstalling) + { + // install the certificate, cannot defer because the data required cannot be passed + hr = ScaInstallCertificateByContext(pwzName, iStore, iStoreLocation, pCertContext); + } + } + else + hr = E_FAIL; + } + else + hr = E_FAIL; + } + else + hr = E_FAIL; + } + else + { + ExitOnFailure(hr = E_FAIL, "failed CryptQueryObject, unknown data"); + } + } + else + { + // return cert and its hash + hr = CertGetCertificateContextProperty(pCertContext, CERT_SHA1_HASH_PROP_ID, (VOID*)pbaHashBuffer, &dwHashSize) + ? S_OK : E_FAIL; + ExitOnFailure(hr, "failed CertGetCertificateContextProperty CERT_SHA1_HASH_PROP_ID"); + ReleaseBSTR(*pbstrCertificate); + + *pbstrCertificate = SysAllocStringByteLen((LPCSTR)(pCertContext->pbCertEncoded), pCertContext->cbCertEncoded); + *pcbCertificate = pCertContext->cbCertEncoded; + if (fIsInstalling) + { + // install the certificate, cannot defer because the data required cannot be passed + hr = ScaInstallCertificateByContext(pwzName, iStore, iStoreLocation, pCertContext); + } + } + +LExit: + if (pCertContext) + { + CertFreeCertificateContext(pCertContext); + pCertContext = NULL; + } + if (hPfxFile != INVALID_HANDLE_VALUE) + { + CloseHandle(hPfxFile); + hPfxFile = INVALID_HANDLE_VALUE; + } + if (blob.pbData) + { + delete [] blob.pbData; + blob.pbData = NULL; + blob.cbData = 0; + } + + return hr; +} + + +HRESULT ScaInstallCertificateByBinaryData(BOOL fAddCert, INT iStore, INT iStoreLocation, LPCWSTR wzName, BYTE* pwzData, DWORD cchData, + LPCWSTR wzPFXPassword) +{ + Assert(wzName); + Assert(pwzData); + Assert(cchData); + HRESULT hr = S_OK; + HCERTSTORE hCertStore = NULL, hPfxCertStore = NULL; + PCCERT_CONTEXT pCertCtx = NULL, pCertCtxExisting = NULL; + DWORD dwFlags, dwEncodingType, dwContentType, dwFormatType; + CERT_BLOB blob; + LPCWSTR wzStore = StoreMapping(iStore); + + dwFlags = iStoreLocation << CERT_SYSTEM_STORE_LOCATION_SHIFT; + hCertStore = CertOpenStore(CERT_STORE_PROV_SYSTEM, 0, NULL, dwFlags, wzStore); + MessageExitOnNullWithLastError(hCertStore, hr, msierrCERTFailedOpen, "failed to open certificate store"); + + blob.pbData = pwzData; + blob.cbData = cchData; + + if (!::CryptQueryObject(CERT_QUERY_OBJECT_BLOB, &blob, CERT_QUERY_CONTENT_FLAG_ALL, CERT_QUERY_FORMAT_FLAG_ALL, + 0, &dwEncodingType, &dwContentType, &dwFormatType, NULL, NULL, (LPCVOID*)&pCertCtx)) + ExitOnLastError(hr, "failed to parse the certificate blob"); + ExitOnNull(pCertCtx, hr, E_UNEXPECTED, "failed to parse the certificate blob"); + + blob.pbData = (BYTE*)wzName; + blob.cbData = (lstrlenW(wzName) + 1) * sizeof(wzName[0]); // including terminating null + + CertSetCertificateContextProperty( + pCertCtx, + CERT_FRIENDLY_NAME_PROP_ID, + 0, + &blob); + + if (fAddCert) + { + // Add + WcaLog(LOGMSG_STANDARD, "Adding certificate: binary name, %ls", wzName); + if (!CertAddCertificateContextToStore( + hCertStore, + pCertCtx, + CERT_STORE_ADD_REPLACE_EXISTING, + NULL)) + { + hr = E_FAIL; + MessageExitOnLastError(hr, msierrCERTFailedAdd, "failed to add certificate to the store"); + } + } + else + { + // Delete + WcaLog(LOGMSG_STANDARD, "Deleting certificate provided: binary name, %ls", wzName); + pCertCtxExisting = CertFindCertificateInStore( + hCertStore, + PKCS_7_ASN_ENCODING | X509_ASN_ENCODING, + 0, + CERT_FIND_EXISTING, + pCertCtx, + NULL); + + if (pCertCtxExisting) + { + if (!CertDeleteCertificateFromStore(pCertCtxExisting)) + { + ExitOnLastError(hr, "failed to delete certificate"); + } + else + { + pCertCtxExisting = NULL; + } + } + } + +LExit: + if (pCertCtx) + { + CertFreeCertificateContext(pCertCtx); + pCertCtx = NULL; + } + if (pCertCtxExisting) + { + CertFreeCertificateContext(pCertCtxExisting); + pCertCtxExisting = NULL; + } + // order is important for store + if (hCertStore) + { + CertCloseStore(hCertStore, 0); + hCertStore = NULL; + } + if (hPfxCertStore) + { + CertCloseStore(hPfxCertStore, 0); + hPfxCertStore = NULL; + } + + return hr; +} +*/ diff --git a/src/ext/Iis/ca/scacert.h b/src/ext/Iis/ca/scacert.h new file mode 100644 index 00000000..39b00d3d --- /dev/null +++ b/src/ext/Iis/ca/scacert.h @@ -0,0 +1,23 @@ +#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 CB_CERTIFICATE_HASH 20 + +// Certificate.Attribute +enum SCA_CERT_ATTRIBUTES +{ + SCA_CERT_ATTRIBUTE_DEFAULT = 0, + SCA_CERT_ATTRIBUTE_REQUEST = 1, + SCA_CERT_ATTRIBUTE_BINARYDATA = 2, + SCA_CERT_ATTRIBUTE_OVERWRITE = 4, + SCA_CERT_ATTRIBUTE_VITAL = 8, +}; + + +// Certificate.StoreLocation +enum SCA_CERTSYSTEMSTORE +{ + SCA_CERTSYSTEMSTORE_CURRENTUSER = 1, + SCA_CERTSYSTEMSTORE_LOCALMACHINE = 2, +}; diff --git a/src/ext/Iis/ca/scacertexec.cpp b/src/ext/Iis/ca/scacertexec.cpp new file mode 100644 index 00000000..95870c79 --- /dev/null +++ b/src/ext/Iis/ca/scacertexec.cpp @@ -0,0 +1,431 @@ +// 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" + +#define SIXTY_FOUR_MEG 64 * 1024 * 1024 + +// prototypes +static HRESULT ExecuteCertificateOperation( + __in MSIHANDLE hInstall, + __in SCA_ACTION saAction, + __in DWORD dwStoreRoot + ); + +static HRESULT ReadCertificateFile( + __in LPCWSTR wzPath, + __out BYTE** prgbData, + __out DWORD* pcbData + ); + +static HRESULT InstallCertificatePackage( + __in HCERTSTORE hStore, + __in BOOL fUserCertificateStore, + __in LPCWSTR wzName, + __in_opt BYTE* rgbData, + __in DWORD cbData, + __in BOOL fVital, + __in_opt LPCWSTR wzPFXPassword + ); + +static HRESULT UninstallCertificatePackage( + __in HCERTSTORE hStore, + __in BOOL fUserCertificateStore, + __in LPCWSTR wzName + ); + +static HRESULT AddCertificate( + __in HCERTSTORE hStore, + __in PCCERT_CONTEXT pCertContext, + __in LPCWSTR wzCertificateUniqueName, + __in BOOL fVital +); + +/* **************************************************************** + AddUserCertificate - CUSTOM ACTION ENTRY POINT for adding per-user + certificates + + * ***************************************************************/ +extern "C" UINT __stdcall AddUserCertificate( + __in MSIHANDLE hInstall + ) +{ + HRESULT hr = S_OK; + DWORD er = ERROR_SUCCESS; + + hr = WcaInitialize(hInstall, "AddUserCertificate"); + ExitOnFailure(hr, "Failed to initialize AddUserCertificate."); + + hr = ExecuteCertificateOperation(hInstall, SCA_ACTION_INSTALL, CERT_SYSTEM_STORE_CURRENT_USER); + ExitOnFailure(hr, "Failed to install per-user certificate."); + +LExit: + er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE; + return WcaFinalize(er); +} + + +/* **************************************************************** + AddMachineCertificate - CUSTOM ACTION ENTRY POINT for adding + per-machine certificates + + * ***************************************************************/ +extern "C" UINT __stdcall AddMachineCertificate( + __in MSIHANDLE hInstall + ) +{ + HRESULT hr = S_OK; + DWORD er = ERROR_SUCCESS; + + hr = WcaInitialize(hInstall, "AddMachineCertificate"); + ExitOnFailure(hr, "Failed to initialize AddMachineCertificate."); + + hr = ExecuteCertificateOperation(hInstall, SCA_ACTION_INSTALL, CERT_SYSTEM_STORE_LOCAL_MACHINE); + ExitOnFailure(hr, "Failed to install per-machine certificate."); + +LExit: + er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE; + return WcaFinalize(er); +} + + +/* **************************************************************** + DeleteUserCertificate - CUSTOM ACTION ENTRY POINT for deleting + per-user certificates + + * ***************************************************************/ +extern "C" UINT __stdcall DeleteUserCertificate( + __in MSIHANDLE hInstall + ) +{ + HRESULT hr = S_OK; + DWORD er = ERROR_SUCCESS; + + hr = WcaInitialize(hInstall, "DeleteUserCertificate"); + ExitOnFailure(hr, "Failed to initialize DeleteUserCertificate."); + + hr = ExecuteCertificateOperation(hInstall, SCA_ACTION_UNINSTALL, CERT_SYSTEM_STORE_CURRENT_USER); + ExitOnFailure(hr, "Failed to uninstall per-user certificate."); + +LExit: + er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE; + return WcaFinalize(er); +} + + +/* **************************************************************** + DeleteMachineCertificate - CUSTOM ACTION ENTRY POINT for deleting + per-machine certificates + + * ***************************************************************/ +extern "C" UINT __stdcall DeleteMachineCertificate( + __in MSIHANDLE hInstall + ) +{ + HRESULT hr = S_OK; + DWORD er = ERROR_SUCCESS; + + hr = WcaInitialize(hInstall, "DeleteMachineCertificate"); + ExitOnFailure(hr, "Failed to initialize DeleteMachineCertificate."); + + hr = ExecuteCertificateOperation(hInstall, SCA_ACTION_UNINSTALL, CERT_SYSTEM_STORE_LOCAL_MACHINE); + ExitOnFailure(hr, "Failed to uninstall per-machine certificate."); + +LExit: + er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE; + return WcaFinalize(er); +} + + +static HRESULT ExecuteCertificateOperation( + __in MSIHANDLE /*hInstall*/, + __in SCA_ACTION saAction, + __in DWORD dwStoreLocation + ) +{ + //AssertSz(FALSE, "Debug ExecuteCertificateOperation() here."); + Assert(saAction & SCA_ACTION_INSTALL || saAction & SCA_ACTION_UNINSTALL); + + HRESULT hr = S_OK; + LPWSTR pwzCaData = NULL; + LPWSTR pwz; + LPWSTR pwzName = NULL; + LPWSTR pwzStore = NULL; + int iAttributes = 0; + LPWSTR pwzPFXPassword = NULL; + LPWSTR pwzFilePath = NULL; + BYTE* pbData = NULL; + DWORD cbData = 0; + DWORD_PTR cbPFXPassword = 0; + + BOOL fUserStoreLocation = (CERT_SYSTEM_STORE_CURRENT_USER == dwStoreLocation); + HCERTSTORE hCertStore = NULL; + + hr = WcaGetProperty(L"CustomActionData", &pwzCaData); + ExitOnFailure(hr, "Failed to get CustomActionData"); + + WcaLog(LOGMSG_TRACEONLY, "CustomActionData: %ls", pwzCaData); + + pwz = pwzCaData; + hr = WcaReadStringFromCaData(&pwz, &pwzName); + ExitOnFailure(hr, "Failed to parse certificate name."); + hr = WcaReadStringFromCaData(&pwz, &pwzStore); + ExitOnFailure(hr, "Failed to parse CustomActionData, StoreName"); + hr = WcaReadIntegerFromCaData(&pwz, &iAttributes); + ExitOnFailure(hr, "Failed to parse certificate attribute"); + if (SCA_ACTION_INSTALL == saAction) // install operations need more data + { + hr = WcaReadStreamFromCaData(&pwz, &pbData, (DWORD_PTR*)&cbData); + ExitOnFailure(hr, "Failed to parse certificate stream."); + + hr = WcaReadStringFromCaData(&pwz, &pwzPFXPassword); + ExitOnFailure(hr, "Failed to parse certificate password."); + } + + // Open the right store. + hCertStore = ::CertOpenStore(CERT_STORE_PROV_SYSTEM, 0, NULL, dwStoreLocation | CERT_STORE_MAXIMUM_ALLOWED_FLAG, pwzStore); + MessageExitOnNullWithLastError(hCertStore, hr, msierrCERTFailedOpen, "Failed to open certificate store: %ls", pwzStore); + + if (SCA_ACTION_INSTALL == saAction) // install operations need more data + { + // Uninstall existing versions of this package. Ignore any failures + // This is needed to clean up the private key of a cert when we replace an existing cert + // CertAddCertificateContextToStore(CERT_STORE_ADD_REPLACE_EXISTING) does not remove the private key if the cert is replaced + UninstallCertificatePackage(hCertStore, fUserStoreLocation, pwzName); + + hr = InstallCertificatePackage(hCertStore, fUserStoreLocation, pwzName, pbData, cbData, iAttributes & SCA_CERT_ATTRIBUTE_VITAL, pwzPFXPassword); + ExitOnFailure(hr, "Failed to install certificate."); + } + else + { + Assert(SCA_ACTION_UNINSTALL == saAction); + + hr = UninstallCertificatePackage(hCertStore, fUserStoreLocation, pwzName); + ExitOnFailure(hr, "Failed to uninstall certificate."); + } + +LExit: + if (NULL != pwzPFXPassword && SUCCEEDED(StrSize(pwzPFXPassword, &cbPFXPassword))) + { + SecureZeroMemory(pwzPFXPassword, cbPFXPassword); + } + + if (hCertStore) + { + if (!::CertCloseStore(hCertStore, CERT_CLOSE_STORE_CHECK_FLAG)) + { + WcaLog(LOGMSG_VERBOSE, "Cert store was closed but not all resources were freed. Error 0x%x", GetLastError()); + } + } + + ReleaseMem(pbData); + ReleaseStr(pwzFilePath); + ReleaseStr(pwzPFXPassword); + ReleaseStr(pwzStore); + ReleaseStr(pwzName); + ReleaseStr(pwzCaData); + return hr; +} + + +static HRESULT InstallCertificatePackage( + __in HCERTSTORE hStore, + __in BOOL fUserCertificateStore, + __in LPCWSTR wzName, + __in_opt BYTE* rgbData, + __in DWORD cbData, + __in BOOL fVital, + __in_opt LPCWSTR wzPFXPassword + ) +{ + HRESULT hr = S_OK; + + HCERTSTORE hPfxCertStore = NULL; + PCCERT_CONTEXT pCertContext = NULL; + CERT_BLOB blob = { 0 }; + DWORD dwKeyset = fUserCertificateStore ? CRYPT_USER_KEYSET : CRYPT_MACHINE_KEYSET; + DWORD dwEncodingType; + DWORD dwContentType; + DWORD dwFormatType; + LPWSTR pwzUniqueName = NULL; + int iUniqueId = 0; + + // Figure out what type of blob (certificate or PFX) we're dealing with here. + blob.pbData = rgbData; + blob.cbData = cbData; + + if (!::CryptQueryObject(CERT_QUERY_OBJECT_BLOB, &blob, CERT_QUERY_CONTENT_FLAG_ALL, CERT_QUERY_FORMAT_FLAG_ALL, 0, &dwEncodingType, &dwContentType, &dwFormatType, NULL, NULL, (LPCVOID*)&pCertContext)) + { + ExitWithLastError(hr, "Failed to parse the certificate blob: %ls", wzName); + } + + hr = StrAllocFormatted(&pwzUniqueName, L"%s_wixCert_%d", wzName, ++iUniqueId); + ExitOnFailure(hr, "Failed to format unique name"); + + if (!pCertContext) + { + // If we have a PFX blob, get the first certificate out of the PFX and use that instead of the PFX. + if (dwContentType & CERT_QUERY_CONTENT_PFX) + { + ExitOnNull(wzPFXPassword, hr, E_INVALIDARG, "Failed to import PFX blob because no password was provided"); + + // If we fail and our password is blank, also try passing in NULL for the password (according to the docs) + hPfxCertStore = ::PFXImportCertStore((CRYPT_DATA_BLOB*)&blob, wzPFXPassword, dwKeyset); + if (NULL == hPfxCertStore && !*wzPFXPassword) + { + hPfxCertStore = ::PFXImportCertStore((CRYPT_DATA_BLOB*)&blob, NULL, dwKeyset); + } + ExitOnNullWithLastError(hPfxCertStore, hr, "Failed to open PFX file."); + + // Install all certificates in the PFX + for (pCertContext = ::CertEnumCertificatesInStore(hPfxCertStore, pCertContext); + pCertContext; + pCertContext = ::CertEnumCertificatesInStore(hPfxCertStore, pCertContext)) + { + hr = AddCertificate(hStore, pCertContext, pwzUniqueName, fVital); + MessageExitOnFailure(hr, msierrCERTFailedAdd, "Failed to add certificate to the store."); + + hr = StrAllocFormatted(&pwzUniqueName, L"%s_wixCert_%d", wzName, ++iUniqueId); + ExitOnFailure(hr, "Failed to format unique name"); + } + } + else + { + hr = E_UNEXPECTED; + ExitOnFailure(hr, "Unexpected certificate type processed."); + } + } + else + { + hr = AddCertificate(hStore, pCertContext, pwzUniqueName, fVital); + MessageExitOnFailure(hr, msierrCERTFailedAdd, "Failed to add certificate to the store."); + } + + hr = WcaProgressMessage(COST_CERT_ADD, FALSE); + ExitOnFailure(hr, "Failed to send install progress message."); + +LExit: + ReleaseStr(pwzUniqueName); + + if (pCertContext) + { + ::CertFreeCertificateContext(pCertContext); + } + + // Close the stores after the context's are released. + if (hPfxCertStore) + { + if (!::CertCloseStore(hPfxCertStore, CERT_CLOSE_STORE_CHECK_FLAG)) + { + WcaLog(LOGMSG_VERBOSE, "PFX cert store was closed but not all resources were freed. Error 0x%x", GetLastError()); + } + } + + return hr; +} + + +static HRESULT UninstallCertificatePackage( + __in HCERTSTORE hStore, + __in BOOL fUserCertificateStore, + __in LPCWSTR wzName + ) +{ + HRESULT hr = S_OK; + DWORD er = ERROR_SUCCESS; + PCCERT_CONTEXT pCertContext = NULL; + CRYPT_KEY_PROV_INFO* pPrivateKeyInfo = NULL; + LPWSTR pwzUniquePrefix = NULL; + int ccUniquePrefix = 0; + + hr = StrAllocFormatted(&pwzUniquePrefix, L"%s_wixCert_", wzName); + ExitOnFailure(hr, "Failed to format unique name"); + ccUniquePrefix = ::lstrlenW(pwzUniquePrefix); + + WcaLog(LOGMSG_STANDARD, "Deleting certificate that begin with friendly name: %ls", pwzUniquePrefix); + + // Loop through all certificates in the store, deleting the ones that begin with our prefix. + while (NULL != (pCertContext = ::CertFindCertificateInStore(hStore, PKCS_7_ASN_ENCODING | X509_ASN_ENCODING, 0, CERT_FIND_ANY, NULL, pCertContext))) + { + WCHAR wzFriendlyName[256] = { 0 }; + DWORD cbFriendlyName = sizeof(wzFriendlyName); + + if (::CertGetCertificateContextProperty(pCertContext, CERT_FRIENDLY_NAME_PROP_ID, reinterpret_cast(wzFriendlyName), &cbFriendlyName) && + lstrlenW(wzFriendlyName) >= ccUniquePrefix && + CSTR_EQUAL == ::CompareStringW(LOCALE_SYSTEM_DEFAULT, 0, pwzUniquePrefix, ccUniquePrefix, wzFriendlyName, ccUniquePrefix)) + { + PCCERT_CONTEXT pCertContextDelete = ::CertDuplicateCertificateContext(pCertContext); // duplicate the context so we can delete it with out disrupting the looping + if(pCertContextDelete) + { + // Delete the certificate and if successful delete the matching private key as well. + if (::CertDeleteCertificateFromStore(pCertContextDelete)) + { + // If we found private key info, delete it. + hr = CertReadProperty(pCertContextDelete, CERT_KEY_PROV_INFO_PROP_ID, &pPrivateKeyInfo, NULL); + if (SUCCEEDED(hr)) + { + HCRYPTPROV hProvIgnored = NULL; // ignored on deletes. + DWORD dwKeyset = fUserCertificateStore ? CRYPT_USER_KEYSET : CRYPT_MACHINE_KEYSET; + + if (!::CryptAcquireContextW(&hProvIgnored, pPrivateKeyInfo->pwszContainerName, pPrivateKeyInfo->pwszProvName, pPrivateKeyInfo->dwProvType, dwKeyset | CRYPT_DELETEKEYSET | CRYPT_SILENT)) + { + er = ::GetLastError(); + hr = HRESULT_FROM_WIN32(er); + } + + ReleaseNullMem(pPrivateKeyInfo); + } + else // don't worry about failures to delete private keys. + { + hr = S_OK; + } + } + else + { + er = ::GetLastError(); + hr = HRESULT_FROM_WIN32(er); + } + + if (FAILED(hr)) + { + WcaLog(LOGMSG_STANDARD, "Failed to delete certificate with friendly name: %ls, continuing anyway. Error: 0x%x", wzFriendlyName, hr); + } + + pCertContextDelete = NULL; + } + } + } + + hr = WcaProgressMessage(COST_CERT_DELETE, FALSE); + ExitOnFailure(hr, "Failed to send uninstall progress message."); + +LExit: + ReleaseStr(pwzUniquePrefix); + ReleaseMem(pPrivateKeyInfo); + if(pCertContext) + { + ::CertFreeCertificateContext(pCertContext); + } + + return hr; +} + +static HRESULT AddCertificate( + __in HCERTSTORE hStore, + __in PCCERT_CONTEXT pCertContext, + __in LPCWSTR wzCertificateUniqueName, + __in BOOL fVital +) +{ + HRESULT hr = S_OK; + + WcaLog(LOGMSG_STANDARD, "Adding certificate: %ls", wzCertificateUniqueName); + + hr = CertInstallSingleCertificate(hStore, pCertContext, wzCertificateUniqueName); + if (FAILED(hr) && !fVital) + { + WcaLog(LOGMSG_STANDARD, "Could not add non-vital certificate: %ls due to error: 0x%x, continuing...", wzCertificateUniqueName, hr); + hr = S_FALSE; + } + + return hr; +} diff --git a/src/ext/Iis/ca/scacost.h b/src/ext/Iis/ca/scacost.h new file mode 100644 index 00000000..be92cf26 --- /dev/null +++ b/src/ext/Iis/ca/scacost.h @@ -0,0 +1,15 @@ +#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. + + +const UINT COST_IIS_TRANSACTIONS = 10000; + +const UINT COST_IIS_CREATEKEY = 5000; +const UINT COST_IIS_DELETEKEY = 5000; +const UINT COST_IIS_WRITEVALUE = 5000; +const UINT COST_IIS_DELETEVALUE = 5000; +const UINT COST_IIS_CREATEAPP = 5000; +const UINT COST_IIS_DELETEAPP = 5000; + +const UINT COST_CERT_ADD = 5000; +const UINT COST_CERT_DELETE = 5000; diff --git a/src/ext/Iis/ca/scaexec.cpp b/src/ext/Iis/ca/scaexec.cpp new file mode 100644 index 00000000..df25e8db --- /dev/null +++ b/src/ext/Iis/ca/scaexec.cpp @@ -0,0 +1,1184 @@ +// 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" + + +/******************************************************************** + StartMetabaseTransaction - CUSTOM ACTION ENTRY POINT for backing up metabase + + Input: deferred CustomActionData - BackupName +********************************************************************/ +extern "C" UINT __stdcall StartMetabaseTransaction(MSIHANDLE hInstall) +{ +//AssertSz(FALSE, "debug StartMetabaseTransaction here"); + HRESULT hr = S_OK; + UINT er = ERROR_SUCCESS; + + IMSAdminBase* piMetabase = NULL; + LPWSTR pwzData = NULL; + BOOL fInitializedCom = FALSE; + + // initialize + hr = WcaInitialize(hInstall, "StartMetabaseTransaction"); + ExitOnFailure(hr, "failed to initialize StartMetabaseTransaction"); + + hr = ::CoInitialize(NULL); + ExitOnFailure(hr, "failed to initialize COM"); + fInitializedCom = TRUE; + hr = ::CoCreateInstance(CLSID_MSAdminBase, NULL, CLSCTX_ALL, IID_IMSAdminBase, reinterpret_cast(&piMetabase)); + if (hr == REGDB_E_CLASSNOTREG) + { + WcaLog(LOGMSG_STANDARD, "Failed to get IIMSAdminBase to backup - continuing"); + hr = S_OK; + } + else + { + MessageExitOnFailure(hr, msierrIISCannotConnect, "failed to get IID_IIMSAdminBase object"); + + hr = WcaGetProperty(L"CustomActionData", &pwzData); + ExitOnFailure(hr, "failed to get CustomActionData"); + + // back up the metabase + Assert(lstrlenW(pwzData) < MD_BACKUP_MAX_LEN); + + // MD_BACKUP_OVERWRITE = Overwrite if a backup of the same name and version exists in the backup location + hr = piMetabase->Backup(pwzData, MD_BACKUP_NEXT_VERSION, MD_BACKUP_OVERWRITE | MD_BACKUP_FORCE_BACKUP | MD_BACKUP_SAVE_FIRST); + if (MD_WARNING_SAVE_FAILED == hr) + { + WcaLog(LOGMSG_STANDARD, "Failed to save metabase before backing up - continuing"); + hr = S_OK; + } + MessageExitOnFailure(hr, msierrIISFailedStartTransaction, "failed to begin metabase transaction: '%ls'", pwzData); + } + hr = WcaProgressMessage(COST_IIS_TRANSACTIONS, FALSE); +LExit: + ReleaseStr(pwzData); + ReleaseObject(piMetabase); + + if (fInitializedCom) + { + ::CoUninitialize(); + } + + if (FAILED(hr)) + er = ERROR_INSTALL_FAILURE; + return WcaFinalize(er); +} + + +/******************************************************************** + RollbackMetabaseTransaction - CUSTOM ACTION ENTRY POINT for unbacking up metabase + + Input: deferred CustomActionData - BackupName +********************************************************************/ +extern "C" UINT __stdcall RollbackMetabaseTransaction(MSIHANDLE hInstall) +{ +//AssertSz(FALSE, "debug RollbackMetabaseTransaction here"); + HRESULT hr = S_OK; + UINT er = ERROR_SUCCESS; + + IMSAdminBase* piMetabase = NULL; + LPWSTR pwzData = NULL; + BOOL fInitializedCom = FALSE; + + hr = WcaInitialize(hInstall, "RollbackMetabaseTransaction"); + ExitOnFailure(hr, "failed to initialize"); + + hr = ::CoInitialize(NULL); + ExitOnFailure(hr, "failed to initialize COM"); + fInitializedCom = TRUE; + hr = ::CoCreateInstance(CLSID_MSAdminBase, NULL, CLSCTX_ALL, IID_IMSAdminBase, reinterpret_cast(&piMetabase)); + if (hr == REGDB_E_CLASSNOTREG) + { + WcaLog(LOGMSG_STANDARD, "Failed to get IIMSAdminBase to rollback - continuing"); + hr = S_OK; + ExitFunction(); + } + ExitOnFailure(hr, "failed to get IID_IIMSAdminBase object"); + + + hr = WcaGetProperty( L"CustomActionData", &pwzData); + ExitOnFailure(hr, "failed to get CustomActionData"); + + hr = piMetabase->Restore(pwzData, MD_BACKUP_HIGHEST_VERSION, 0); + ExitOnFailure(hr, "failed to rollback metabase transaction: '%ls'", pwzData); + + hr = piMetabase->DeleteBackup(pwzData, MD_BACKUP_HIGHEST_VERSION); + ExitOnFailure(hr, "failed to cleanup metabase transaction '%ls', continuing", pwzData); + +LExit: + ReleaseStr(pwzData); + ReleaseObject(piMetabase); + + if (fInitializedCom) + { + ::CoUninitialize(); + } + + if (FAILED(hr)) + { + er = ERROR_INSTALL_FAILURE; + } + return WcaFinalize(er); +} + + +/******************************************************************** + CommitMetabaseTransaction - CUSTOM ACTION ENTRY POINT for unbacking up metabase + + Input: deferred CustomActionData - BackupName + * *****************************************************************/ +extern "C" UINT __stdcall CommitMetabaseTransaction(MSIHANDLE hInstall) +{ + HRESULT hr = S_OK; + UINT er = ERROR_SUCCESS; + + IMSAdminBase* piMetabase = NULL; + LPWSTR pwzData = NULL; + BOOL fInitializedCom = FALSE; + + hr = WcaInitialize(hInstall, "CommitMetabaseTransaction"); + ExitOnFailure(hr, "failed to initialize"); + + hr = ::CoInitialize(NULL); + ExitOnFailure(hr, "failed to initialize COM"); + fInitializedCom = TRUE; + hr = ::CoCreateInstance(CLSID_MSAdminBase, NULL, CLSCTX_ALL, IID_IMSAdminBase, reinterpret_cast(&piMetabase)); + if (hr == REGDB_E_CLASSNOTREG) + { + WcaLog(LOGMSG_STANDARD, "Failed to get IIMSAdminBase to commit - continuing"); + hr = S_OK; + ExitFunction(); + } + ExitOnFailure(hr, "failed to get IID_IIMSAdminBase object"); + + hr = WcaGetProperty( L"CustomActionData", &pwzData); + ExitOnFailure(hr, "failed to get CustomActionData"); + + hr = piMetabase->DeleteBackup(pwzData, MD_BACKUP_HIGHEST_VERSION); + ExitOnFailure(hr, "failed to cleanup metabase transaction: '%ls'", pwzData); + +LExit: + ReleaseStr(pwzData); + ReleaseObject(piMetabase); + + if (fInitializedCom) + { + ::CoUninitialize(); + } + + if (FAILED(hr)) + { + er = ERROR_INSTALL_FAILURE; + } + return WcaFinalize(er); +} + + +/******************************************************************** + CreateMetabaseKey - Installs metabase keys + + Input: deferred CustomActionData - Key + * *****************************************************************/ +static HRESULT CreateMetabaseKey(__in LPWSTR* ppwzCustomActionData, __in IMSAdminBase* piMetabase) +{ +//AssertSz(FALSE, "debug CreateMetabaseKey here"); + HRESULT hr = S_OK; + METADATA_HANDLE mhRoot = 0; + LPWSTR pwzData = NULL; + LPCWSTR pwzKey; + + int i; + + hr = WcaReadStringFromCaData(ppwzCustomActionData, &pwzData); + ExitOnFailure(hr, "failed to read key from custom action data"); + + hr = piMetabase->OpenKey(METADATA_MASTER_ROOT_HANDLE, L"/LM", METADATA_PERMISSION_WRITE, 10, &mhRoot); + for (i = 30; i > 0 && HRESULT_FROM_WIN32(ERROR_PATH_BUSY) == hr; i--) + { + ::Sleep(1000); + WcaLog(LOGMSG_STANDARD, "failed to open root key, retrying %d time(s)...", i); + hr = piMetabase->OpenKey(METADATA_MASTER_ROOT_HANDLE, L"/LM", METADATA_PERMISSION_WRITE, 10, &mhRoot); + } + MessageExitOnFailure(hr, msierrIISFailedOpenKey, "failed to open metabase key: %ls", L"/LM"); + + pwzKey = pwzData + 3; + + WcaLog(LOGMSG_VERBOSE, "Creating Metabase Key: %ls", pwzKey); + + hr = piMetabase->AddKey(mhRoot, pwzKey); + if (HRESULT_FROM_WIN32(ERROR_ALREADY_EXISTS) == hr) + { + WcaLog(LOGMSG_VERBOSE, "Key `%ls` already existed, continuing.", pwzData); + hr = S_OK; + } + MessageExitOnFailure(hr, msierrIISFailedCreateKey, "failed to create metabase key: %ls", pwzKey); + + hr = WcaProgressMessage(COST_IIS_CREATEKEY, FALSE); + +LExit: + if (mhRoot) + { + piMetabase->CloseKey(mhRoot); + } + + return hr; +} + + +/******************************************************************** + WriteMetabaseValue -Installs metabase values + + Input: deferred CustomActionData - Key\tIdentifier\tAttributes\tUserType\tDataType\tData + * *****************************************************************/ +static HRESULT WriteMetabaseValue(__in LPWSTR* ppwzCustomActionData, __in IMSAdminBase* piMetabase) +{ + //AssertSz(FALSE, "debug WriteMetabaseValue here"); + HRESULT hr = S_OK; + + METADATA_HANDLE mhKey = 0; + + LPWSTR pwzKey = NULL; + LPWSTR pwzTemp = NULL; + DWORD dwData = 0; + DWORD dwTemp = 0; + BOOL fFreeData = FALSE; + METADATA_RECORD mr; + ::ZeroMemory((LPVOID)&mr, sizeof(mr)); + METADATA_RECORD mrGet; + ::ZeroMemory((LPVOID)&mrGet, sizeof(mrGet)); + + int i; + + // get the key first + hr = WcaReadStringFromCaData(ppwzCustomActionData, &pwzKey); + ExitOnFailure(hr, "failed to read key"); + hr = WcaReadIntegerFromCaData(ppwzCustomActionData, reinterpret_cast(&mr.dwMDIdentifier)); + ExitOnFailure(hr, "failed to read identifier"); + hr = WcaReadIntegerFromCaData(ppwzCustomActionData, reinterpret_cast(&mr.dwMDAttributes)); + ExitOnFailure(hr, "failed to read attributes"); + hr = WcaReadIntegerFromCaData(ppwzCustomActionData, reinterpret_cast(&mr.dwMDUserType)); + ExitOnFailure(hr, "failed to read user type"); + hr = WcaReadIntegerFromCaData(ppwzCustomActionData, reinterpret_cast(&mr.dwMDDataType)); + ExitOnFailure(hr, "failed to read data type"); + + switch (mr.dwMDDataType) // data + { + case DWORD_METADATA: + hr = WcaReadIntegerFromCaData(ppwzCustomActionData, reinterpret_cast(&dwData)); + mr.dwMDDataLen = sizeof(dwData); + mr.pbMDData = reinterpret_cast(&dwData); + break; + case STRING_METADATA: + hr = WcaReadStringFromCaData(ppwzCustomActionData, &pwzTemp); + mr.dwMDDataLen = (lstrlenW(pwzTemp) + 1) * sizeof(WCHAR); + mr.pbMDData = reinterpret_cast(pwzTemp); + break; + case MULTISZ_METADATA: + { + hr = WcaReadStringFromCaData(ppwzCustomActionData, &pwzTemp); + mr.dwMDDataLen = (lstrlenW(pwzTemp) + 1) * sizeof(WCHAR); + for (LPWSTR pwzT = pwzTemp; *pwzT; ++pwzT) + { + if (MAGIC_MULTISZ_CHAR == *pwzT) + { + *pwzT = L'\0'; + } + } + mr.pbMDData = reinterpret_cast(pwzTemp); + } + break; + case BINARY_METADATA: + hr = WcaReadStreamFromCaData(ppwzCustomActionData, &mr.pbMDData, reinterpret_cast(&mr.dwMDDataLen)); + fFreeData = TRUE; + break; + default: + hr = E_UNEXPECTED; + break; + } + ExitOnFailure(hr, "failed to parse CustomActionData into metabase record"); + + WcaLog(LOGMSG_VERBOSE, "Writing Metabase Value Under Key: %ls ID: %d", pwzKey, mr.dwMDIdentifier); + + hr = piMetabase->OpenKey(METADATA_MASTER_ROOT_HANDLE, L"/LM", METADATA_PERMISSION_WRITE, 10, &mhKey); + for (i = 30; i > 0 && HRESULT_FROM_WIN32(ERROR_PATH_BUSY) == hr; i--) + { + ::Sleep(1000); + WcaLog(LOGMSG_STANDARD, "failed to open '%ls' key, retrying %d time(s)...", pwzKey, i); + hr = piMetabase->OpenKey(METADATA_MASTER_ROOT_HANDLE, L"/LM", METADATA_PERMISSION_WRITE, 10, &mhKey); + } + MessageExitOnFailure(hr, msierrIISFailedOpenKey, "failed to open metabase key: %ls", pwzKey); + + if (lstrlenW(pwzKey) < 3) + { + ExitOnFailure(hr = E_INVALIDARG, "Key didn't begin with \"/LM\" as expected - key value: %ls", pwzKey); + } + + hr = piMetabase->SetData(mhKey, pwzKey + 3, &mr); // pwzKey + 3 to skip "/LM" that was used to open the key. + + // This means we're trying to write to a secure key without the secure flag set - let's try again with the secure flag set + if (MD_ERROR_CANNOT_REMOVE_SECURE_ATTRIBUTE == hr) + { + mr.dwMDAttributes |= METADATA_SECURE; + hr = piMetabase->SetData(mhKey, pwzKey + 3, &mr); + } + + // If IIS6 returned failure, let's check if the correct value exists in the metabase before actually failing the CA + if (FAILED(hr)) + { + // Backup the original failure error, so we can log it below if necessary + HRESULT hrOldValue = hr; + + mrGet.dwMDIdentifier = mr.dwMDIdentifier; + mrGet.dwMDAttributes = METADATA_NO_ATTRIBUTES; + mrGet.dwMDUserType = mr.dwMDUserType; + mrGet.dwMDDataType = mr.dwMDDataType; + mrGet.dwMDDataLen = mr.dwMDDataLen; + mrGet.pbMDData = static_cast(MemAlloc(mr.dwMDDataLen, TRUE)); + + hr = piMetabase->GetData(mhKey, pwzKey + 3, &mrGet, &dwTemp); + if (SUCCEEDED(hr)) + { + if (mrGet.dwMDDataType == mr.dwMDDataType && mrGet.dwMDDataLen == mr.dwMDDataLen && 0 == memcmp(mrGet.pbMDData, mr.pbMDData, mr.dwMDDataLen)) + { + WcaLog(LOGMSG_VERBOSE, "Received error while writing metabase value under key: %ls ID: %d with error 0x%x, but the correct value is in the metabase - continuing", pwzKey, mr.dwMDIdentifier, hrOldValue); + hr = S_OK; + } + else + { + WcaLog(LOGMSG_VERBOSE, "Succeeded in checking metabase value after write value, but the values didn't match"); + hr = hrOldValue; + } + } + else + { + WcaLog(LOGMSG_VERBOSE, "Failed to check value after metabase write failure (error code 0x%x)", hr); + hr = hrOldValue; + } + } + MessageExitOnFailure(hr, msierrIISFailedWriteData, "failed to write data to metabase key: %ls", pwzKey); + + hr = WcaProgressMessage(COST_IIS_WRITEVALUE, FALSE); + +LExit: + ReleaseStr(pwzTemp); + ReleaseStr(pwzKey); + + if (mhKey) + { + piMetabase->CloseKey(mhKey); + } + + if (fFreeData && mr.pbMDData) + { + MemFree(mr.pbMDData); + } + + return hr; +} + + +/******************************************************************** + DeleteMetabaseValue -Installs metabase values + + Input: deferred CustomActionData - Key\tIdentifier\tAttributes\tUserType\tDataType\tData + * *****************************************************************/ +static HRESULT DeleteMetabaseValue(__in LPWSTR* ppwzCustomActionData, __in IMSAdminBase* piMetabase) +{ + //AssertSz(FALSE, "debug DeleteMetabaseValue here"); + HRESULT hr = S_OK; + + METADATA_HANDLE mhKey = 0; + + LPWSTR pwzKey = NULL; + DWORD dwIdentifier = 0; + DWORD dwDataType = 0; + + int i; + + // get the key first + hr = WcaReadStringFromCaData(ppwzCustomActionData, &pwzKey); + ExitOnFailure(hr, "failed to read key"); + hr = WcaReadIntegerFromCaData(ppwzCustomActionData, reinterpret_cast(&dwIdentifier)); + ExitOnFailure(hr, "failed to read identifier"); + hr = WcaReadIntegerFromCaData(ppwzCustomActionData, reinterpret_cast(&dwDataType)); + ExitOnFailure(hr, "failed to read data type"); + + WcaLog(LOGMSG_VERBOSE, "Deleting Metabase Value Under Key: %ls ID: %d", pwzKey, dwIdentifier); + + hr = piMetabase->OpenKey(METADATA_MASTER_ROOT_HANDLE, L"/LM", METADATA_PERMISSION_WRITE, 10, &mhKey); + for (i = 30; i > 0 && HRESULT_FROM_WIN32(ERROR_PATH_BUSY) == hr; i--) + { + ::Sleep(1000); + WcaLog(LOGMSG_STANDARD, "failed to open '%ls' key, retrying %d time(s)...", pwzKey, i); + hr = piMetabase->OpenKey(METADATA_MASTER_ROOT_HANDLE, L"/LM", METADATA_PERMISSION_WRITE, 10, &mhKey); + } + MessageExitOnFailure(hr, msierrIISFailedOpenKey, "failed to open metabase key: %ls", pwzKey); + + if (lstrlenW(pwzKey) < 3) + { + ExitOnFailure(hr = E_INVALIDARG, "Key didn't begin with \"/LM\" as expected - key value: %ls", pwzKey); + } + + hr = piMetabase->DeleteData(mhKey, pwzKey + 3, dwIdentifier, dwDataType); // pwzKey + 3 to skip "/LM" that was used to open the key. + if (MD_ERROR_DATA_NOT_FOUND == hr || HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND) == hr) + { + hr = S_OK; + } + MessageExitOnFailure(hr, msierrIISFailedDeleteValue, "failed to delete value %d from metabase key: %ls", dwIdentifier, pwzKey); + + hr = WcaProgressMessage(COST_IIS_DELETEVALUE, FALSE); +LExit: + ReleaseStr(pwzKey); + + if (mhKey) + piMetabase->CloseKey(mhKey); + + return hr; +} + + +/******************************************************************** + DeleteAspApp - Deletes applications in IIS + + Input: deferred CustomActionData - MetabaseRoot\tRecursive + * *****************************************************************/ +static HRESULT DeleteAspApp(__in LPWSTR* ppwzCustomActionData, __in IMSAdminBase* piMetabase, __in ICatalogCollection* pApplicationCollection, __in IWamAdmin* piWam) +{ + const int BUFFER_BYTES = 512; + const BSTR bstrPropName = SysAllocString(L"Deleteable"); + + HRESULT hr = S_OK; + ICatalogObject* pApplication = NULL; + + LPWSTR pwzRoot = NULL; + DWORD dwActualBufferSize = 0; + long lSize = 0; + long lIndex = 0; + long lChanges = 0; + + VARIANT keyValue; + ::VariantInit(&keyValue); + VARIANT propValue; + propValue.vt = VT_BOOL; + propValue.boolVal = TRUE; + + METADATA_RECORD mr; + // Get current set of web service extensions. + ::ZeroMemory(&mr, sizeof(mr)); + mr.dwMDIdentifier = MD_APP_PACKAGE_ID; + mr.dwMDAttributes = 0; + mr.dwMDUserType = ASP_MD_UT_APP; + mr.dwMDDataType = STRING_METADATA; + mr.pbMDData = new unsigned char[BUFFER_BYTES]; + mr.dwMDDataLen = BUFFER_BYTES; + + hr = WcaReadStringFromCaData(ppwzCustomActionData, &pwzRoot); // MetabaseRoot + ExitOnFailure(hr, "failed to get metabase root"); + + hr = piMetabase->GetData(METADATA_MASTER_ROOT_HANDLE, pwzRoot, &mr, &dwActualBufferSize); + if (HRESULT_FROM_WIN32(MD_ERROR_DATA_NOT_FOUND) == hr || HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND) == hr || HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND) == hr) + { + // This one doesn't have an independent app GUID associated with it - it may have been already partially deleted, or a low isolation app + WcaLog(LOGMSG_VERBOSE, "No independent COM+ application found associated with %ls. It either doesn't exist, or was already removed - continuing", pwzRoot); + ExitFunction1(hr = S_OK); + } + MessageExitOnFailure(hr, msierrIISFailedDeleteApp, "failed to get GUID for application at path: %ls", pwzRoot); + + WcaLog(LOGMSG_VERBOSE, "Deleting ASP App (used query: %ls) with GUID: %ls", pwzRoot, (LPWSTR)(mr.pbMDData)); + + // Delete the application association from IIS's point of view before it's obliterated from the application collection + hr = piWam->AppDelete(pwzRoot, FALSE); + if (FAILED(hr)) + { + // This isn't necessarily an error if we fail here, but let's log a failure if it happens + WcaLog(LOGMSG_VERBOSE, "error 0x%x: failed to call IWamAdmin::AppDelete() while removing web application - continuing"); + hr = S_OK; + } + + if (!pApplicationCollection) + { + WcaLog(LOGMSG_STANDARD, "Could not remove application with GUID %ls because the application collection could not be found", (LPWSTR)(mr.pbMDData)); + ExitFunction1(hr = S_OK); + } + + hr = pApplicationCollection->Populate(); + MessageExitOnFailure(hr, msierrIISFailedDeleteApp, "failed to populate Application collection"); + + hr = pApplicationCollection->get_Count(&lSize); + MessageExitOnFailure(hr, msierrIISFailedDeleteApp, "failed to get size of Application collection"); + WcaLog(LOGMSG_TRACEONLY, "Found %u items in application collection", lSize); + + // No need to check this too early, as we may not even need this to have successfully allocated + ExitOnNull(bstrPropName, hr, E_OUTOFMEMORY, "failed to allocate memory for \"Deleteable\" string"); + + for (lIndex = 0; lIndex < lSize; ++lIndex) + { + hr = pApplicationCollection->get_Item(lIndex, reinterpret_cast(&pApplication)); + MessageExitOnFailure(hr, msierrIISFailedDeleteApp, "failed to get COM+ application while enumerating through COM+ applications"); + + hr = pApplication->get_Key(&keyValue); + MessageExitOnFailure(hr, msierrIISFailedDeleteApp, "failed to get key of COM+ application while enumerating through COM+ applications"); + + WcaLog(LOGMSG_TRACEONLY, "While enumerating through COM+ applications, found an application with GUID: %ls", (LPWSTR)keyValue.bstrVal); + + if (VT_BSTR == keyValue.vt && 0 == lstrcmpW((LPWSTR)keyValue.bstrVal, (LPWSTR)(mr.pbMDData))) + { + hr = pApplication->put_Value(bstrPropName, propValue); + if (FAILED(hr)) + { + // This isn't necessarily a critical error unless we fail to actually delete it in the next step + WcaLog(LOGMSG_VERBOSE, "error 0x%x: failed to ensure COM+ application with guid %ls is deletable - continuing", hr, (LPWSTR)(mr.pbMDData)); + } + + hr = pApplicationCollection->SaveChanges(&lChanges); + if (FAILED(hr)) + { + // This isn't necessarily a critical error unless we fail to actually delete it in the next step + WcaLog(LOGMSG_VERBOSE, "error 0x%x: failed to save changes while ensuring COM+ application with guid %ls is deletable - continuing", hr, (LPWSTR)(mr.pbMDData)); + } + + hr = pApplicationCollection->Remove(lIndex); + if (FAILED(hr)) + { + WcaLog(LOGMSG_STANDARD, "error 0x%x: failed to remove COM+ application with guid %ls. The COM application will not be removed", hr, (LPWSTR)(mr.pbMDData)); + } + else + { + hr = pApplicationCollection->SaveChanges(&lChanges); + if (FAILED(hr)) + { + WcaLog(LOGMSG_STANDARD, "error 0x%x: failed to save changes when removing COM+ application with guid %ls. The COM application will not be removed - continuing", hr, (LPWSTR)(mr.pbMDData)); + } + else + { + WcaLog(LOGMSG_VERBOSE, "Found and removed application with GUID %ls", (LPWSTR)(mr.pbMDData)); + } + } + + // We've found the right key and successfully deleted the app - let's exit the loop now + lIndex = lSize; + } + } + // If we didn't find it, it isn't an error, because the application we want to delete doesn't seem to exist! + + hr = WcaProgressMessage(COST_IIS_DELETEAPP, FALSE); +LExit: + ReleaseBSTR(bstrPropName); + + ReleaseStr(pwzRoot); + // Don't release pApplication, because it points to an object within the collection + + delete [] mr.pbMDData; + + return hr; +} + + +/******************************************************************** + CreateAspApp - Creates applications in IIS + + Input: deferred CustomActionData - MetabaseRoot\tInProc + * ****************************************************************/ +static HRESULT CreateAspApp(__in LPWSTR* ppwzCustomActionData, __in IWamAdmin* piWam) +{ + HRESULT hr = S_OK; + + LPWSTR pwzRoot = NULL; + BOOL fInProc; + + hr = WcaReadStringFromCaData(ppwzCustomActionData, &pwzRoot); // MetabaseRoot + ExitOnFailure(hr, "failed to get metabase root"); + hr = WcaReadIntegerFromCaData(ppwzCustomActionData, reinterpret_cast(&fInProc)); // InProc + ExitOnFailure(hr, "failed to get in proc flag"); + + WcaLog(LOGMSG_VERBOSE, "Creating ASP App: %ls", pwzRoot); + + hr = piWam->AppCreate(pwzRoot, fInProc); + MessageExitOnFailure(hr, msierrIISFailedCreateApp, "failed to create web application: %ls", pwzRoot); + + hr = WcaProgressMessage(COST_IIS_CREATEAPP, FALSE); +LExit: + return hr; +} + + +/******************************************************************** + DeleteMetabaseKey - Deletes metabase keys + + Input: deferred CustomActionData - Key + ******************************************************************/ +static HRESULT DeleteMetabaseKey(__in LPWSTR *ppwzCustomActionData, __in IMSAdminBase* piMetabase) +{ + HRESULT hr = S_OK; + + METADATA_HANDLE mhRoot = 0; + + LPWSTR pwzData = NULL; + + LPCWSTR pwzKey; + int i; + + hr = WcaReadStringFromCaData(ppwzCustomActionData, &pwzData); + ExitOnFailure(hr, "failed to read key to be deleted"); + + hr = piMetabase->OpenKey(METADATA_MASTER_ROOT_HANDLE, L"/LM", METADATA_PERMISSION_WRITE, 10, &mhRoot); + for (i = 30; i > 0 && HRESULT_FROM_WIN32(ERROR_PATH_BUSY) == hr; i--) + { + ::Sleep(1000); + WcaLog(LOGMSG_STANDARD, "failed to open root key, retrying %d time(s)...", i); + hr = piMetabase->OpenKey(METADATA_MASTER_ROOT_HANDLE, L"/LM", METADATA_PERMISSION_WRITE, 10, &mhRoot); + } + MessageExitOnFailure(hr, msierrIISFailedOpenKey, "failed to open metabase key: %ls", L"/LM"); + + pwzKey = pwzData + 3; + + WcaLog(LOGMSG_VERBOSE, "Deleting Metabase Key: %ls", pwzKey); + + hr = piMetabase->DeleteKey(mhRoot, pwzKey); + if (HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND) == hr) + { + WcaLog(LOGMSG_STANDARD, "Key `%ls` did not exist, continuing.", pwzData); + hr = S_OK; + } + MessageExitOnFailure(hr, msierrIISFailedDeleteKey, "failed to delete metabase key: %ls", pwzData); + + hr = WcaProgressMessage(COST_IIS_DELETEKEY, FALSE); +LExit: + if (mhRoot) + { + piMetabase->CloseKey(mhRoot); + } + + return hr; +} + + +/******************************************************************** + WriteMetabaseChanges - CUSTOM ACTION ENTRY POINT for IIS Metabase changes + + *******************************************************************/ +extern "C" UINT __stdcall WriteMetabaseChanges(MSIHANDLE hInstall) +{ +//AssertSz(FALSE, "debug WriteMetabaseChanges here"); + HRESULT hr = S_OK; + UINT er = ERROR_SUCCESS; + IMSAdminBase* piMetabase = NULL; + IWamAdmin* piWam = NULL; + ICOMAdminCatalog* pCatalog = NULL; + ICatalogCollection* pApplicationCollection = NULL; + WCA_CASCRIPT_HANDLE hWriteMetabaseScript = NULL; + BSTR bstrApplications = SysAllocString(L"Applications"); + BOOL fInitializedCom = FALSE; + + LPWSTR pwzData = NULL; + LPWSTR pwz = NULL; + LPWSTR pwzScriptKey = NULL; + METABASE_ACTION maAction = MBA_UNKNOWNACTION; + + hr = WcaInitialize(hInstall, "WriteMetabaseChanges"); + ExitOnFailure(hr, "failed to initialize"); + + hr = ::CoInitialize(NULL); + ExitOnFailure(hr, "failed to initialize COM"); + fInitializedCom = TRUE; + + hr = WcaGetProperty( L"CustomActionData", &pwzData); + ExitOnFailure(hr, "failed to get CustomActionData"); + + WcaLog(LOGMSG_TRACEONLY, "CustomActionData: %ls", pwzData); + + // Get the CaScript key + hr = WcaReadStringFromCaData(&pwzData, &pwzScriptKey); + ExitOnFailure(hr, "Failed to get CaScript key from custom action data"); + + hr = WcaCaScriptOpen(WCA_ACTION_INSTALL, WCA_CASCRIPT_SCHEDULED, FALSE, pwzScriptKey, &hWriteMetabaseScript); + ExitOnFailure(hr, "Failed to open CaScript file"); + + // The rest of our existing custom action data string should be empty - go ahead and overwrite it + ReleaseNullStr(pwzData); + hr = WcaCaScriptReadAsCustomActionData(hWriteMetabaseScript, &pwzData); + ExitOnFailure(hr, "Failed to read script into CustomAction data."); + + pwz = pwzData; + + while (S_OK == (hr = WcaReadIntegerFromCaData(&pwz, (int *)&maAction))) + { + switch (maAction) + { + case MBA_CREATEAPP: + if (NULL == piWam) + { + hr = ::CoCreateInstance(CLSID_WamAdmin, NULL, CLSCTX_ALL, IID_IWamAdmin, reinterpret_cast(&piWam)); + MessageExitOnFailure(hr, msierrIISCannotConnect, "failed to get IID_IWamAdmin object"); + } + + hr = CreateAspApp(&pwz, piWam); + ExitOnFailure(hr, "failed to create ASP App"); + break; + case MBA_DELETEAPP: + if (NULL == piMetabase) + { + hr = ::CoCreateInstance(CLSID_MSAdminBase, NULL, CLSCTX_ALL, IID_IMSAdminBase, reinterpret_cast(&piMetabase)); + MessageExitOnFailure(hr, msierrIISCannotConnect, "failed to get IID_IIMSAdminBase object"); + } + + if (NULL == pCatalog) + { + hr = CoCreateInstance(CLSID_COMAdminCatalog, NULL, CLSCTX_INPROC_SERVER, IID_IUnknown, (void**)&pCatalog); + MessageExitOnFailure(hr, msierrIISCannotConnect, "failed to get IID_ICOMAdmin object"); + + hr = pCatalog->GetCollection(bstrApplications, reinterpret_cast(&pApplicationCollection)); + if (FAILED(hr)) + { + hr = S_OK; + WcaLog(LOGMSG_STANDARD, "error 0x%x: failed to get ApplicationCollection object for list of COM+ applications - COM+ applications will not be able to be uninstalled - continuing", hr); + } + } + + if (NULL == piWam) + { + hr = ::CoCreateInstance(CLSID_WamAdmin, NULL, CLSCTX_ALL, IID_IWamAdmin, reinterpret_cast(&piWam)); + MessageExitOnFailure(hr, msierrIISCannotConnect, "failed to get IID_IWamAdmin object"); + } + + hr = DeleteAspApp(&pwz, piMetabase, pApplicationCollection, piWam); + ExitOnFailure(hr, "failed to delete ASP App"); + break; + case MBA_CREATEKEY: + if (NULL == piMetabase) + { + hr = ::CoCreateInstance(CLSID_MSAdminBase, NULL, CLSCTX_ALL, IID_IMSAdminBase, reinterpret_cast(&piMetabase)); + MessageExitOnFailure(hr, msierrIISCannotConnect, "failed to get IID_IIMSAdminBase object"); + } + + hr = CreateMetabaseKey(&pwz, piMetabase); + ExitOnFailure(hr, "failed to create metabase key"); + break; + case MBA_DELETEKEY: + if (NULL == piMetabase) + { + hr = ::CoCreateInstance(CLSID_MSAdminBase, NULL, CLSCTX_ALL, IID_IMSAdminBase, reinterpret_cast(&piMetabase)); + MessageExitOnFailure(hr, msierrIISCannotConnect, "failed to get IID_IIMSAdminBase object"); + } + + hr = DeleteMetabaseKey(&pwz, piMetabase); + ExitOnFailure(hr, "failed to delete metabase key"); + break; + case MBA_WRITEVALUE: + if (NULL == piMetabase) + { + hr = ::CoCreateInstance(CLSID_MSAdminBase, NULL, CLSCTX_ALL, IID_IMSAdminBase, reinterpret_cast(&piMetabase)); + MessageExitOnFailure(hr, msierrIISCannotConnect, "failed to get IID_IIMSAdminBase object"); + } + + hr = WriteMetabaseValue(&pwz, piMetabase); + ExitOnFailure(hr, "failed to write metabase value"); + break; + case MBA_DELETEVALUE: + if (NULL == piMetabase) + { + hr = ::CoCreateInstance(CLSID_MSAdminBase, NULL, CLSCTX_ALL, IID_IMSAdminBase, reinterpret_cast(&piMetabase)); + MessageExitOnFailure(hr, msierrIISCannotConnect, "failed to get IID_IIMSAdminBase object"); + } + + hr = DeleteMetabaseValue(&pwz, piMetabase); + ExitOnFailure(hr, "failed to delete metabase value"); + break; + default: + ExitOnFailure(hr = E_UNEXPECTED, "Unexpected metabase action specified: %d", maAction); + break; + } + } + if (E_NOMOREITEMS == hr) // If there are no more items, all is well + { + if (NULL != piMetabase) + { + hr = piMetabase->SaveData(); + for (int i = 30; i > 0 && HRESULT_FROM_WIN32(ERROR_PATH_BUSY) == hr; i--) + { + ::Sleep(1000); + WcaLog(LOGMSG_VERBOSE, "Failed to force save of metabase data, retrying %d time(s)...", i); + hr = piMetabase->SaveData(); + } + if (FAILED(hr)) + { + WcaLog(LOGMSG_VERBOSE, "Failed to force save of metabase data: 0x%x - continuing", hr); + } + hr = S_OK; + } + else + { + hr = S_OK; + } + } + +LExit: + WcaCaScriptClose(hWriteMetabaseScript, WCA_CASCRIPT_CLOSE_DELETE); + + ReleaseBSTR(bstrApplications); + ReleaseStr(pwzScriptKey); + ReleaseStr(pwzData); + ReleaseObject(piMetabase); + ReleaseObject(piWam); + ReleaseObject(pCatalog); + ReleaseObject(pApplicationCollection); + + if (fInitializedCom) + { + ::CoUninitialize(); + } + + if (FAILED(hr)) + { + er = ERROR_INSTALL_FAILURE; + } + return WcaFinalize(er); +} +/******************************************************************** + WriteIIS7ConfigChanges - CUSTOM ACTION ENTRY POINT for IIS7 config changes + + *******************************************************************/ +extern "C" UINT __stdcall WriteIIS7ConfigChanges(MSIHANDLE hInstall) +{ + //AssertSz(FALSE, "debug WriteIIS7ConfigChanges here"); + HRESULT hr = S_OK; + UINT er = ERROR_SUCCESS; + LPWSTR pwzData = NULL; + LPWSTR pwzScriptKey = NULL; + LPWSTR pwzHashString = NULL; + BYTE rgbActualHash[SHA1_HASH_LEN] = { }; + DWORD dwHashedBytes = SHA1_HASH_LEN; + + WCA_CASCRIPT_HANDLE hWriteIis7Script = NULL; + + hr = WcaInitialize(hInstall, "WriteIIS7ConfigChanges"); + ExitOnFailure(hr, "Failed to initialize"); + + hr = WcaGetProperty( L"CustomActionData", &pwzScriptKey); + ExitOnFailure(hr, "Failed to get CustomActionData"); + WcaLog(LOGMSG_TRACEONLY, "Script WriteIIS7ConfigChanges: %ls", pwzScriptKey); + + hr = WcaCaScriptOpen(WCA_ACTION_INSTALL, WCA_CASCRIPT_SCHEDULED, FALSE, pwzScriptKey, &hWriteIis7Script); + ExitOnFailure(hr, "Failed to open CaScript file"); + + hr = WcaCaScriptReadAsCustomActionData(hWriteIis7Script, &pwzData); + ExitOnFailure(hr, "Failed to read script into CustomAction data."); + + hr = CrypHashBuffer((BYTE*)pwzData, sizeof(pwzData) * sizeof(WCHAR), PROV_RSA_AES, CALG_SHA1, rgbActualHash, dwHashedBytes); + ExitOnFailure(hr, "Failed to calculate hash of CustomAction data."); + + hr = StrAlloc(&pwzHashString, ((dwHashedBytes * 2) + 1)); + ExitOnFailure(hr, "Failed to allocate string for script hash"); + + hr = StrHexEncode(rgbActualHash, dwHashedBytes, pwzHashString, ((dwHashedBytes * 2) + 1)); + ExitOnFailure(hr, "Failed to convert hash bytes to string."); + + WcaLog(LOGMSG_TRACEONLY, "CustomActionData WriteIIS7ConfigChanges: %ls", pwzData); + WcaLog(LOGMSG_VERBOSE, "Custom action data hash: %ls", pwzHashString); + WcaLog(LOGMSG_VERBOSE, "CustomActionData WriteIIS7ConfigChanges length: %d", wcslen(pwzData)); + + hr = IIS7ConfigChanges(hInstall, pwzData); + ExitOnFailure(hr, "WriteIIS7ConfigChanges Failed."); + +LExit: + WcaCaScriptClose(hWriteIis7Script, WCA_CASCRIPT_CLOSE_DELETE); + ReleaseStr(pwzScriptKey); + ReleaseStr(pwzData); + ReleaseStr(pwzHashString); + + if (FAILED(hr)) + { + er = ERROR_INSTALL_FAILURE; + } + + return WcaFinalize(er); +} + + +/******************************************************************** + CommitIIS7ConfigTransaction - CUSTOM ACTION ENTRY POINT for unbacking up config + + Input: deferred CustomActionData - BackupName + * *****************************************************************/ +extern "C" UINT __stdcall CommitIIS7ConfigTransaction(MSIHANDLE hInstall) +{ + HRESULT hr = S_OK; + UINT er = ERROR_SUCCESS; + LPWSTR pwzData = NULL; + WCHAR wzConfigCopy[MAX_PATH]; + DWORD dwSize = 0; + + BOOL fIsWow64Process = FALSE; + BOOL fIsFSRedirectDisabled = FALSE; + + hr = WcaInitialize(hInstall, "CommitIIS7ConfigTransaction"); + ExitOnFailure(hr, "failed to initialize IIS7 commit transaction"); + + WcaInitializeWow64(); + fIsWow64Process = WcaIsWow64Process(); + if (fIsWow64Process) + { + hr = WcaDisableWow64FSRedirection(); + if(FAILED(hr)) + { + //eat this error + hr = S_OK; + } + else + { + fIsFSRedirectDisabled = TRUE; + } + } + + hr = WcaGetProperty( L"CustomActionData", &pwzData); + ExitOnFailure(hr, "failed to get CustomActionData"); + + // Config AdminMgr changes already committed, just + // delete backup config file. + + dwSize = ExpandEnvironmentStringsW(L"%windir%\\system32\\inetsrv\\config\\applicationHost.config", + wzConfigCopy, + MAX_PATH + ); + if ( dwSize == 0 ) + { + ExitWithLastError(hr, "failed to get ExpandEnvironmentStrings"); + } + + hr = ::StringCchCatW(wzConfigCopy, MAX_PATH, L"."); + ExitOnFailure(hr, "Commit IIS7 failed StringCchCatW of ."); + + hr = ::StringCchCatW(wzConfigCopy, MAX_PATH, pwzData); + ExitOnFailure(hr, "Commit IIS7 failed StringCchCatW of extension"); + + if (!::DeleteFileW(wzConfigCopy)) + { + hr = HRESULT_FROM_WIN32(GetLastError()); + if (HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND) == hr || + HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND) == hr) + { + WcaLog(LOGMSG_STANDARD, "Failed to delete backup applicationHost, not found - continuing"); + hr = S_OK; + } + else + { + ExitOnFailure(hr, "failed to delete config backup"); + } + } + +LExit: + ReleaseStr(pwzData); + + // Make sure we revert FS Redirection if necessary before exiting + if (fIsFSRedirectDisabled) + { + fIsFSRedirectDisabled = FALSE; + WcaRevertWow64FSRedirection(); + } + WcaFinalizeWow64(); + + + if (FAILED(hr)) + { + er = ERROR_INSTALL_FAILURE; + } + return WcaFinalize(er); +} +/******************************************************************** + StartIIS7Config Transaction - CUSTOM ACTION ENTRY POINT for backing up config + + Input: deferred CustomActionData - BackupName +********************************************************************/ +extern "C" UINT __stdcall StartIIS7ConfigTransaction(MSIHANDLE hInstall) +{ + HRESULT hr = S_OK; + UINT er = ERROR_SUCCESS; + LPWSTR pwzData = NULL; + WCHAR wzConfigSource[MAX_PATH]; + WCHAR wzConfigCopy[MAX_PATH]; + DWORD dwSize = 0; + + + BOOL fIsWow64Process = FALSE; + BOOL fIsFSRedirectDisabled = FALSE; + + // initialize + hr = WcaInitialize(hInstall, "StartIIS7ConfigTransaction"); + ExitOnFailure(hr, "failed to initialize StartIIS7ConfigTransaction"); + + WcaInitializeWow64(); + fIsWow64Process = WcaIsWow64Process(); + if (fIsWow64Process) + { + hr = WcaDisableWow64FSRedirection(); + if(FAILED(hr)) + { + //eat this error + hr = S_OK; + } + else + { + fIsFSRedirectDisabled = TRUE; + } + } + + hr = WcaGetProperty(L"CustomActionData", &pwzData); + ExitOnFailure(hr, "failed to get CustomActionData"); + + + dwSize = ExpandEnvironmentStringsW(L"%windir%\\system32\\inetsrv\\config\\applicationHost.config", + wzConfigSource, + MAX_PATH + ); + if ( dwSize == 0 ) + { + ExitWithLastError(hr, "failed to get ExpandEnvironmentStrings"); + } + hr = ::StringCchCopyW(wzConfigCopy, MAX_PATH, wzConfigSource); + ExitOnFailure(hr, "Commit IIS7 failed StringCchCopyW"); + + //add ca action as extension + + hr = ::StringCchCatW(wzConfigCopy, MAX_PATH, L"."); + ExitOnFailure(hr, "Commit IIS7 failed StringCchCatW of ."); + + hr = ::StringCchCatW(wzConfigCopy, MAX_PATH, pwzData); + ExitOnFailure(hr, "Commit IIS7 failed StringCchCatW of extension"); + + if ( !::CopyFileW(wzConfigSource, wzConfigCopy, FALSE) ) + { + hr = HRESULT_FROM_WIN32(GetLastError()); + if (HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND) == hr || + HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND) == hr) + { + // IIS may not be installed on the machine, we'll fail later if we try to install anything + WcaLog(LOGMSG_STANDARD, "Failed to back up applicationHost, not found - continuing"); + hr = S_OK; + } + else + { + ExitOnFailure(hr, "Failed to copy config backup %ls -> %ls", wzConfigSource, wzConfigCopy); + } + } + + + hr = WcaProgressMessage(COST_IIS_TRANSACTIONS, FALSE); + + +LExit: + + ReleaseStr(pwzData); + + // Make sure we revert FS Redirection if necessary before exiting + if (fIsFSRedirectDisabled) + { + fIsFSRedirectDisabled = FALSE; + WcaRevertWow64FSRedirection(); + } + WcaFinalizeWow64(); + + if (FAILED(hr)) + er = ERROR_INSTALL_FAILURE; + return WcaFinalize(er); +} + + +/******************************************************************** + RollbackIIS7ConfigTransaction - CUSTOM ACTION ENTRY POINT for unbacking up config + + Input: deferred CustomActionData - BackupName +********************************************************************/ +extern "C" UINT __stdcall RollbackIIS7ConfigTransaction(MSIHANDLE hInstall) +{ + HRESULT hr = S_OK; + UINT er = ERROR_SUCCESS; + LPWSTR pwzData = NULL; + WCHAR wzConfigSource[MAX_PATH]; + WCHAR wzConfigCopy[MAX_PATH]; + DWORD dwSize = 0; + + BOOL fIsWow64Process = FALSE; + BOOL fIsFSRedirectDisabled = FALSE; + + hr = WcaInitialize(hInstall, "RollbackIIS7ConfigTransaction"); + ExitOnFailure(hr, "failed to initialize"); + + WcaInitializeWow64(); + fIsWow64Process = WcaIsWow64Process(); + if (fIsWow64Process) + { + hr = WcaDisableWow64FSRedirection(); + if(FAILED(hr)) + { + //eat this error + hr = S_OK; + } + else + { + fIsFSRedirectDisabled = TRUE; + } + } + + hr = WcaGetProperty( L"CustomActionData", &pwzData); + ExitOnFailure(hr, "failed to get CustomActionData"); + + dwSize = ExpandEnvironmentStringsW(L"%windir%\\system32\\inetsrv\\config\\applicationHost.config", + wzConfigSource, + MAX_PATH + ); + if ( dwSize == 0 ) + { + ExitWithLastError(hr, "failed to get ExpandEnvironmentStrings"); + } + hr = ::StringCchCopyW(wzConfigCopy, MAX_PATH, wzConfigSource); + ExitOnFailure(hr, "Commit IIS7 failed StringCchCopyW"); + + //add ca action as extension + + hr = ::StringCchCatW(wzConfigCopy, MAX_PATH, L"."); + ExitOnFailure(hr, "Commit IIS7 failed StringCchCatW of ."); + + hr = ::StringCchCatW(wzConfigCopy, MAX_PATH, pwzData); + ExitOnFailure(hr, "Commit IIS7 failed StringCchCatW of extension"); + + //copy is reverse of start transaction + if (!::CopyFileW(wzConfigCopy, wzConfigSource, FALSE)) + { + hr = HRESULT_FROM_WIN32(GetLastError()); + if (HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND) == hr || + HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND) == hr) + { + WcaLog(LOGMSG_STANDARD, "Failed to restore applicationHost, not found - continuing"); + hr = S_OK; + } + else + { + ExitOnFailure(hr, "failed to restore config backup"); + } + } + + if (!::DeleteFileW(wzConfigCopy)) + { + ExitWithLastError(hr, "failed to delete config backup"); + } + + hr = WcaProgressMessage(COST_IIS_TRANSACTIONS, FALSE); + +LExit: + ReleaseStr(pwzData); + + // Make sure we revert FS Redirection if necessary before exiting + if (fIsFSRedirectDisabled) + { + fIsFSRedirectDisabled = FALSE; + WcaRevertWow64FSRedirection(); + } + WcaFinalizeWow64(); + + if (FAILED(hr)) + { + er = ERROR_INSTALL_FAILURE; + } + return WcaFinalize(er); +} diff --git a/src/ext/Iis/ca/scaexecIIS7.cpp b/src/ext/Iis/ca/scaexecIIS7.cpp new file mode 100644 index 00000000..108007a1 --- /dev/null +++ b/src/ext/Iis/ca/scaexecIIS7.cpp @@ -0,0 +1,4205 @@ +#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. + + +#include "precomp.h" + +//local CAData action functions +HRESULT IIS7Site( + __inout LPWSTR *ppwzCustomActionData, + __in IAppHostWritableAdminManager *pAdminMgr + ); + +HRESULT IIS7Application( + __inout LPWSTR *ppwzCustomActionData, + __in IAppHostWritableAdminManager *pAdminMgr + ); +HRESULT IIS7VDir( + __inout LPWSTR *ppwzCustomActionData, + __in IAppHostWritableAdminManager *pAdminMgr + ); +HRESULT IIS7Binding( + __inout LPWSTR *ppwzCustomActionData, + __in IAppHostWritableAdminManager *pAdminMgr + ); +HRESULT IIS7AppPool( + __inout LPWSTR *ppwzCustomActionData, + __in IAppHostWritableAdminManager *pAdminMgr + ); +HRESULT IIS7AppExtension( + __inout LPWSTR *ppwzCustomActionData, + __in IAppHostWritableAdminManager *pAdminMgr + ); +HRESULT IIS7MimeMap( + __inout LPWSTR *ppwzCustomActionData, + __in IAppHostWritableAdminManager *pAdminMgr + ); +HRESULT IIS7DirProperties( + __inout LPWSTR *ppwzCustomActionData, + __in IAppHostWritableAdminManager *pAdminMgr + ); +HRESULT IIS7WebLog( + __inout LPWSTR *ppwzCustomActionData, + __in IAppHostWritableAdminManager *pAdminMgr + ); +HRESULT IIS7FilterGlobal( + __inout LPWSTR *ppwzCustomActionData, + __in IAppHostWritableAdminManager *pAdminMgr + ); +HRESULT IIS7FilterSite( + __inout LPWSTR *ppwzCustomActionData, + __in IAppHostWritableAdminManager *pAdminMgr + ); +HRESULT IIS7HttpHeader( + __inout LPWSTR *ppwzCustomActionData, + __in IAppHostWritableAdminManager *pAdminMgr + ); +HRESULT IIS7WebError( + __inout LPWSTR *ppwzCustomActionData, + __in IAppHostWritableAdminManager *pAdminMgr + ); +HRESULT IIS7WebSvcExt( + __inout LPWSTR *ppwzCustomActionData, + __in IAppHostWritableAdminManager *pAdminMgr + ); +HRESULT IIS7WebProperty( + __inout LPWSTR *ppwzCustomActionData, + __in IAppHostWritableAdminManager *pAdminMgr + ); +HRESULT IIS7WebDir( + __inout LPWSTR *ppwzCustomActionData, + __in IAppHostWritableAdminManager *pAdminMgr + ); +HRESULT IIS7AspProperty( + __inout LPWSTR *ppwzCustomActionData, + __in IAppHostWritableAdminManager *pAdminMgr + ); +HRESULT IIS7SslBinding( + __inout LPWSTR *ppwzCustomActionData, + __in IAppHostWritableAdminManager *pAdminMgr + ); +//local helper functions + +static HRESULT GetNextAvailableSiteId( + IAppHostWritableAdminManager *pAdminMgr, + DWORD *plSiteId + ); +static HRESULT GetSiteElement( + IAppHostWritableAdminManager *pAdminMgr, + LPCWSTR swSiteName, + IAppHostElement **pSiteElement, + BOOL* fFound + ); +static HRESULT GetApplicationElement( + IAppHostElement *pSiteElement, + LPCWSTR swAppPath, + IAppHostElement **pAppElement, + BOOL* fFound + ); +static HRESULT GetApplicationElementForVDir( + IAppHostElement *pSiteElement, + LPCWSTR swVDirPath, + IAppHostElement **ppAppElement, + LPCWSTR *ppwzVDirSubPath, + BOOL* fFound + ); + +static HRESULT CreateApplication( + IAppHostElement *pSiteElement, + LPCWSTR swAppPath, + IAppHostElement **pAppElement + ); +static HRESULT DeleteApplication( + IAppHostElement *pSiteElement, + LPCWSTR swAppPath + ); + +static HRESULT SetAppPool( + IAppHostElement *pAppElementpAppElement, + LPCWSTR pwzAppPool + ); +static HRESULT CreateVdir( + IAppHostElement *pAppElement, + LPCWSTR pwzVDirPath, + LPCWSTR pwzVDirPhyDir + ); +static HRESULT DeleteVdir( + IAppHostElement *pAppElement, + LPCWSTR pwzVDirPath + ); + +static HRESULT CreateBinding( + IAppHostElement *pSiteElem, + LPCWSTR pwzProtocol, + LPCWSTR pwzInfo + ); +static HRESULT DeleteBinding( + IAppHostElement *pSiteElem, + LPCWSTR pwzProtocol, + LPCWSTR pwzInfo + ); + +static HRESULT CreateSslBinding( + IAppHostElement *pSiteElem, + LPCWSTR pwzStoreName, + LPCWSTR pwzEncodedCertificateHash + ); +static HRESULT DeleteSslBinding( + IAppHostElement *pSiteElem, + LPCWSTR pwzStoreName, + LPCWSTR pwzEncodedCertificateHash + ); + +static HRESULT CreateSite( + IAppHostElementCollection *pAdminMgr, + LPCWSTR swSiteName, + IAppHostElement **pSiteElement + ); + +static HRESULT DeleteAppPool( + IAppHostWritableAdminManager *pAdminMgr, + LPCWSTR swAppPoolName + ); +static HRESULT CreateAppPool( + __inout LPWSTR *ppwzCustomActionData, + IAppHostWritableAdminManager *pAdminMgr, + LPCWSTR swAppPoolName + ); + +static HRESULT SetDirPropAuthentications( + IAppHostWritableAdminManager *pAdminMgr, + LPCWSTR wcConfigPath, + DWORD dwData + ); +static HRESULT SetDirPropAuthProvider( + IAppHostWritableAdminManager *pAdminMgr, + LPCWSTR wszConfigPath, + __in LPWSTR wszData + ); +static HRESULT SetDirPropDefDoc( + IAppHostWritableAdminManager *pAdminMgr, + LPCWSTR wszConfigPath, + __in LPWSTR wszData + ); + +static HRESULT ClearLocationTag( + IAppHostWritableAdminManager *pAdminMgr, + LPCWSTR swLocationPath + ); + +static HRESULT CreateWebLog( + IAppHostElement *pSiteElem, + LPCWSTR pwzFormat + ); + +static HRESULT CreateGlobalFilter( + __inout LPWSTR *ppwzCustomActionData, + IAppHostElement *pSection + ); +static HRESULT DeleteGlobalFilter( + __inout LPWSTR *ppwzCustomActionData, + IAppHostElement *pSection + ); + +static HRESULT CreateSiteFilter( + __inout LPWSTR *ppwzCustomActionData, + IAppHostWritableAdminManager *pAdminMgr + ); +static HRESULT DeleteSiteFilter( + __inout LPWSTR *ppwzCustomActionData, + IAppHostWritableAdminManager *pAdminMgr + ); + +static HRESULT DeleteCollectionElement( + __in IAppHostElementCollection *pCollection, + __in LPCWSTR pwzElementName, + __in LPCWSTR pwzAttributeName, + __in LPCWSTR pwzAttributeValue + ); + +struct SCA_WEB_ERROR_SERVER +{ + int iErrorCode; + int iSubCode; + WCHAR wzFile[MAX_PATH]; + WCHAR wzLangPath[MAX_PATH]; + int iResponseMode; + SCA_WEB_ERROR_SERVER *psweNext; +}; +static HRESULT AddWebErrorToList( + SCA_WEB_ERROR_SERVER** ppsweList + ); +static void ScaWebErrorFreeList7( + SCA_WEB_ERROR_SERVER *psweList + ); +static HRESULT PopulateHttpErrors( + IAppHostElement *pSection, + SCA_WEB_ERROR_SERVER **psweList + ); +static HRESULT GetErrorFromList( + const SCA_WEB_ERROR_SERVER & we, + SCA_WEB_ERROR_SERVER **ppsweList, + SCA_WEB_ERROR_SERVER **pswe, + BOOL *fFound + ); + +static void ConvSecToHMS( + int Sec, + __out_ecount(cchDest) LPWSTR wcTime, + size_t cchDest + ); +static void ConvSecToDHMS( + unsigned int Sec, + __out_ecount(cchDest) LPWSTR wcTime, + size_t cchDest + ); + +//////////////////////////////////////////////////////////////////// +// ScopeBSTR: Local helper class to construct & free BSTR from LPWSTR +// +///////////////////////////////////////////////////////////////////// +class ScopeBSTR +{ +public: + BSTR m_str; + + ScopeBSTR() + { + m_str = NULL; + } + + ScopeBSTR( __in LPCWSTR pSrc) + { + if (pSrc == NULL) + { + m_str = NULL; + } + else + { + m_str = ::SysAllocString(pSrc); + + } + } + + ~ScopeBSTR() + { + ::SysFreeString(m_str); + } + + operator BSTR() + { + return m_str; + } +}; + + +/******************************************************************** + IIS7ConfigChanges - Start of IIS7 config changes + + *******************************************************************/ +HRESULT IIS7ConfigChanges(MSIHANDLE /*hInstall*/, __inout LPWSTR pwzData) +{ + HRESULT hr = S_OK; + BOOL fInitializedCom = FALSE; + + IAppHostWritableAdminManager *pAdminMgr = NULL; + + LPWSTR pwz = NULL; + LPWSTR pwzLast = NULL; + LPWSTR pwzBackup = NULL; + DWORD cchData = lstrlenW(pwzData); + int iAction = -1; + + int iRetryCount = 0; + + hr = ::CoInitialize(NULL); + ExitOnFailure(hr, "Failed to initialize COM"); + fInitializedCom = TRUE; + + pwz = pwzLast = pwzData; + + hr = StrAllocString(&pwzBackup, pwz, 0); + ExitOnFailure(hr, "Failed to backup custom action data"); + + while (S_OK == (hr = WcaReadIntegerFromCaData(&pwz, &iAction))) + { + if (NULL == pAdminMgr) + { + hr = ::CoCreateInstance( __uuidof(AppHostWritableAdminManager), + NULL, + CLSCTX_INPROC_SERVER, + __uuidof(IAppHostWritableAdminManager), + reinterpret_cast (&pAdminMgr)); + ExitOnFailure(hr , "Failed to open AppHostWritableAdminManager to configure IIS7"); + } + + switch (iAction) + { + case IIS_SITE: + { +#pragma prefast(suppress:26010, "This is a prefast issue - pAdminMgr is correctly allocated") + hr = IIS7Site(&pwz, pAdminMgr); + ExitOnFailure(hr, "Failed to configure IIS site."); + break; + } + case IIS_APPLICATION: + { +#pragma prefast(suppress:26010, "This is a prefast issue - pAdminMgr is correctly allocated") + hr = IIS7Application(&pwz, pAdminMgr); + ExitOnFailure(hr, "Failed to configure IIS application."); + break; + } + case IIS_VDIR: + { +#pragma prefast(suppress:26010, "This is a prefast issue - pAdminMgr is correctly allocated") + hr = IIS7VDir(&pwz, pAdminMgr); + ExitOnFailure(hr, "Failed to configure IIS VDir."); + break; + } + case IIS_BINDING: + { +#pragma prefast(suppress:26010, "This is a prefast issue - pAdminMgr is correctly allocated") + hr = IIS7Binding(&pwz, pAdminMgr); + ExitOnFailure(hr, "Failed to configure IIS site binding."); + break; + } + case IIS_APPPOOL: + { +#pragma prefast(suppress:26010, "This is a prefast issue - pAdminMgr is correctly allocated") + hr = IIS7AppPool(&pwz, pAdminMgr); + ExitOnFailure(hr, "Failed to configure IIS appPool."); + break; + } + case IIS_APPEXT_BEGIN: + { +#pragma prefast(suppress:26010, "This is a prefast issue - pAdminMgr is correctly allocated") + hr = IIS7AppExtension(&pwz, pAdminMgr); + ExitOnFailure(hr, "Failed to configure IIS AppExtension."); + break; + } + case IIS_MIMEMAP_BEGIN: + { +#pragma prefast(suppress:26010, "This is a prefast issue - pAdminMgr is correctly allocated") + hr = IIS7MimeMap(&pwz, pAdminMgr); + ExitOnFailure(hr, "Failed to configure IIS MimeMap."); + break; + } + case IIS_DIRPROP_BEGIN: + { +#pragma prefast(suppress:26010, "This is a prefast issue - pAdminMgr is correctly allocated") + hr = IIS7DirProperties(&pwz, pAdminMgr); + ExitOnFailure(hr, "Failed to configure IIS DirProperties."); + break; + } + case IIS_WEBLOG: + { +#pragma prefast(suppress:26010, "This is a prefast issue - pAdminMgr is correctly allocated") + hr = IIS7WebLog(&pwz, pAdminMgr); + ExitOnFailure(hr, "Failed to configure IIS WebLog."); + break; + } + case IIS_FILTER_GLOBAL_BEGIN: + { +#pragma prefast(suppress:26010, "This is a prefast issue - pAdminMgr is correctly allocated") + hr = IIS7FilterGlobal(&pwz, pAdminMgr); + ExitOnFailure(hr, "Failed to configure IIS filter global."); + break; + } + case IIS_FILTER_BEGIN: + { +#pragma prefast(suppress:26010, "This is a prefast issue - pAdminMgr is correctly allocated") + hr = IIS7FilterSite(&pwz, pAdminMgr); + ExitOnFailure(hr, "Failed to configure IIS Filter."); + break; + } + case IIS_HTTP_HEADER_BEGIN: + { +#pragma prefast(suppress:26010, "This is a prefast issue - pAdminMgr is correctly allocated") + hr = IIS7HttpHeader(&pwz, pAdminMgr); + ExitOnFailure(hr, "Failed to configure IIS http Header."); + break; + } + case IIS_WEBERROR_BEGIN: + { +#pragma prefast(suppress:26010, "This is a prefast issue - pAdminMgr is correctly allocated") + hr = IIS7WebError(&pwz, pAdminMgr); + ExitOnFailure(hr, "Failed to configure IIS http Errors."); + break; + } + case IIS_WEB_SVC_EXT: + { +#pragma prefast(suppress:26010, "This is a prefast issue - pAdminMgr is correctly allocated") + hr = IIS7WebSvcExt(&pwz, pAdminMgr); + ExitOnFailure(hr, "Failed to configure IIS web svc ext."); + break; + } + case IIS_PROPERTY: + { +#pragma prefast(suppress:26010, "This is a prefast issue - pAdminMgr is correctly allocated") + hr = IIS7WebProperty(&pwz, pAdminMgr); + ExitOnFailure(hr, "Failed to configure IIS web property."); + break; + } + case IIS_WEBDIR: + { +#pragma prefast(suppress:26010, "This is a prefast issue - pAdminMgr is correctly allocated") + hr = IIS7WebDir(&pwz, pAdminMgr); + ExitOnFailure(hr, "Failed to configure IIS web directory."); + break; + } + case IIS_ASP_BEGIN: + { +#pragma prefast(suppress:26010, "This is a prefast issue - pAdminMgr is correctly allocated") + hr = IIS7AspProperty(&pwz, pAdminMgr); + ExitOnFailure(hr, "Failed to configure IIS Asp property."); + break; + } + case IIS_SSL_BINDING: +#pragma prefast(suppress:26010, "This is a prefast issue - pAdminMgr is correctly allocated") + hr = IIS7SslBinding(&pwz, pAdminMgr); + ExitOnFailure(hr, "Failed to configure IIS SSL binding."); + break; + + default: + ExitOnFailure(hr = E_UNEXPECTED, "IIS7ConfigChanges: Unexpected IIS Config action specified: %d", iAction); + break; + } + if (S_OK == hr) + { + // commit config changes now to close out IIS Admin changes, + // the Rollback or Commit defered CAs will determine final commit status. + hr = pAdminMgr->CommitChanges(); + + // Our transaction may have been interrupted. + if (hr == HRESULT_FROM_WIN32(ERROR_SHARING_VIOLATION) || hr == HRESULT_FROM_WIN32(ERROR_TRANSACTIONAL_CONFLICT)) + { + WcaLog(LOGMSG_VERBOSE, "Sharing violation or transactional conflict during attempt to save changes to applicationHost.config"); + if (++iRetryCount > 30) + { + if (IDRETRY == WcaErrorMessage(msierrIISFailedCommitInUse, hr, INSTALLMESSAGE_ERROR | MB_RETRYCANCEL, 0)) + { + iRetryCount = 0; + } + else + { + ExitOnFailure(hr, "Failed to Commit IIS Config Changes, in silent mode or user has chosen to cancel"); + } + } + + // Throw away the changes since IIS has no way to remove uncommited changes from an AdminManager. + ReleaseNullObject(pAdminMgr); + + // Restore our CA data backup + pwz = pwzLast; + hr = ::StringCchCopyW(pwz, cchData - (pwz - pwzData) + 1, pwzBackup); + ExitOnFailure(hr , "Failed to restore custom action data backup"); + + } + else if (FAILED(hr)) + { + ExitOnFailure(hr , "Failed to Commit IIS Config Changes"); + } + else + { + // store a backup of CA data @ the last place that we successfully commited changes unless we are done. + if(NULL != pwz) + { + pwzLast = pwz; + hr = StrAllocString(&pwzBackup, pwz, 0); + ExitOnFailure(hr, "Failed to backup custom action data"); + } + } + } + } + if (E_NOMOREITEMS == hr) // If there are no more items, all is well + { + hr = S_OK; + } +LExit: + ReleaseObject(pAdminMgr); + ReleaseStr(pwzBackup); + + if (fInitializedCom) + { + ::CoUninitialize(); + } + + return hr; +} +//------------------------------------------------------------------------------------------------- +// IIS7AspProperty +// Called by WriteIIS7ConfigChanges +// Processes asp properties for WebApplication +// +//------------------------------------------------------------------------------------------------- + +HRESULT IIS7AspProperty( + __inout LPWSTR *ppwzCustomActionData, + __in IAppHostWritableAdminManager *pAdminMgr + ) +{ + HRESULT hr = S_OK; + + int iAction = -1; + int iData = 0; + + LPWSTR pwzData = NULL; + LPWSTR pwzSiteName = NULL; + LPWSTR pwzPathName = NULL; + LPWSTR pwzLocationPath = NULL; + WCHAR wcTime[60]; + + IAppHostElement *pSection = NULL; + IAppHostElement *pElement = NULL; + + //read web site key + hr = WcaReadStringFromCaData(ppwzCustomActionData, &pwzSiteName); + ExitOnFailure(hr, "Failed read webDir webkey"); + + //read path key + hr = WcaReadStringFromCaData(ppwzCustomActionData, &pwzPathName); + ExitOnFailure(hr, "Failed read webDir path"); + + //Construct Location path + hr = StrAllocFormatted(&pwzLocationPath, L"%s/%s", IIS_CONFIG_APPHOST_ROOT, pwzSiteName); + ExitOnFailure(hr, "failed to format webDir location"); + // + //Do not append trailing '/' for default vDir + // + if (CSTR_EQUAL != ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, pwzPathName, -1, L"/", -1)) + { + hr = StrAllocConcat(&pwzLocationPath, L"/", 0); + ExitOnFailure(hr, "failed to copy location WebDir '/'"); + hr = StrAllocConcat(&pwzLocationPath, pwzPathName, 0); + ExitOnFailure(hr, "failed to copy location WebDir path"); + } + + //get asp section at config path location tag + hr = pAdminMgr->GetAdminSection( ScopeBSTR(IIS_CONFIG_ASP_SECTION), pwzLocationPath, &pSection); + ExitOnFailure(hr, "Failed get httpErrors section"); + + // Get action + hr = WcaReadIntegerFromCaData(ppwzCustomActionData, &iAction); + ExitOnFailure(hr, "Failed to read property action"); + + while (IIS_ASP_END != iAction) + { + switch (iAction) + { + case IIS_ASP_SESSIONSTATE: + { + //system.webServer/asp /session | allowSessionState + hr = WcaReadIntegerFromCaData(ppwzCustomActionData, &iData); + ExitOnFailure(hr, "Failed to read asp session state"); + hr = pSection->GetElementByName(ScopeBSTR(IIS_CONFIG_SESSION), &pElement); + ExitOnFailure(hr, "Failed to get asp session element"); + hr = Iis7PutPropertyBool( pElement, IIS_CONFIG_ALLOWSTATE, iData); + ExitOnFailure(hr, "Failed to put asp session value"); + ReleaseNullObject(pElement); + break; + } + case IIS_ASP_SESSIONTIMEOUT: + { + //system.webServer/asp /session | timeout + hr = WcaReadIntegerFromCaData(ppwzCustomActionData, &iData); + ExitOnFailure(hr, "Failed to read asp session timeout"); + hr = pSection->GetElementByName(ScopeBSTR(IIS_CONFIG_SESSION), &pElement); + ExitOnFailure(hr, "Failed to get asp session timeout"); + *wcTime = '\0'; + ConvSecToHMS(iData * 60, wcTime, countof( wcTime)); + hr = Iis7PutPropertyString( pElement, IIS_CONFIG_TIMEOUT, wcTime); + ExitOnFailure(hr, "Failed to put asp timeout value"); + ReleaseNullObject(pElement); + break; + } + case IIS_ASP_BUFFER: + { + //system.webServer/asp | bufferingOn + hr = WcaReadIntegerFromCaData(ppwzCustomActionData, &iData); + ExitOnFailure(hr, "Failed to read asp bufferingOn"); + hr = Iis7PutPropertyBool( pSection, IIS_CONFIG_BUFFERING, iData); + ExitOnFailure(hr, "Failed to put asp bufferingOn value"); + break; + } + case IIS_ASP_PARENTPATHS: + { + //system.webServer/asp | enableParentPaths + hr = WcaReadIntegerFromCaData(ppwzCustomActionData, &iData); + ExitOnFailure(hr, "Failed to read asp ParentPaths"); + hr = Iis7PutPropertyBool( pSection, IIS_CONFIG_PARENTPATHS, iData); + ExitOnFailure(hr, "Failed to put asp ParentPaths value"); + break; + } + case IIS_ASP_SCRIPTLANG: + { + //system.webServer/asp | scriptLanguage + hr = WcaReadStringFromCaData(ppwzCustomActionData, &pwzData); + ExitOnFailure(hr, "Failed to read asp scriptLanguage"); + hr = Iis7PutPropertyString( pSection, IIS_CONFIG_SCRIPTLANG, pwzData); + ExitOnFailure(hr, "Failed to put asp scriptLanguage value"); + break; + } + case IIS_ASP_SCRIPTTIMEOUT: + { + //system.webServer/asp /limits | scriptTimeout + hr = WcaReadIntegerFromCaData(ppwzCustomActionData, &iData); + ExitOnFailure(hr, "Failed to read asp scriptTimeout"); + hr = pSection->GetElementByName(ScopeBSTR(IIS_CONFIG_LIMITS), &pElement); + ExitOnFailure(hr, "Failed to get asp session element"); + *wcTime = '\0'; + ConvSecToHMS(iData, wcTime, countof( wcTime)); + hr = Iis7PutPropertyString( pElement, IIS_CONFIG_SCRIPTTIMEOUT, wcTime); + ExitOnFailure(hr, "Failed to put asp scriptTimeout value"); + ReleaseNullObject(pElement); + break; + + } + case IIS_ASP_SCRIPTSERVERDEBUG: + { + //system.webServer/asp | appAllowDebugging + hr = WcaReadIntegerFromCaData(ppwzCustomActionData, &iData); + ExitOnFailure(hr, "Failed to read asp appAllowDebugging"); + hr = Iis7PutPropertyBool( pSection, IIS_CONFIG_ALLOWDEBUG, iData); + ExitOnFailure(hr, "Failed to put asp appAllowDebugging value"); + break; + } + case IIS_ASP_SCRIPTCLIENTDEBUG: + { + //system.webServer/asp | appAllowClientDebug + hr = WcaReadIntegerFromCaData(ppwzCustomActionData, &iData); + ExitOnFailure(hr, "Failed to read asp appAllowClientDebug"); + hr = Iis7PutPropertyBool( pSection, IIS_CONFIG_ALLOWCLIENTDEBUG, iData); + ExitOnFailure(hr, "Failed to put asp appAllowClientDebug value"); + break; + } + default: + { + ExitOnFailure(hr = E_UNEXPECTED, "Unexpected IIS Config action specified for asp properties"); + break; + } + } + // Get next action + hr = WcaReadIntegerFromCaData(ppwzCustomActionData, &iAction); + ExitOnFailure(hr, "Failed to read asp prop action"); + } + +LExit: + ReleaseStr(pwzData); + ReleaseStr(pwzSiteName); + ReleaseStr(pwzPathName); + ReleaseStr(pwzLocationPath); + ReleaseObject(pSection); + ReleaseObject(pElement); + + return hr; +} + +//------------------------------------------------------------------------------------------------- +// IIS7WebDir +// Called by WriteIIS7ConfigChanges +// Processes WebDir +// +//------------------------------------------------------------------------------------------------- +HRESULT IIS7WebDir( + __inout LPWSTR *ppwzCustomActionData, + __in IAppHostWritableAdminManager *pAdminMgr + ) +{ + HRESULT hr = S_OK; + + int iAction = -1; + + LPWSTR pwzSiteName = NULL; + LPWSTR pwzPathName = NULL; + LPWSTR pwzLocationPath = NULL; + + // Get action + hr = WcaReadIntegerFromCaData(ppwzCustomActionData, &iAction); + ExitOnFailure(hr, "Failed to read property action"); + + //read web site key + hr = WcaReadStringFromCaData(ppwzCustomActionData, &pwzSiteName); + ExitOnFailure(hr, "Failed read webDir webkey"); + + //read path key + hr = WcaReadStringFromCaData(ppwzCustomActionData, &pwzPathName); + ExitOnFailure(hr, "Failed read webDir path"); + + switch (iAction) + { + case IIS_CREATE: + { + //no action needed for create since WebDir has a + //WebDirProperties element that will create and populate + //location tag + break; + } + case IIS_DELETE: + { + //Construct Location path + hr = StrAllocString(&pwzLocationPath, pwzSiteName, 0); + ExitOnFailure(hr, "failed to copy location WebDir web name"); + // + //Do not append trailing '/' for default vDir + // + if (CSTR_EQUAL != ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, pwzPathName, -1, L"/", -1)) + { + hr = StrAllocConcat(&pwzLocationPath, L"/", 0); + ExitOnFailure(hr, "failed to copy location WebDir '/'"); + hr = StrAllocConcat(&pwzLocationPath, pwzPathName, 0); + ExitOnFailure(hr, "failed to copy location WebDir path"); + } + // and delete location tag for this application + hr = ClearLocationTag(pAdminMgr, pwzLocationPath); + ExitOnFailure(hr, "failed to clear location tag for %ls", pwzLocationPath) + break; + } + default: + { + ExitOnFailure(hr = E_UNEXPECTED, "Unexpected IIS Config action specified for WebDir"); + break; + } + } +LExit: + ReleaseStr(pwzSiteName); + ReleaseStr(pwzPathName); + ReleaseStr(pwzLocationPath); + + return hr; +} + +//------------------------------------------------------------------------------------------------- +// IIS7WebProperty +// Called by WriteIIS7ConfigChanges +// Processes isapiCgiRestriction +// +//------------------------------------------------------------------------------------------------- +HRESULT IIS7WebProperty( + __inout LPWSTR *ppwzCustomActionData, + __in IAppHostWritableAdminManager *pAdminMgr + ) +{ + HRESULT hr = S_OK; + + int iAction = -1; + int iData = 0; + + IAppHostElement *pSection = NULL; + + // Get action + hr = WcaReadIntegerFromCaData(ppwzCustomActionData, &iAction); + ExitOnFailure(hr, "Failed to read property action"); + + switch (iAction) + { + case IIS_PROPERTY_MAXBAND: + { + hr = WcaReadIntegerFromCaData(ppwzCustomActionData, &iData); + ExitOnFailure(hr, "Failed to read property max band"); + //set value at system.applicationHost/webLimits | maxGlobalBandwidth + //Get IIS config section + hr = pAdminMgr->GetAdminSection(ScopeBSTR(IIS_CONFIG_WEBLIMITS_SECTION), ScopeBSTR(IIS_CONFIG_APPHOST_ROOT), &pSection); + ExitOnFailure(hr, "Failed get isapiCgiRestriction section"); + if (!pSection) + { + hr = HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND); + ExitOnFailure(hr, "Failed get isapiCgiRestriction section object"); + } + hr = Iis7PutPropertyInteger(pSection, IIS_CONFIG_WEBLIMITS_MAXBAND, iData); + + break; + } + case IIS_PROPERTY_LOGUTF8: + { + hr = WcaReadIntegerFromCaData(ppwzCustomActionData, &iData); + ExitOnFailure(hr, "Failed to read property log"); + //set value at system.applicationHost/log | logInUTF8 + //Get IIS config section + hr = pAdminMgr->GetAdminSection(ScopeBSTR(IIS_CONFIG_LOG_SECTION), ScopeBSTR(IIS_CONFIG_APPHOST_ROOT), &pSection); + ExitOnFailure(hr, "Failed get isapiCgiRestriction section"); + if (!pSection) + { + hr = HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND); + ExitOnFailure(hr, "Failed get isapiCgiRestriction section object"); + } + hr = Iis7PutPropertyBool(pSection, IIS_CONFIG_LOG_UTF8, iData); + + break; + } + default: + { + ExitOnFailure(hr = E_UNEXPECTED, "Unexpected IIS Config action specified for Web Property"); + break; + } + } + +LExit: + ReleaseObject(pSection); + + return hr; +} + +//------------------------------------------------------------------------------------------------- +// IIS7WebSvcExt +// Called by WriteIIS7ConfigChanges +// Processes isapiCgiRestriction +// +//------------------------------------------------------------------------------------------------- +HRESULT IIS7WebSvcExt( + __inout LPWSTR *ppwzCustomActionData, + __in IAppHostWritableAdminManager *pAdminMgr + ) +{ + HRESULT hr = S_OK; + + int iAction = -1; + int iData = 0; + BOOL fFound = FALSE; + LPWSTR pwzData = NULL; + LPWSTR pwzPath = NULL; + + IAppHostElement *pSection = NULL; + IAppHostElement *pElement = NULL; + IAppHostElementCollection *pCollection = NULL; + + // Get action + hr = WcaReadIntegerFromCaData(ppwzCustomActionData, &iAction); + ExitOnFailure(hr, "Failed to read WebSvcExt action"); + + //get path + hr = WcaReadStringFromCaData(ppwzCustomActionData, &pwzPath); + ExitOnFailure(hr, "Failed to read WebSvcExt key"); + + //Get IIS config section + hr = pAdminMgr->GetAdminSection(ScopeBSTR(IIS_CONFIG_RESTRICTION_SECTION), ScopeBSTR(IIS_CONFIG_APPHOST_ROOT), &pSection); + ExitOnFailure(hr, "Failed get isapiCgiRestriction section"); + if (!pSection) + { + hr = HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND); + ExitOnFailure(hr, "Failed get isapiCgiRestriction section object"); + } + //get collection + hr = pSection->get_Collection(&pCollection); + ExitOnFailure(hr, "Failed get isapiCgiRestriction collection"); + + //find element + hr = Iis7FindAppHostElementPath(pCollection, IIS_CONFIG_ADD, IIS_CONFIG_PATH, pwzPath, &pElement, NULL); + ExitOnFailure(hr, "Failed get isapiCgiRestriction element"); + fFound = (NULL != pElement); + + switch (iAction) + { + case IIS_CREATE: + { + if (!fFound) + { + //create a restriction element + hr = pCollection->CreateNewElement(ScopeBSTR(IIS_CONFIG_ADD), &pElement); + ExitOnFailure(hr, "Failed create isapiCgiRestriction element"); + //put path + hr = Iis7PutPropertyString(pElement, IIS_CONFIG_PATH, pwzPath); + ExitOnFailure(hr, "Failed set isapiCgiRestriction path property"); + } + //update common properties + + //update allowed + hr = WcaReadIntegerFromCaData(ppwzCustomActionData, &iData); + ExitOnFailure(hr, "Failed to read WebSvcExt allowed"); + hr = Iis7PutPropertyBool(pElement, IIS_CONFIG_ALLOWED, iData); + ExitOnFailure(hr, "Failed set isapiCgiRestriction allowed property"); + + //update groupId + hr = WcaReadStringFromCaData(ppwzCustomActionData, &pwzData); + ExitOnFailure(hr, "Failed to read WebSvcExt group ID"); + if (*pwzData) + { + hr = Iis7PutPropertyString(pElement, IIS_CONFIG_GROUPID, pwzData); + ExitOnFailure(hr, "Failed set isapiCgiRestriction groupId property"); + } + //update description + hr = WcaReadStringFromCaData(ppwzCustomActionData, &pwzData); + ExitOnFailure(hr, "Failed to read WebSvcExt description"); + if (*pwzData) + { + hr = Iis7PutPropertyString(pElement, IIS_CONFIG_DESC, pwzData); + ExitOnFailure(hr, "Failed set isapiCgiRestriction description property"); + } + // add element if new + if (!fFound) + { + hr = pCollection->AddElement(pElement); + ExitOnFailure(hr, "Failed add isapiCgiRestriction element"); + } + + break; + } + case IIS_DELETE: + { + hr = DeleteCollectionElement(pCollection, IIS_CONFIG_ADD, IIS_CONFIG_PATH, pwzPath); + ExitOnFailure(hr, "Failed delete isapiCgiRestriction element"); + break; + } + default: + { + ExitOnFailure(hr = E_UNEXPECTED, "Unexpected IIS Config action specified for WebSvcExt"); + break; + } + } + +LExit: + ReleaseStr(pwzPath); + ReleaseStr(pwzData); + ReleaseObject(pSection); + ReleaseObject(pElement); + ReleaseObject(pCollection); + + return hr; + +} + +//------------------------------------------------------------------------------------------------- +// IIS7WebError +// Called by WriteIIS7ConfigChanges +// Processes http header CA Data +// +//------------------------------------------------------------------------------------------------- + +HRESULT IIS7WebError( + __inout LPWSTR *ppwzCustomActionData, + __in IAppHostWritableAdminManager *pAdminMgr + ) +{ + HRESULT hr = S_OK; + LPWSTR pwzConfigPath = NULL; + LPWSTR pwzSiteName = NULL; + LPWSTR pwzAppName = NULL; + + IAppHostElement *pElement = NULL; + IAppHostElement *pSection = NULL; + IAppHostElementCollection *pCollection = NULL; + + SCA_WEB_ERROR_SERVER *psweList = NULL; + SCA_WEB_ERROR_SERVER* pswe = NULL; + SCA_WEB_ERROR_SERVER we; + BOOL fFound = FALSE; + + int iAction = -1; + LPWSTR pwzData = NULL; + + //read web site key + hr = WcaReadStringFromCaData(ppwzCustomActionData, &pwzSiteName); + ExitOnFailure(hr, "Failed read web error site name"); + + //read app key + hr = WcaReadStringFromCaData(ppwzCustomActionData, &pwzAppName); + ExitOnFailure(hr, "Failed read web error app name"); + + //Construct config root path + hr = StrAllocFormatted(&pwzConfigPath, L"%s/%s", IIS_CONFIG_APPHOST_ROOT, pwzSiteName); + ExitOnFailure(hr, "failed to format web error config path"); + + if (CSTR_EQUAL != ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, pwzAppName, -1, L"/", -1)) + { + hr = StrAllocConcat(&pwzConfigPath, L"/", 0); + ExitOnFailure(hr, "failed to copy web error config path delim"); + hr = StrAllocConcat(&pwzConfigPath, pwzAppName, 0); + ExitOnFailure(hr, "failed to app name to web error config path"); + } + + //get httpErrors section at config path location tag + hr = pAdminMgr->GetAdminSection(ScopeBSTR(IIS_CONFIG_HTTPERRORS_SECTION), pwzConfigPath, &pSection); + ExitOnFailure(hr, "Failed get httpErrors section"); + + //get existing httpErrors list & clear collection + hr = PopulateHttpErrors(pSection, &psweList); + ExitOnFailure(hr, "Failed to read httpErrors list"); + + //get collection + hr = pSection->get_Collection(&pCollection); + ExitOnFailure(hr, "Failed get httpErrors collection"); + + DWORD cErrors = 0; + hr = pCollection->get_Count(&cErrors); + + // Get web error action + hr = WcaReadIntegerFromCaData(ppwzCustomActionData, &iAction); + ExitOnFailure(hr, "Failed to read filter action"); + while (IIS_WEBERROR_END != iAction) + { + //Process property action + if (IIS_WEBERROR == iAction) + { + hr = WcaReadIntegerFromCaData(ppwzCustomActionData, &(we.iErrorCode)); + ExitOnFailure(hr, "failed to get httpErrors ErrorCode"); + + hr = WcaReadIntegerFromCaData(ppwzCustomActionData, &(we.iSubCode)); + ExitOnFailure(hr, "failed to get httpErrors SubCode"); + //0 is the sub error code wild card for IIS6, change to -1 for IIS7 + if (we.iSubCode == 0) + { + we.iSubCode = -1; + } + hr = WcaReadStringFromCaData(ppwzCustomActionData, &pwzData); + ExitOnFailure(hr, "Failed to get httpErrors File"); + hr = ::StringCchCopyW(we.wzFile, countof(we.wzFile), pwzData); + ExitOnFailure(hr, "Failed to copy httpErrors File"); + + hr = WcaReadIntegerFromCaData(ppwzCustomActionData, &(we.iResponseMode)); + ExitOnFailure(hr, "Failed to get httpErrors File code"); + + fFound = FALSE; + hr = GetErrorFromList( we, &psweList, &pswe, &fFound); + if (!fFound) + { + hr = AddWebErrorToList(&psweList); + ExitOnFailure(hr, "failed to add web error to list"); + pswe = psweList; + } + else + { + //if overwriting existing http errors element then clear lang path + hr = ::StringCchCopyW(pswe->wzLangPath, countof(pswe->wzLangPath), L""); + ExitOnFailure(hr, "Failed to copy httpErrors lang path value"); + } + pswe->iErrorCode = we.iErrorCode; + pswe->iSubCode = we.iSubCode; + hr = ::StringCchCopyW(pswe->wzFile, countof(pswe->wzFile), we.wzFile); + ExitOnFailure(hr, "Failed to copy httpErrors File value"); + pswe->iResponseMode = we.iResponseMode; + + } + else + { + ExitOnFailure(hr = E_UNEXPECTED, "Unexpected IIS Config action specified for http header"); + } + + // Get AppExt action + hr = WcaReadIntegerFromCaData(ppwzCustomActionData, &iAction); + ExitOnFailure(hr, "Failed to read filter action"); + } + + //No inheritance - put a clear in at this loc tag + hr = pCollection->CreateNewElement(ScopeBSTR(IIS_CONFIG_CLEAR), &pElement); + ExitOnFailure(hr, "Failed create httpErrors clear"); + hr = pCollection->AddElement(pElement); + ExitOnFailure(hr, "Failed add httpErrors clear"); + + //now we have merged new, from MSI, http errors with global list + //write this back out at location tag. + // Loop through the HTTP headers + for ( pswe = psweList; pswe; pswe = pswe->psweNext) + { + hr = pCollection->CreateNewElement(ScopeBSTR(IIS_CONFIG_ERROR), &pElement); + ExitOnFailure(hr, "Failed create httpErrors element"); + + // status code + hr = Iis7PutPropertyInteger(pElement, IIS_CONFIG_STATUSCODE, pswe->iErrorCode); + ExitOnFailure(hr, "Failed set httpErrors code value"); + + //sub status + hr = Iis7PutPropertyInteger(pElement, IIS_CONFIG_SUBSTATUS, pswe->iSubCode); + ExitOnFailure(hr, "Failed set httpErrors sub code value"); + + //lang path + hr = Iis7PutPropertyString(pElement, IIS_CONFIG_LANGPATH, pswe->wzLangPath); + ExitOnFailure(hr, "Failed set httpErrors lang path value"); + + //path + hr = Iis7PutPropertyString(pElement, IIS_CONFIG_PATH, pswe->wzFile); + ExitOnFailure(hr, "Failed set httpErrors path value"); + + //response mode + hr = Iis7PutPropertyInteger(pElement, IIS_CONFIG_RESPMODE, pswe->iResponseMode); + ExitOnFailure(hr, "Failed set httpErrors resp mode value"); + + //add the element + hr = pCollection->AddElement(pElement); + ExitOnFailure(hr, "Failed add httpErrors element"); + ReleaseNullObject(pElement); + } + +LExit: + ScaWebErrorFreeList7(psweList); + + ReleaseStr(pwzConfigPath); + ReleaseStr(pwzSiteName); + ReleaseStr(pwzAppName); + ReleaseStr(pwzData); + ReleaseObject(pElement); + ReleaseObject(pSection); + ReleaseObject(pCollection); + + return hr; +} + +static HRESULT PopulateHttpErrors(IAppHostElement *pSection, SCA_WEB_ERROR_SERVER **ppsweList) +{ + HRESULT hr = S_OK; + + IAppHostElement *pElement = NULL; + IAppHostElementCollection *pCollection = NULL; + IAppHostProperty *pProperty = NULL; + + DWORD cErrors = 0; + SCA_WEB_ERROR_SERVER *pswe = NULL; + + VARIANT vPropValue; + VARIANT vtIndex; + + VariantInit(&vPropValue); + VariantInit(&vtIndex); + + hr = pSection->get_Collection(&pCollection); + ExitOnFailure(hr, "Failed get httpErrors collection"); + + hr = pCollection->get_Count(&cErrors); + ExitOnFailure(hr, "Failed get sites collection count"); + + vtIndex.vt = VT_UI4; + for (DWORD i = 0; i < cErrors; ++i) + { + vtIndex.ulVal = i; + hr = pCollection->get_Item(vtIndex , &pElement); + ExitOnFailure(hr, "Failed get httpErrors collection item"); + + hr = AddWebErrorToList(ppsweList); + ExitOnFailure(hr, "Failed add web error list item"); + pswe = *ppsweList; + + //get all properties + // + // statusCode UINT + // subStatusCode INT + // prefixLanguageFilePath type="string" + // path type="string" + // responseMode type="enum" defaultValue="File"> + // + // + // + + // status code + hr = pElement->GetPropertyByName(ScopeBSTR(IIS_CONFIG_STATUSCODE), &pProperty); + ExitOnFailure(hr, "Failed get httpErrors code property"); + hr = pProperty->get_Value(&vPropValue); + ExitOnFailure(hr, "Failed get httpErrors code value"); + pswe->iErrorCode = vPropValue.uintVal; + ReleaseVariant(vPropValue); + + //sub status + hr = pElement->GetPropertyByName(ScopeBSTR(IIS_CONFIG_SUBSTATUS), &pProperty); + ExitOnFailure(hr, "Failed get httpErrors sub code property"); + hr = pProperty->get_Value(&vPropValue); + ExitOnFailure(hr, "Failed get httpErrors sub code value"); + pswe->iSubCode = vPropValue.intVal; + ReleaseVariant(vPropValue); + + //lang path + hr = pElement->GetPropertyByName(ScopeBSTR(IIS_CONFIG_LANGPATH), &pProperty); + ExitOnFailure(hr, "Failed get httpErrors lang path property"); + hr = pProperty->get_Value(&vPropValue); + ExitOnFailure(hr, "Failed get httpErrors lang path value"); + hr = ::StringCchCopyW(pswe->wzLangPath, countof(pswe->wzLangPath), vPropValue.bstrVal); + ExitOnFailure(hr, "Failed to copy httpErrors lang path"); + ReleaseVariant(vPropValue); + + //path + hr = pElement->GetPropertyByName(ScopeBSTR(IIS_CONFIG_PATH), &pProperty); + ExitOnFailure(hr, "Failed get httpErrors path property"); + hr = pProperty->get_Value(&vPropValue); + ExitOnFailure(hr, "Failed get httpErrors path value"); + hr = ::StringCchCopyW(pswe->wzFile, countof(pswe->wzFile), vPropValue.bstrVal); + ExitOnFailure(hr, "Failed to copy httpErrors File"); + ReleaseVariant(vPropValue); + + //response mode + hr = pElement->GetPropertyByName(ScopeBSTR(IIS_CONFIG_RESPMODE), &pProperty); + ExitOnFailure(hr, "Failed get httpErrors resp mode property"); + hr = pProperty->get_Value(&vPropValue); + ExitOnFailure(hr, "Failed get httpErrors resp mode value"); + pswe->iResponseMode = vPropValue.intVal; + ReleaseVariant(vPropValue); + + ReleaseNullObject(pElement); + ReleaseNullObject(pProperty); + } + + //remove the elements from connection so we can add back later + hr = pCollection->Clear(); + ExitOnFailure(hr, "Failed clear httpErrors collection"); + +LExit: + ReleaseVariant(vPropValue); + ReleaseObject(pProperty); + ReleaseObject(pElement); + ReleaseObject(pCollection); + + return hr; +} + +static void ScaWebErrorFreeList7(SCA_WEB_ERROR_SERVER *psweList) +{ + SCA_WEB_ERROR_SERVER *psweDelete = psweList; + while (psweList) + { + psweDelete = psweList; + psweList = psweList->psweNext; + + MemFree(psweDelete); + } +} +static HRESULT AddWebErrorToList(SCA_WEB_ERROR_SERVER **ppsweList) +{ + HRESULT hr = S_OK; + + SCA_WEB_ERROR_SERVER* pswe = static_cast(MemAlloc(sizeof(SCA_WEB_ERROR_SERVER), TRUE)); + + ExitOnNull(pswe, hr, E_OUTOFMEMORY, "failed to allocate memory for new web error list element"); + + pswe->psweNext = *ppsweList; + *ppsweList = pswe; + +LExit: + return hr; +} +static HRESULT GetErrorFromList( const SCA_WEB_ERROR_SERVER& we, + SCA_WEB_ERROR_SERVER **ppsweList, + SCA_WEB_ERROR_SERVER **ppswe, + BOOL *fFound) +{ + HRESULT hr = S_OK; + + *fFound = FALSE; + + SCA_WEB_ERROR_SERVER *pswe; + + for ( pswe = *ppsweList; pswe; pswe = pswe->psweNext) + { + if ((pswe->iErrorCode == we.iErrorCode) && (pswe->iSubCode == we.iSubCode)) + { + *fFound = TRUE; + *ppswe = pswe; + break; + } + } + + return hr; +} + +//------------------------------------------------------------------------------------------------- +// IIS7HttpHeader +// Called by WriteIIS7ConfigChanges +// Processes http header CA Data +// +//------------------------------------------------------------------------------------------------- + +HRESULT IIS7HttpHeader( + __inout LPWSTR *ppwzCustomActionData, + __in IAppHostWritableAdminManager *pAdminMgr + ) +{ + HRESULT hr = S_OK; + LPWSTR pwzConfigPath = NULL; + LPWSTR pwzSiteName = NULL; + LPWSTR pwzAppName = NULL; + + LPWSTR pwzHeaderName = NULL; + LPWSTR pwzHeaderValue = NULL; + + IAppHostElement *pElement = NULL; + IAppHostElement *pSection = NULL; + IAppHostElementCollection *pCollection = NULL; + IAppHostElement *pElementHeaders = NULL; + + int iAction = -1; + BOOL fFound = FALSE; + + //read web site key + hr = WcaReadStringFromCaData(ppwzCustomActionData, &pwzSiteName); + ExitOnFailure(hr, "Failed read header web site name"); + + //read app key + hr = WcaReadStringFromCaData(ppwzCustomActionData, &pwzAppName); + ExitOnFailure(hr, "Failed read header appkey"); + + //Construct config root path + hr = StrAllocFormatted(&pwzConfigPath, L"%s/%s", IIS_CONFIG_APPHOST_ROOT, pwzSiteName); + ExitOnFailure(hr, "failed to format web error config path"); + + if (CSTR_EQUAL != ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, pwzAppName, -1, L"/", -1)) + { + hr = StrAllocConcat(&pwzConfigPath, L"/", 0); + ExitOnFailure(hr, "failed to copy web error config path delim"); + hr = StrAllocConcat(&pwzConfigPath, pwzAppName, 0); + ExitOnFailure(hr, "failed to app name to web error config path"); + } + + //get admin handlers section at config path location tag + hr = pAdminMgr->GetAdminSection(ScopeBSTR(IIS_CONFIG_HTTPPROTO_SECTION), pwzConfigPath, &pSection); + ExitOnFailure(hr, "Failed get http protocol section"); + + hr = pSection->GetElementByName(ScopeBSTR(IIS_CONFIG_HEADERS), &pElementHeaders); + ExitOnFailure(hr, "Failed get http customHeaders section"); + + hr = pElementHeaders->get_Collection(&pCollection); + ExitOnFailure(hr, "Failed get http customHeaders collection"); + + // Get filter action + hr = WcaReadIntegerFromCaData(ppwzCustomActionData, &iAction); + ExitOnFailure(hr, "Failed to read filter action"); + while (IIS_HTTP_HEADER_END != iAction) + { + //Process property action + if (IIS_HTTP_HEADER == iAction) + { + hr = WcaReadStringFromCaData(ppwzCustomActionData, &pwzHeaderName); + ExitOnFailure(hr, "Fail to read httpHeader name"); + + hr = WcaReadStringFromCaData(ppwzCustomActionData, &pwzHeaderValue); + ExitOnFailure(hr, "Fail to read httpHeader value"); + + hr = Iis7FindAppHostElementString(pCollection, IIS_CONFIG_ADD, IIS_CONFIG_NAME, pwzHeaderName, &pElement, NULL); + ExitOnFailure(hr, "Failed get isapiCgiRestriction element"); + fFound = (NULL != pElement); + + if (!fFound) + { + //make a new element + hr = pCollection->CreateNewElement(ScopeBSTR(IIS_CONFIG_ADD), &pElement); + ExitOnFailure(hr, "Failed to create filter config element"); + + hr = Iis7PutPropertyString(pElement, IIS_CONFIG_NAME, pwzHeaderName); + ExitOnFailure(hr, "Failed to set header name"); + } + + hr = Iis7PutPropertyString(pElement, IIS_CONFIG_VALUE, pwzHeaderValue); + ExitOnFailure(hr, "Failed to set header Value"); + + if (!fFound) + { + hr = pCollection->AddElement(pElement); + ExitOnFailure(hr, "Failed add http header"); + } + + } + else + { + ExitOnFailure(hr = E_UNEXPECTED, "Unexpected IIS Config action specified for http header"); + } + + // Get AppExt action + hr = WcaReadIntegerFromCaData(ppwzCustomActionData, &iAction); + ExitOnFailure(hr, "Failed to read filter action"); + } + +LExit: + ReleaseStr(pwzConfigPath); + ReleaseStr(pwzSiteName); + ReleaseStr(pwzAppName); + ReleaseStr(pwzHeaderName); + ReleaseStr(pwzHeaderValue); + ReleaseObject(pElementHeaders); + ReleaseObject(pElement); + ReleaseObject(pSection); + ReleaseObject(pCollection); + + return hr; +} + +//------------------------------------------------------------------------------------------------- +// IIS7FilterGlobal +// Called by WriteIIS7ConfigChanges +// Processes Filter CA Data +// +//------------------------------------------------------------------------------------------------- +HRESULT IIS7FilterGlobal( + __inout LPWSTR *ppwzCustomActionData, + __in IAppHostWritableAdminManager *pAdminMgr + ) +{ + HRESULT hr = S_OK; + int iAction = 0; + + IAppHostElement *pSection = NULL; + + hr = pAdminMgr->GetAdminSection(ScopeBSTR(IIS_CONFIG_ISAPI_SECTION), ScopeBSTR(IIS_CONFIG_APPHOST_ROOT), &pSection); + ExitOnFailure(hr, "Failed get sites section"); + + if (!pSection) + { + hr = HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND); + ExitOnFailure(hr, "Failed get isapiFilters section object"); + } + + // Get filter action + hr = WcaReadIntegerFromCaData(ppwzCustomActionData, &iAction); + ExitOnFailure(hr, "Failed to read filter action"); + + while (IIS_FILTER_END != iAction) + { + //Process property action + switch (iAction) + { + case IIS_FILTER : + { + hr = WcaReadIntegerFromCaData(ppwzCustomActionData, &iAction); + ExitOnFailure(hr, "Failed to read filter action"); + + if (iAction == IIS_CREATE) + { + hr = CreateGlobalFilter(ppwzCustomActionData, pSection); + } + else + { + hr = DeleteGlobalFilter(ppwzCustomActionData, pSection); + } + break; + } + default: + { + ExitOnFailure(hr = E_UNEXPECTED, "Unexpected IIS Config action specified for global filter"); + break; + } + } + // Get AppExt action + hr = WcaReadIntegerFromCaData(ppwzCustomActionData, &iAction); + ExitOnFailure(hr, "Failed to read filter action"); + + } + +LExit: + ReleaseObject(pSection); + + return hr; +} + +static HRESULT CreateGlobalFilter( __inout LPWSTR *ppwzCustomActionData, IAppHostElement *pSection) +{ + HRESULT hr = S_OK; + + LPWSTR pwzFilterName = NULL; + LPWSTR pwzSiteName = NULL; + LPWSTR pwzFilterPath = NULL; + int iLoadOrder = 0; + DWORD cFilters = 0; + + IAppHostElement *pElement = NULL; + IAppHostElementCollection *pCollection = NULL; + + hr = pSection->get_Collection(&pCollection); + ExitOnFailure(hr, "Failed get filter collection"); + + hr = pCollection->get_Count(&cFilters); + ExitOnFailure(hr, "Failed get filter collection count"); + + // Attempt to delete, we will we recreate with desired property values and order + hr = DeleteCollectionElement(pCollection, IIS_CONFIG_FILTER, IIS_CONFIG_NAME, pwzFilterName); + ExitOnFailure(hr, "Failed to delete filter"); + + //make a new element + hr = pCollection->CreateNewElement(ScopeBSTR(IIS_CONFIG_FILTER), &pElement); + ExitOnFailure(hr, "Failed to create filter config element"); + + //filter Name key + hr = WcaReadStringFromCaData(ppwzCustomActionData, &pwzFilterName); + ExitOnFailure(hr, "Failed to read filter name"); + hr = Iis7PutPropertyString(pElement, IIS_CONFIG_NAME, pwzFilterName); + ExitOnFailure(hr, "Failed to set filter name"); + + //web site name + hr = WcaReadStringFromCaData(ppwzCustomActionData, &pwzSiteName); + ExitOnFailure(hr, "Failed to read filter site name"); + + // filter path + hr = WcaReadStringFromCaData(ppwzCustomActionData, &pwzFilterPath); + ExitOnFailure(hr, "Failed to read filter path"); + hr = Iis7PutPropertyString(pElement,IIS_CONFIG_PATH, pwzFilterPath); + ExitOnFailure(hr, "Failed to set filter path"); + + //filter load order + hr = WcaReadIntegerFromCaData(ppwzCustomActionData, &iLoadOrder); + ExitOnFailure(hr, "Failed to read filter load order"); + + // put element in order in list + int iPosition = -1; + int icFilters = cFilters; + switch (iLoadOrder) + { + case 0 : + { + iPosition = -1; + break; + } + case -1 : + { + iPosition = icFilters; + break; + } + case MSI_NULL_INTEGER : + { + iPosition = icFilters; + break; + } + default: + { + if (iLoadOrder > icFilters) + { + iPosition = icFilters; + } + else + { + iPosition = iLoadOrder; + } + break; + } + } + hr = pCollection->AddElement(pElement, iPosition); + ExitOnFailure(hr, "Failed to add filter element"); + +LExit: + ReleaseStr(pwzFilterName); + ReleaseStr(pwzSiteName); + ReleaseStr(pwzFilterPath); + ReleaseObject(pCollection); + ReleaseObject(pElement); + + return hr; +} + +static HRESULT DeleteGlobalFilter( __inout LPWSTR *ppwzCustomActionData, IAppHostElement *pSection) +{ + HRESULT hr = S_OK; + + LPWSTR pwzFilterName = NULL; + LPWSTR pwzSiteName = NULL; + + IAppHostElementCollection *pCollection = NULL; + + hr = pSection->get_Collection(&pCollection); + ExitOnFailure(hr, "Failed get filter collection"); + + //filter Name key + hr = WcaReadStringFromCaData(ppwzCustomActionData, &pwzFilterName); + ExitOnFailure(hr, "Failed to read filter name"); + + //web site name + hr = WcaReadStringFromCaData(ppwzCustomActionData, &pwzSiteName); // TODO: unused? + ExitOnFailure(hr, "Failed to read filter site name"); + + DeleteCollectionElement(pCollection, IIS_CONFIG_FILTER, IIS_CONFIG_NAME, pwzFilterName); + ExitOnFailure(hr, "Failed to delete filter %ls", pwzFilterName); + +LExit: + ReleaseStr(pwzFilterName); + ReleaseStr(pwzSiteName); + ReleaseObject(pCollection); + + return hr; +} + +//------------------------------------------------------------------------------------------------- +// IIS7FilterSite +// Called by WriteIIS7ConfigChanges +// Processes Filter CA Data +// +//------------------------------------------------------------------------------------------------- +HRESULT IIS7FilterSite( + __inout LPWSTR *ppwzCustomActionData, + __in IAppHostWritableAdminManager *pAdminMgr + ) +{ + HRESULT hr = S_OK; + int iAction = 0; + + // Get filter action + hr = WcaReadIntegerFromCaData(ppwzCustomActionData, &iAction); + ExitOnFailure(hr, "Failed to read filter action"); + + while (IIS_FILTER_END != iAction) + { + //Process property action + switch (iAction) + { + case IIS_FILTER : + { + hr = WcaReadIntegerFromCaData(ppwzCustomActionData, &iAction); + ExitOnFailure(hr, "Failed to read filter action"); + + if (iAction == IIS_CREATE) + { + hr = CreateSiteFilter(ppwzCustomActionData, pAdminMgr); + } + else + { + hr = DeleteSiteFilter(ppwzCustomActionData, pAdminMgr); + } + break; + } + default: + { + ExitOnFailure(hr = E_UNEXPECTED, "Unexpected IIS Config action specified for global filter"); + break; + } + } + + // Get AppExt action + hr = WcaReadIntegerFromCaData(ppwzCustomActionData, &iAction); + ExitOnFailure(hr, "Failed to read filter action"); + } + +LExit: + return hr; + +} + +static HRESULT CreateSiteFilter(__inout LPWSTR *ppwzCustomActionData, IAppHostWritableAdminManager *pAdminMgr) +{ + HRESULT hr = S_OK; + LPWSTR pwzFilterName = NULL; + LPWSTR pwzSiteName = NULL; + LPWSTR pwzFilterPath = NULL; + LPWSTR pwzConfigPath = NULL; + int iLoadOrder = 0; + DWORD cFilters; + + IAppHostElement *pElement = NULL; + IAppHostElement *pSection = NULL; + IAppHostElementCollection *pCollection = NULL; + + //filter Name key + hr = WcaReadStringFromCaData(ppwzCustomActionData, &pwzFilterName); + ExitOnFailure(hr, "Failed to read filter name"); + + //web site name + hr = WcaReadStringFromCaData(ppwzCustomActionData, &pwzSiteName); + ExitOnFailure(hr, "Failed to read filter site name"); + + //Construct config root + hr = StrAllocFormatted(&pwzConfigPath, L"%s/%s", IIS_CONFIG_APPHOST_ROOT, pwzSiteName); + ExitOnFailure(hr, "failed to format filter config path"); + + //get admin isapiFilters section at config path location tag + hr = pAdminMgr->GetAdminSection(ScopeBSTR(IIS_CONFIG_ISAPI_SECTION), pwzConfigPath, &pSection); + ExitOnFailure(hr, "Failed get isapiFilters section"); + + hr = pSection->get_Collection(&pCollection); + ExitOnFailure(hr, "Failed get filter collection"); + + hr = pCollection->get_Count(&cFilters); + ExitOnFailure(hr, "Failed get filter collection count"); + + // Attempt to delete, we will we recreate with desired property values and order + hr = DeleteCollectionElement(pCollection, IIS_CONFIG_FILTER, IIS_CONFIG_NAME, pwzFilterName); + ExitOnFailure(hr, "Failed to delete filter"); + + //make a new element + hr = pCollection->CreateNewElement(ScopeBSTR(IIS_CONFIG_FILTER), &pElement); + ExitOnFailure(hr, "Failed to create filter config element"); + + hr = Iis7PutPropertyString(pElement,IIS_CONFIG_NAME, pwzFilterName); + ExitOnFailure(hr, "Failed to set filter name"); + + // filter path + hr = WcaReadStringFromCaData(ppwzCustomActionData, &pwzFilterPath); + ExitOnFailure(hr, "Failed to read filter path"); + + hr = Iis7PutPropertyString(pElement, IIS_CONFIG_PATH, pwzFilterPath); + ExitOnFailure(hr, "Failed to set filter path"); + + //filter load order + hr = WcaReadIntegerFromCaData(ppwzCustomActionData, &iLoadOrder); + ExitOnFailure(hr, "Failed to read filter load order"); + + // put element in order in list + int iPosition = -1; + int icFilters = cFilters; + switch (iLoadOrder) + { + case 0 : + { + iPosition = -1; + break; + } + case -1 : + { + iPosition = icFilters; + break; + } + case MSI_NULL_INTEGER : + { + iPosition = icFilters; + break; + } + default: + { + if (iLoadOrder > icFilters) + { + iPosition = icFilters; + } + else + { + iPosition = iLoadOrder; + } + break; + } + } + + hr = pCollection->AddElement(pElement, iPosition); + ExitOnFailure(hr, "Failed to add filter element"); + +LExit: + ReleaseStr(pwzFilterName); + ReleaseStr(pwzSiteName); + ReleaseStr(pwzFilterPath); + ReleaseStr(pwzConfigPath); + ReleaseObject(pElement); + ReleaseObject(pSection); + ReleaseObject(pCollection); + + return hr; +} + +static HRESULT DeleteSiteFilter(__inout LPWSTR *ppwzCustomActionData, IAppHostWritableAdminManager *pAdminMgr) +{ + HRESULT hr = S_OK; + LPWSTR pwzFilterName = NULL; + LPWSTR pwzSiteName = NULL; + LPWSTR pwzConfigPath = NULL; + + IAppHostElement *pSection = NULL; + IAppHostElementCollection *pCollection = NULL; + + //filter Name key + hr = WcaReadStringFromCaData(ppwzCustomActionData, &pwzFilterName); + ExitOnFailure(hr, "Failed to read filter name"); + + //web site name + hr = WcaReadStringFromCaData(ppwzCustomActionData, &pwzSiteName); + ExitOnFailure(hr, "Failed to read filter site name"); + + //Construct config root + hr = StrAllocFormatted(&pwzConfigPath, L"%s/%s", IIS_CONFIG_APPHOST_ROOT, pwzSiteName); + ExitOnFailure(hr, "failed to format filter config path"); + + //get admin isapiFilters section at config path location tag + hr = pAdminMgr->GetAdminSection(ScopeBSTR(IIS_CONFIG_ISAPI_SECTION), pwzConfigPath, &pSection); + ExitOnFailure(hr, "Failed get isapiFilters section"); + + hr = pSection->get_Collection(&pCollection); + ExitOnFailure(hr, "Failed get filter collection"); + + DeleteCollectionElement(pCollection, IIS_CONFIG_FILTER, IIS_CONFIG_NAME, pwzFilterName); + ExitOnFailure(hr, "Failed to delete filter %ls", pwzFilterName); + +LExit: + ReleaseStr(pwzFilterName); + ReleaseStr(pwzSiteName); + ReleaseStr(pwzConfigPath); + ReleaseObject(pSection); + ReleaseObject(pCollection); + + return hr; +} + +//------------------------------------------------------------------------------------------------- +// IIS7Site +// Called by WriteIIS7ConfigChanges +// Processes WebSite CA Data +// +//------------------------------------------------------------------------------------------------- +HRESULT IIS7Site( + __inout LPWSTR *ppwzCustomActionData, + __in IAppHostWritableAdminManager *pAdminMgr) +{ + HRESULT hr = S_OK; + int iAction = -1; + int iData = 0; + BOOL fFound = FALSE; + + LPWSTR pwzSiteName = NULL; + IAppHostElement *pSites = NULL; + IAppHostElementCollection *pCollection = NULL; + IAppHostElement *pSiteElem = NULL; + IAppHostElement *pElement = NULL; + + // Get site action + hr = WcaReadIntegerFromCaData(ppwzCustomActionData, &iAction); + ExitOnFailure(hr, "Failed to read site action"); + + //get site name + hr = WcaReadStringFromCaData(ppwzCustomActionData, &pwzSiteName); + ExitOnFailure(hr, "Failed to read site key"); + + //Get site if it exists + hr = GetSiteElement(pAdminMgr, pwzSiteName, &pSiteElem, &fFound); + ExitOnFailure(hr, "Failed to read sites from config"); + + hr = pAdminMgr->GetAdminSection(ScopeBSTR(IIS_CONFIG_SITES_SECTION), ScopeBSTR(IIS_CONFIG_APPHOST_ROOT), &pSites); + ExitOnFailure(hr, "Failed get sites section"); + ExitOnNull(pSites, hr, ERROR_FILE_NOT_FOUND, "Failed get sites section object"); + + hr = pSites->get_Collection( &pCollection); + ExitOnFailure(hr, "Failed get site collection"); + switch (iAction) + { + case IIS_DELETE : + { + if (fFound) + { + hr = DeleteCollectionElement(pCollection, IIS_CONFIG_SITE, IIS_CONFIG_NAME, pwzSiteName); + ExitOnFailure(hr, "Failed to delete website"); + } + ExitFunction(); + break; + } + case IIS_CREATE : + { + if (!fFound) + { + //Create the site + hr = CreateSite(pCollection, pwzSiteName, &pSiteElem); + ExitOnFailure(hr, "Failed to create site"); + + } + } + } + // + //Set other Site properties + // + //set site Id + hr = WcaReadIntegerFromCaData(ppwzCustomActionData, &iData); + ExitOnFailure(hr, "Failed to read site Id"); + if (iData != MSI_NULL_INTEGER && -1 != iData) + { + hr = Iis7PutPropertyInteger(pSiteElem, IIS_CONFIG_SITE_ID, iData); + ExitOnFailure(hr, "Failed set site Id data"); + } + //Set Site AutoStart + hr = WcaReadIntegerFromCaData(ppwzCustomActionData, &iData); + ExitOnFailure(hr, "Failed to read site autostart"); + if (MSI_NULL_INTEGER != iData) + { + hr = Iis7PutPropertyBool(pSiteElem, IIS_CONFIG_AUTOSTART, iData); + ExitOnFailure(hr, "Failed set site config data"); + } + + //Set Site Connection timeout + hr = WcaReadIntegerFromCaData(ppwzCustomActionData, &iData); + ExitOnFailure(hr, "Failed to read site connection tomeout data"); + if (MSI_NULL_INTEGER != iData) + { + // get limits element, get connectionTimeout property + hr = pSiteElem->GetElementByName(ScopeBSTR(IIS_CONFIG_LIMITS), &pElement); + ExitOnFailure(hr, "Failed to read limits from config"); + //convert iData in seconds to timeSpan hh:mm:ss + WCHAR wcTime[60]; + *wcTime = '\0'; + ConvSecToHMS( iData, wcTime, countof( wcTime)); + + hr = Iis7PutPropertyString(pElement, IIS_CONFIG_CONNECTTIMEOUT, wcTime); + ExitOnFailure(hr, "IIS: failed set connection timeout config data"); + } + +LExit: + ReleaseStr(pwzSiteName); + ReleaseObject(pSites); + ReleaseObject(pCollection); + ReleaseObject(pSiteElem); + ReleaseObject(pElement); + + return hr; +} +//------------------------------------------------------------------------------------------------- +// IIS7Application +// Processes Application CA Data +// +// +//------------------------------------------------------------------------------------------------- + +HRESULT IIS7Application( + __inout LPWSTR *ppwzCustomActionData, + __in IAppHostWritableAdminManager *pAdminMgr) +{ + HRESULT hr = S_OK; + + int iAction = -1; + BOOL fSiteFound = FALSE; + BOOL fAppFound = FALSE; + + LPWSTR pwzSiteName = NULL; + LPWSTR pwzAppPath = NULL; + LPWSTR pwzAppPool = NULL; + LPWSTR pwzLocationPath = NULL; + IAppHostElement *pSiteElem = NULL; + IAppHostElement *pAppElement = NULL; + // Get Application action + hr = WcaReadIntegerFromCaData( ppwzCustomActionData, &iAction); + ExitOnFailure(hr, "Failed to read application action") + //get site key name + hr = WcaReadStringFromCaData(ppwzCustomActionData, &pwzSiteName); + ExitOnFailure(hr, "Failed to read app site key"); + //get application path + hr = WcaReadStringFromCaData(ppwzCustomActionData, &pwzAppPath); + ExitOnFailure(hr, "Failed to read app path key"); + //get application Pool + hr = WcaReadStringFromCaData(ppwzCustomActionData, &pwzAppPool); + ExitOnFailure(hr, "Failed to read app pool key"); + + //Get site if it exists + hr = GetSiteElement(pAdminMgr, pwzSiteName, &pSiteElem, &fSiteFound); + ExitOnFailure(hr, "Failed to read sites from config"); + + switch (iAction) + { + case IIS_CREATE : + { + if (fSiteFound) + { + //have site get application collection + hr = GetApplicationElement(pSiteElem, + pwzAppPath, + &pAppElement, + &fAppFound); + ExitOnFailure(hr, "Error reading application from config"); + + if (!fAppFound) + { + //Create Application + hr = CreateApplication(pSiteElem, pwzAppPath, &pAppElement); + ExitOnFailure(hr, "Error creating application in config"); + } + //Update application properties: + // + //Set appPool + hr = SetAppPool(pAppElement, pwzAppPool); + ExitOnFailure(hr, "Unable to set appPool for application"); + } + else + { + hr = HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND); + ExitOnFailure(hr, "Site not found for create application"); + } + break; + } + case IIS_DELETE : + { + if (fSiteFound) + { + //have site get application collection + hr = GetApplicationElement( pSiteElem, + pwzAppPath, + &pAppElement, + &fAppFound); + ExitOnFailure(hr, "Error reading application from config") + if (fAppFound) + { + //delete Application + hr = DeleteApplication(pSiteElem, pwzAppPath); + ExitOnFailure(hr, "Error deleating application from config") + //Construct Location path + // TODO: it seems odd that these are just + // jammed together, need to determine if this requires a '\' + hr = StrAllocString(&pwzLocationPath, pwzSiteName, 0); + ExitOnFailure(hr, "failed to copy location config path web name"); + hr = StrAllocConcat(&pwzLocationPath, pwzAppPath, 0); + ExitOnFailure(hr, "failed to copy location config path appPath "); + + // and delete location tag for this application + hr = ClearLocationTag(pAdminMgr, pwzLocationPath); + ExitOnFailure(hr, "failed to clear location tag for %ls", pwzLocationPath); + } + } + break; + } + default: + ExitOnFailure(hr = E_UNEXPECTED, "Unexpected IIS Config action specified for Application"); + break; + } + +LExit: + ReleaseStr(pwzSiteName); + ReleaseStr(pwzAppPath); + ReleaseStr(pwzAppPool); + ReleaseStr(pwzLocationPath); + ReleaseObject(pSiteElem); + ReleaseObject(pAppElement); + + return hr; +} +//------------------------------------------------------------------------------------------------- +// IIS7VDir +// Processes VDir CA Data +// +// +//------------------------------------------------------------------------------------------------- +HRESULT IIS7VDir( + __inout LPWSTR *ppwzCustomActionData, + __in IAppHostWritableAdminManager *pAdminMgr) +{ + HRESULT hr = S_OK; + + int iAction = -1; + BOOL fSiteFound = FALSE; + BOOL fAppFound = FALSE; + + LPWSTR pwzSiteName = NULL; + LPWSTR pwzVDirPath = NULL; + LPWSTR pwzVDirPhyDir = NULL; + LPCWSTR pwzVDirSubPath = NULL; + + IAppHostElement *pSiteElem = NULL; + IAppHostElement *pAppElement = NULL; + IAppHostElementCollection *pElement = NULL; + + // Get Application action + hr = WcaReadIntegerFromCaData(ppwzCustomActionData, &iAction); + ExitOnFailure(hr, "Failed to read VDir action"); + + //get site key name + hr = WcaReadStringFromCaData(ppwzCustomActionData, &pwzSiteName); + ExitOnFailure(hr, "Failed to read site key"); + //get VDir path + hr = WcaReadStringFromCaData(ppwzCustomActionData, &pwzVDirPath); + ExitOnFailure(hr, "Failed to read VDir key"); + //get physical dir path + hr = WcaReadStringFromCaData(ppwzCustomActionData, &pwzVDirPhyDir); + ExitOnFailure(hr, "Failed to read VDirPath key"); + + //Get site if it exists + hr = GetSiteElement(pAdminMgr, pwzSiteName, &pSiteElem, &fSiteFound); + ExitOnFailure(hr, "Failed to read sites from config"); + + if (IIS_CREATE == iAction) + { + if (fSiteFound) + { + //have site get application + hr = GetApplicationElementForVDir( pSiteElem, + pwzVDirPath, + &pAppElement, + &pwzVDirSubPath, + &fAppFound); + ExitOnFailure(hr, "Error reading application element from config"); + + if (!fAppFound) + { + // need application to add vDir + hr = E_FILENOTFOUND; + ExitOnFailure(hr, "Error application not found for create VDir"); + } + // + // create the virDir + // + hr = CreateVdir(pAppElement, pwzVDirSubPath, pwzVDirPhyDir); + ExitOnFailure(hr, "Failed to create vdir for application"); + } + else + { + hr = E_FILENOTFOUND; + ExitOnFailure(hr, "IIS: site not found for create VDir"); + } + } + else if (IIS_DELETE == iAction) + { + if (fSiteFound) + { + //have site get application + hr = GetApplicationElementForVDir( pSiteElem, + pwzVDirPath, + &pAppElement, + &pwzVDirSubPath, + &fAppFound); + ExitOnFailure(hr, "Error reading application from config") + if (fAppFound) + { + //delete vdir + hr = DeleteVdir(pAppElement, pwzVDirSubPath); + ExitOnFailure(hr, "Unable to delete vdir for application"); + } + } + } + + LExit: + ReleaseStr(pwzSiteName); + ReleaseStr(pwzVDirPath); + ReleaseStr(pwzVDirPhyDir); + ReleaseObject(pSiteElem); + ReleaseObject(pAppElement); + ReleaseObject(pElement); + + return hr; +} + +//------------------------------------------------------------------------------------------------- +// IIS7Binding +// Processes Bindings CA Data +// +// +//------------------------------------------------------------------------------------------------- +HRESULT IIS7Binding( + __inout LPWSTR *ppwzCustomActionData, + __in IAppHostWritableAdminManager *pAdminMgr) +{ + HRESULT hr = S_OK; + + int iAction = -1; + BOOL fSiteFound = FALSE; + + LPWSTR pwzSiteName = NULL; + LPWSTR pwzProtocol = NULL; + LPWSTR pwzInfo = NULL; + + IAppHostElement *pSiteElem = NULL; + + // Get Application action + hr = WcaReadIntegerFromCaData( ppwzCustomActionData, &iAction); + ExitOnFailure(hr, "Failed to read binding action"); + + //get site key name + hr = WcaReadStringFromCaData(ppwzCustomActionData, &pwzSiteName); + ExitOnFailure(hr, "Failed to read binding site name key"); + + //get binding protocol + hr = WcaReadStringFromCaData(ppwzCustomActionData, &pwzProtocol); + ExitOnFailure(hr, "Failed to read binding protocol"); + + //get binding info + hr = WcaReadStringFromCaData(ppwzCustomActionData, &pwzInfo); + ExitOnFailure(hr, "Failed to read binding info"); + + //Get site if it exists + hr = GetSiteElement(pAdminMgr, pwzSiteName, &pSiteElem, &fSiteFound); + ExitOnFailure(hr, "Failed to read sites from config"); + + if (IIS_CREATE == iAction) + { + if (fSiteFound) + { + //add binding + hr = CreateBinding(pSiteElem, pwzProtocol, pwzInfo); + ExitOnFailure(hr, "Failed to create site binding"); + } + else + { + hr = HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND); + ExitOnFailure(hr, "Site not found for create binding"); + } + } + else if (IIS_DELETE == iAction) + { + if (fSiteFound) + { + //delete binding + hr = DeleteBinding(pSiteElem, pwzProtocol, pwzInfo); + ExitOnFailure(hr, "Failed to delete binding"); + } + } + + LExit: + ReleaseStr(pwzSiteName); + ReleaseStr(pwzProtocol); + ReleaseStr(pwzInfo); + ReleaseObject(pSiteElem); + + return hr; +} +//------------------------------------------------------------------------------------------------- +// IIS7Binding +// Processes WebLog CA Data +// +// +//------------------------------------------------------------------------------------------------- +HRESULT IIS7WebLog( + __inout LPWSTR *ppwzCustomActionData, + __in IAppHostWritableAdminManager *pAdminMgr) +{ + HRESULT hr = S_OK; + + BOOL fSiteFound = FALSE; + + LPWSTR pwzSiteName = NULL; + LPWSTR pwzLogFormat = NULL; + + IAppHostElement *pSiteElem = NULL; + + //get site key name + hr = WcaReadStringFromCaData(ppwzCustomActionData, &pwzSiteName); + ExitOnFailure(hr, "Failed to read web log site name key"); + + //get log format + hr = WcaReadStringFromCaData(ppwzCustomActionData, &pwzLogFormat); + ExitOnFailure(hr, "Failed to read web log protocol"); + + //Get site if it exists + hr = GetSiteElement(pAdminMgr, pwzSiteName, &pSiteElem, &fSiteFound); + ExitOnFailure(hr, "Failed to read web log sites from config"); + + if (fSiteFound) + { + //add log format + hr = CreateWebLog(pSiteElem, pwzLogFormat); + ExitOnFailure(hr, "Failed to create weblog file format"); + } + else + { + hr = HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND); + ExitOnFailure(hr, "Site not found for create weblog file format"); + } + + LExit: + ReleaseStr(pwzSiteName); + ReleaseStr(pwzLogFormat); + ReleaseObject(pSiteElem); + + return hr; +} +//------------------------------------------------------------------------------------------------- +// IIS7AppPool +// Processes AppPool CA Data +// +// +//------------------------------------------------------------------------------------------------- +HRESULT IIS7AppPool( + __inout LPWSTR *ppwzCustomActionData, + __in IAppHostWritableAdminManager *pAdminMgr + ) +{ + HRESULT hr = S_OK; + + int iAction = -1; + + LPWSTR pwzAppPoolName = NULL; + + // Get AppPool action + hr = WcaReadIntegerFromCaData( ppwzCustomActionData, &iAction); + ExitOnFailure(hr, "Failed to read AppPool action"); + + //get appPool name + hr = WcaReadStringFromCaData(ppwzCustomActionData, &pwzAppPoolName); + ExitOnFailure(hr, "Failed to read AppPool name key"); + + switch (iAction) + { + case IIS_CREATE : + { + hr = CreateAppPool(ppwzCustomActionData, pAdminMgr, pwzAppPoolName); + break; + } + case IIS_DELETE: + { + hr = DeleteAppPool(pAdminMgr, pwzAppPoolName); + break; + } + default: + ExitOnFailure(hr = E_UNEXPECTED, "Unexpected IIS Config action specified for appPool"); + break; + } + +LExit: + ReleaseStr(pwzAppPoolName); + return hr; +} + +//------------------------------------------------------------------------------------------------- +// IIS7AppExtension +// Processes AppExtension (config handlers) CA Data +// +// +//------------------------------------------------------------------------------------------------- +HRESULT IIS7AppExtension( + __inout LPWSTR *ppwzCustomActionData, + __in IAppHostWritableAdminManager *pAdminMgr) +{ + HRESULT hr = S_OK; + + LPWSTR pwzWebName = NULL; + LPWSTR pwzWebRoot = NULL; + LPWSTR pwzData = NULL; + LPWSTR pwzConfigPath = NULL; + LPWSTR pwzHandlerName = NULL; + LPWSTR pwzPath = NULL; + int iAction = -1; + + IAppHostElement *pSection = NULL; + IAppHostElement *pElement = NULL; + IAppHostElementCollection *pCollection = NULL; + + BOOL fFound = FALSE; + DWORD cHandlers = 1000; + + //get web name + hr = WcaReadStringFromCaData(ppwzCustomActionData, &pwzWebName); + ExitOnFailure(hr, "Failed to read appExt Web name key"); + + //get root name + hr = WcaReadStringFromCaData(ppwzCustomActionData, &pwzWebRoot); + ExitOnFailure(hr, "Failed to read appExt Web name key"); + + //Construct config root + hr = StrAllocFormatted(&pwzConfigPath, L"%s/%s", IIS_CONFIG_APPHOST_ROOT, pwzWebName); + ExitOnFailure(hr, "failed to format appext config path"); + // + //Do not append trailing '/' for default vDir + // + if (CSTR_EQUAL != ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, pwzWebRoot, -1, L"/", -1)) + { + hr = StrAllocConcat(&pwzConfigPath, L"/", 0); + ExitOnFailure(hr, "failed to copy appext config path delim"); + hr = StrAllocConcat(&pwzConfigPath, pwzWebRoot, 0); + ExitOnFailure(hr, "failed to copy appext config path root name"); + } + //get admin handlers section at config path location tag + hr = pAdminMgr->GetAdminSection(ScopeBSTR(IIS_CONFIG_HANDLERS_SECTION), pwzConfigPath, &pSection); + ExitOnFailure(hr, "Failed get appext section"); + + if (!pSection) + { + hr = HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND); + ExitOnFailure(hr, "Failed get appext section object"); + } + + // Get AppExt action + hr = WcaReadIntegerFromCaData(ppwzCustomActionData, &iAction); + ExitOnFailure(hr, "Failed to read appExt action"); + + hr = pSection->get_Collection(&pCollection); + ExitOnFailure(hr, "Failed get handlers collection for appext"); + + while (IIS_APPEXT_END != iAction) + { + fFound = FALSE; + + //Process property action + switch (iAction) + { + case IIS_APPEXT : + { + // These IDs aren't really stable but this is stable enough to support repair since the MSI won't change + hr = StrAllocFormatted(&pwzHandlerName, L"MsiCustom-%u", ++cHandlers); + ExitOnFailure(hr, "Failed increment handler name"); + + hr = Iis7FindAppHostElementString(pCollection, IIS_CONFIG_ADD, IIS_CONFIG_NAME, pwzHandlerName, &pElement, NULL); + ExitOnFailure(hr, "Failed to find mimemap extension"); + + fFound = (NULL != pElement); + if (!fFound) + { + //create new handler element + hr = pCollection->CreateNewElement(ScopeBSTR(IIS_CONFIG_ADD), &pElement); + ExitOnFailure(hr, "Failed get create handler element for appext"); + + hr = Iis7PutPropertyString(pElement, IIS_CONFIG_NAME, pwzHandlerName); + ExitOnFailure(hr, "Failed set appext name property"); + } + + //BUGBUG: For compat we are assuming these are all ISAPI MODULES so we are + //setting the modules property to IsapiModule. + //Currently can't deal with handlers of different module types. + hr = Iis7PutPropertyString(pElement, IIS_CONFIG_MODULES, L"IsapiModule"); + ExitOnFailure(hr, "Failed set site appExt path property"); + + //get extension (path) + hr = WcaReadStringFromCaData(ppwzCustomActionData, &pwzData); + ExitOnFailure(hr, "Failed to read appExt extension"); + hr = StrAllocFormatted(&pwzPath, L"*.%s", pwzData); + ExitOnFailure(hr, "Failed decorate appExt path"); + //put property + hr = Iis7PutPropertyString(pElement, IIS_CONFIG_PATH, pwzPath); + ExitOnFailure(hr, "Failed set site appExt path property"); + + //get executable + hr = WcaReadStringFromCaData(ppwzCustomActionData, &pwzData); + ExitOnFailure(hr, "Failed to read appExt executable"); + //put property + hr = Iis7PutPropertyString(pElement, IIS_CONFIG_EXECUTABLE, pwzData); + ExitOnFailure(hr, "Failed set site appExt executable property"); + + //get verbs + hr = WcaReadStringFromCaData(ppwzCustomActionData, &pwzData); + ExitOnFailure(hr, "Failed to read appExt verbs"); + //put property + hr = Iis7PutPropertyString(pElement, IIS_CONFIG_VERBS, pwzData); + ExitOnFailure(hr, "Failed set site appExt verbs property"); + + break; + } + default: + { + ExitOnFailure(hr = E_UNEXPECTED, "Unexpected IIS Config action specified for AppExt"); + break; + } + } + + if (!fFound) + { + // put handler element at beginning of list + hr = pCollection->AddElement(pElement, 0); + ExitOnFailure(hr, "Failed add handler element for appext"); + } + + ReleaseNullObject(pElement); + + // Get AppExt action + hr = WcaReadIntegerFromCaData(ppwzCustomActionData, &iAction); + ExitOnFailure(hr, "Failed to read AppPool Property action"); + } + +LExit: + ReleaseStr(pwzWebName); + ReleaseStr(pwzWebRoot); + ReleaseStr(pwzData); + ReleaseStr(pwzConfigPath); + ReleaseStr(pwzHandlerName); + ReleaseStr(pwzPath); + ReleaseObject(pSection); + ReleaseObject(pElement); + ReleaseObject(pCollection); + + return hr; +} + +//------------------------------------------------------------------------------------------------- +// IIS7MimeMap +// Processes Mime Map (config handlers) CA Data +// +// +//------------------------------------------------------------------------------------------------- + HRESULT IIS7MimeMap( + __inout LPWSTR *ppwzCustomActionData, + __in IAppHostWritableAdminManager *pAdminMgr + ) +{ + HRESULT hr = S_OK; + LPWSTR pwzConfigPath = NULL; + LPWSTR pwzWebName = NULL; + LPWSTR pwzWebRoot = NULL; + LPWSTR pwzData = NULL; + int iAction = -1; + + IAppHostElement *pSection = NULL; + IAppHostElement *pElement = NULL; + IAppHostElementCollection *pCollection = NULL; + + BOOL fFound = FALSE; + + //get web name + hr = WcaReadStringFromCaData(ppwzCustomActionData, &pwzWebName); + ExitOnFailure(hr, "Failed to read mime map Web name key"); + + //get vdir root name + hr = WcaReadStringFromCaData(ppwzCustomActionData, &pwzWebRoot); + ExitOnFailure(hr, "Failed to read vdir root name key"); + + //Construct config root + hr = StrAllocFormatted(&pwzConfigPath, L"%s/%s", IIS_CONFIG_APPHOST_ROOT, pwzWebName); + ExitOnFailure(hr, "failed to format mime map config path web name"); + // + //Do not append trailing '/' for default vDir + // + if (CSTR_EQUAL != ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, pwzWebRoot, -1, L"/", -1)) + { + hr = StrAllocConcat(&pwzConfigPath, L"/", 0); + ExitOnFailure(hr, "failed to copy appext config path delim"); + hr = StrAllocConcat(&pwzConfigPath, pwzWebRoot, 0); + ExitOnFailure(hr, "failed to copy appext config path root name"); + } + + //get admin section at config path location tag + hr = pAdminMgr->GetAdminSection(ScopeBSTR(IIS_CONFIG_STATICCONTENT_SECTION), pwzConfigPath, &pSection); + ExitOnFailure(hr, "Failed get staticContent section for mimemap"); + + if (!pSection) + { + hr = HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND); + ExitOnFailure(hr, "Failed get staticContent section object"); + } + + // Get mimemap action + hr = WcaReadIntegerFromCaData(ppwzCustomActionData, &iAction); + ExitOnFailure(hr, "Failed to read mimemap action"); + + hr = pSection->get_Collection(&pCollection); + ExitOnFailure(hr, "Failed get staticContent collection for mimemap"); + + while (IIS_MIMEMAP_END != iAction) + { + //Process property action + switch (iAction) + { + case IIS_MIMEMAP : + { + //get extension + hr = WcaReadStringFromCaData(ppwzCustomActionData, &pwzData); + ExitOnFailure(hr, "Failed to read mimemap extension"); + + hr = Iis7FindAppHostElementString(pCollection, IIS_CONFIG_MIMEMAP, IIS_CONFIG_FILEEXT, pwzData, &pElement, NULL); + ExitOnFailure(hr, "Failed to find mimemap extension"); + fFound = (NULL != pElement); + + if (!fFound) + { + //create new mimeMap element + hr = pCollection->CreateNewElement(ScopeBSTR(IIS_CONFIG_MIMEMAP), &pElement); + ExitOnFailure(hr, "Failed get create MimeMap element"); + } + + //put property + hr = Iis7PutPropertyString(pElement, IIS_CONFIG_FILEEXT, pwzData); + ExitOnFailure(hr, "Failed set mimemap extension property"); + + //get type + hr = WcaReadStringFromCaData(ppwzCustomActionData, &pwzData); + ExitOnFailure(hr, "Failed to read mimemap type"); + //put property + hr = Iis7PutPropertyString(pElement, IIS_CONFIG_MIMETYPE, pwzData); + ExitOnFailure(hr, "Failed set mimemap type property"); + + break; + } + default: + { + ExitOnFailure(hr = E_UNEXPECTED, "Unexpected IIS Config action specified for mimeMap"); + break; + } + } + + if (!fFound) + { + // put mimeMap element at beginning of list + hr = pCollection->AddElement(pElement, -1); + ExitOnFailure(hr, "Failed add mimemap"); + } + + // Get AppExt action + hr = WcaReadIntegerFromCaData(ppwzCustomActionData, &iAction); + ExitOnFailure(hr, "Failed to read mimemap action"); + + ReleaseNullObject(pElement); + } + +LExit: + ReleaseStr(pwzConfigPath); + ReleaseStr(pwzWebName); + ReleaseStr(pwzWebRoot); + ReleaseStr(pwzData); + ReleaseObject(pSection); + ReleaseObject(pElement); + ReleaseObject(pCollection); + + return hr; +} + +//------------------------------------------------------------------------------------------------- +// IIS7DirProperties +// ProcessesVdir Properties CA Data +// +// +//------------------------------------------------------------------------------------------------- +HRESULT IIS7DirProperties( + __inout LPWSTR *ppwzCustomActionData, + __in IAppHostWritableAdminManager *pAdminMgr + ) +{ + HRESULT hr = S_OK; + WCHAR wcTime[60]; + LPWSTR pwzConfigPath = NULL; + LPWSTR pwzWebName = NULL; + LPWSTR pwzWebRoot = NULL; + LPWSTR pwzData = NULL; + int iAction = -1; + int iData = 0; + DWORD dwData = 0; + + IAppHostElement *pSection = NULL; + IAppHostElement *pElement = NULL; + IAppHostElementCollection *pCollection = NULL; + + //get web name + hr = WcaReadStringFromCaData(ppwzCustomActionData, &pwzWebName); + ExitOnFailure(hr, "Failed to read DirProp Web name key"); + + //get vdir root name + hr = WcaReadStringFromCaData(ppwzCustomActionData, &pwzWebRoot); + ExitOnFailure(hr, "Failed to read DirProp Web name key"); + + //Construct config root + hr = StrAllocFormatted(&pwzConfigPath, L"%s/%s", IIS_CONFIG_APPHOST_ROOT, pwzWebName); + ExitOnFailure(hr, "failed to format mime map config path web name"); + // + //Do not append trailing '/' for default vDir + // + if (CSTR_EQUAL != ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, pwzWebRoot, -1, L"/", -1)) + { + hr = StrAllocConcat(&pwzConfigPath, L"/", 0); + ExitOnFailure(hr, "failed to copy appext config path delim"); + hr = StrAllocConcat(&pwzConfigPath, pwzWebRoot, 0); + ExitOnFailure(hr, "failed to copy appext config path root name"); + } + + // Get DirProps action + hr = WcaReadIntegerFromCaData(ppwzCustomActionData, &iAction); + ExitOnFailure(hr, "Failed to read DirProps action"); + + while (IIS_DIRPROP_END != iAction) + { + //Process property action + switch (iAction) + { + case IIS_DIRPROP_ACCESS : + { + hr = WcaReadIntegerFromCaData(ppwzCustomActionData, &iData); + ExitOnFailure(hr, "Failed to read DirProps access"); + //iData contains bit flags for + //no translation required + //get admin section at config path location tag + hr = pAdminMgr->GetAdminSection(ScopeBSTR(IIS_CONFIG_HANDLERS_SECTION), pwzConfigPath, &pSection); + ExitOnFailure(hr, "Failed get handlers section for DirProp"); + if (!pSection) + { + hr = HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND); + ExitOnFailure(hr, "Failed get handlers section object for DirProps"); + } + dwData = iData; + hr = Iis7PutPropertyInteger( pSection, L"accessPolicy", dwData); + ExitOnFailure(hr, "Failed set accessPolicy for DirProps"); + ReleaseNullObject(pSection); + break; + } + case IIS_DIRPROP_USER : + { + hr = WcaReadStringFromCaData(ppwzCustomActionData, &pwzData); + ExitOnFailure(hr, "Failed to read DirProps user"); + hr = pAdminMgr->GetAdminSection(ScopeBSTR(L"system.webServer/security/authentication/anonymousAuthentication"), pwzConfigPath, &pSection); + ExitOnFailure(hr, "Failed get AnonymousAuthentication section for DirProp"); + hr = Iis7PutPropertyString( pSection, IIS_CONFIG_USERNAME, pwzData); + ExitOnFailure(hr, "Failed set accessPolicy for DirProps"); + ReleaseNullObject(pSection); + break; + } + case IIS_DIRPROP_PWD : + { + hr = WcaReadStringFromCaData(ppwzCustomActionData, &pwzData); + ExitOnFailure(hr, "Failed to read DirProps pwd"); + hr = pAdminMgr->GetAdminSection(ScopeBSTR(L"system.webServer/security/authentication/anonymousAuthentication"), pwzConfigPath, &pSection); + ExitOnFailure(hr, "Failed get AnonymousAuthentication section for DirProp"); + hr = Iis7PutPropertyString( pSection, IIS_CONFIG_PASSWORD, pwzData); + ExitOnFailure(hr, "Failed set accessPolicy for DirProps"); + ReleaseNullObject(pSection); + break; + } + case IIS_DIRPROP_DEFDOCS : + { + hr = WcaReadStringFromCaData(ppwzCustomActionData, &pwzData); + ExitOnFailure(hr, "Failed to read DirProps def doc"); + hr = SetDirPropDefDoc(pAdminMgr, pwzConfigPath, pwzData); + ExitOnFailure(hr, "Failed to set DirProps Default Documents"); + break; + } + case IIS_DIRPROP_AUTH : + { + hr = WcaReadIntegerFromCaData(ppwzCustomActionData, &iData); + ExitOnFailure(hr, "Failed to read DirProps auth"); + //iData contains bit flags for /security/authentication/<...> + // Anonymous = 1 + // Basic = 2 + // Windows = 4 + // Digest =16 + // Passport =64 *not supported + //translation required from bit map to section + // E.G security/authentication/windowsAuthentication [property enabled true|false] + dwData= iData; + hr = SetDirPropAuthentications(pAdminMgr, pwzConfigPath, dwData); + ExitOnFailure(hr, "Failed set Authentication for DirProps"); + break; + } + case IIS_DIRPROP_SSLFLAGS : + { + hr = WcaReadIntegerFromCaData(ppwzCustomActionData, &iData); + ExitOnFailure(hr, "Failed to read DirProps sslFlags"); + //iData contains bit flags for /security/access sslFlags + //no translation required + hr = pAdminMgr->GetAdminSection(ScopeBSTR(L"system.webServer/security/access"), pwzConfigPath, &pSection); + ExitOnFailure(hr, "Failed get security/access section for DirProp"); + dwData = iData; + hr = Iis7PutPropertyInteger( pSection, L"sslFlags", dwData); + ExitOnFailure(hr, "Failed set security/access for DirProps"); + ReleaseNullObject(pSection); + break; + } + case IIS_DIRPROP_AUTHPROVID : + { + hr = WcaReadStringFromCaData(ppwzCustomActionData, &pwzData); + ExitOnFailure(hr, "Failed to read DirProps auth provider"); + hr = SetDirPropAuthProvider(pAdminMgr, pwzConfigPath, pwzData); + ExitOnFailure(hr, "Failed to set DirProps auth provider"); + break; + } + case IIS_DIRPROP_ASPERROR: + { + hr = WcaReadIntegerFromCaData(ppwzCustomActionData, &iData); + ExitOnFailure(hr, "Failed to read DirProps aspDetailedError"); + hr = pAdminMgr->GetAdminSection(ScopeBSTR(IIS_CONFIG_ASP_SECTION), pwzConfigPath, &pSection); + ExitOnFailure(hr, "Failed get asp section for DirProp"); + hr = Iis7PutPropertyBool(pSection, IIS_CONFIG_SCRIPTERROR, iData); + ExitOnFailure(hr, "Failed to set DirProps aspDetailedError"); + ReleaseNullObject(pSection); + break; + } + case IIS_DIRPROP_HTTPEXPIRES: + { + hr = WcaReadStringFromCaData(ppwzCustomActionData, &pwzData); + ExitOnFailure(hr, "Failed to read DirProps httpExpires provider"); + hr = pAdminMgr->GetAdminSection(ScopeBSTR(IIS_CONFIG_STATICCONTENT_SECTION), pwzConfigPath, &pSection); + ExitOnFailure(hr, "Failed get staticContent section for DirProp"); + hr = pSection->GetElementByName(ScopeBSTR(IIS_CONFIG_CLIENTCACHE), &pElement); + ExitOnFailure(hr, "Failed to get clientCache element"); + hr = Iis7PutPropertyString(pElement, IIS_CONFIG_HTTPEXPIRES, pwzData); + ExitOnFailure(hr, "Failed to set clientCache httpExpires value"); + hr = Iis7PutPropertyString(pElement, IIS_CONFIG_CACHECONTROLMODE, IIS_CONFIG_USEEXPIRES); + ExitOnFailure(hr, "Failed to set clientCache cacheControlMode value"); + ReleaseNullObject(pSection); + ReleaseNullObject(pElement); + break; + } + case IIS_DIRPROP_MAXAGE: + { + hr = WcaReadIntegerFromCaData(ppwzCustomActionData, &iData); + ExitOnFailure(hr, "Failed to read DirProps httpExpires provider"); + hr = pAdminMgr->GetAdminSection(ScopeBSTR(IIS_CONFIG_STATICCONTENT_SECTION), pwzConfigPath, &pSection); + ExitOnFailure(hr, "Failed get staticContent section for DirProp"); + hr = pSection->GetElementByName(ScopeBSTR(IIS_CONFIG_CLIENTCACHE), &pElement); + ExitOnFailure(hr, "Failed to get clientCache element"); + *wcTime = '\0'; + ConvSecToDHMS(iData, wcTime, countof(wcTime)); + hr = Iis7PutPropertyString(pElement, IIS_CONFIG_MAXAGE, wcTime); + ExitOnFailure(hr, "Failed to set clientCache maxAge value"); + hr = Iis7PutPropertyString(pElement, IIS_CONFIG_CACHECONTROLMODE, IIS_CONFIG_USEMAXAGE); + ExitOnFailure(hr, "Failed to set clientCache cacheControlMode value"); + ReleaseNullObject(pSection); + ReleaseNullObject(pElement); + break; + } + case IIS_DIRPROP_CACHECUST: + { + hr = WcaReadStringFromCaData(ppwzCustomActionData, &pwzData); + ExitOnFailure(hr, "Failed to read DirProps cacheControlCustom"); + hr = pAdminMgr->GetAdminSection(ScopeBSTR(IIS_CONFIG_STATICCONTENT_SECTION), pwzConfigPath, &pSection); + ExitOnFailure(hr, "Failed get staticContent section for DirProp"); + hr = pSection->GetElementByName(ScopeBSTR(IIS_CONFIG_CLIENTCACHE), &pElement); + ExitOnFailure(hr, "Failed to get clientCache element"); + hr = Iis7PutPropertyString(pElement, IIS_CONFIG_CACHECUST, pwzData); + ExitOnFailure(hr, "Failed to set clientCache cacheControlCustom value"); + ReleaseNullObject(pSection); + ReleaseNullObject(pElement); + break; + } + case IIS_DIRPROP_NOCUSTERROR: + { + //no value, if have ID tag write clear to system.webServer/httpErrors + //error collection + hr = pAdminMgr->GetAdminSection(ScopeBSTR(IIS_CONFIG_HTTPERRORS_SECTION), pwzConfigPath, &pSection); + ExitOnFailure(hr, "Failed get httperrors section for DirProp"); + hr = pSection->get_Collection(&pCollection); + ExitOnFailure(hr, "Failed get error collection for DirProp"); + hr = pCollection->CreateNewElement(ScopeBSTR(IIS_CONFIG_CLEAR), &pElement); + ExitOnFailure(hr, "Failed to create clear element for error collection for DirProp"); + hr = pCollection->AddElement(pElement); + ExitOnFailure(hr, "Failed to add lear element for error collection for DirProp"); + ReleaseNullObject(pSection); + ReleaseNullObject(pElement); + break; + } + case IIS_DIRPROP_LOGVISITS: + { + hr = WcaReadIntegerFromCaData(ppwzCustomActionData, &iData); + ExitOnFailure(hr, "Failed to read DirProps logVisits"); + hr = pAdminMgr->GetAdminSection(ScopeBSTR(IIS_CONFIG_HTTPLOGGING_SECTION), pwzConfigPath, &pSection); + ExitOnFailure(hr, "Failed get httpLogging section for DirProp"); + hr = Iis7PutPropertyBool(pSection, IIS_CONFIG_DONTLOG, iData); + ExitOnFailure(hr, "Failed to set DirProps aspDetailedError"); + ReleaseNullObject(pSection); + break; + } + default: + { + ExitOnFailure(hr = E_UNEXPECTED, "Unexpected IIS Config action specified for WebDirProperties"); + break; + } + } + + // Get AppExt action + hr = WcaReadIntegerFromCaData(ppwzCustomActionData, &iAction); + ExitOnFailure(hr, "Failed to read DirProps Property action"); + } +LExit: + ReleaseStr(pwzConfigPath); + ReleaseStr(pwzWebName); + ReleaseStr(pwzWebRoot); + ReleaseStr(pwzData); + ReleaseObject(pSection); + ReleaseObject(pElement); + ReleaseObject(pCollection); + + return hr; +} + +//------------------------------------------------------------------------------------------------- +// IIS7SslBinding +// ProcessesVdir Properties CA Data +// +// +//------------------------------------------------------------------------------------------------- +HRESULT IIS7SslBinding( + __inout LPWSTR *ppwzCustomActionData, + __in IAppHostWritableAdminManager *pAdminMgr + ) +{ + HRESULT hr = S_OK; + int iAction = -1; + BOOL fSiteFound = FALSE; + + LPWSTR pwzSiteName = NULL; + LPWSTR pwzStoreName = NULL; + LPWSTR pwzEncodedCertificateHash = NULL; + + IAppHostElement *pSiteElem = NULL; + + // Get Application action + hr = WcaReadIntegerFromCaData( ppwzCustomActionData, &iAction); + ExitOnFailure(hr, "Failed to read binding action"); + + //get site key name + hr = WcaReadStringFromCaData(ppwzCustomActionData, &pwzSiteName); + ExitOnFailure(hr, "Failed to read binding site name key"); + + //get binding protocol + hr = WcaReadStringFromCaData(ppwzCustomActionData, &pwzStoreName); + ExitOnFailure(hr, "Failed to read binding protocol"); + + //get binding info + hr = WcaReadStringFromCaData(ppwzCustomActionData, &pwzEncodedCertificateHash); + ExitOnFailure(hr, "Failed to read binding info"); + + //Get site if it exists + hr = GetSiteElement(pAdminMgr, pwzSiteName, &pSiteElem, &fSiteFound); + ExitOnFailure(hr, "Failed to read sites from config"); + + if (IIS_CREATE == iAction) + { + if (fSiteFound) + { + //add SSL cert to binding + hr = CreateSslBinding(pSiteElem, pwzStoreName, pwzEncodedCertificateHash); + ExitOnFailure(hr, "Failed to create site binding"); + } + else + { + hr = HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND); + ExitOnFailure(hr, "Site not found for create binding"); + } + } + else if (IIS_DELETE == iAction) + { + if (fSiteFound) + { + //delete binding + hr = DeleteSslBinding(pSiteElem, pwzStoreName, pwzEncodedCertificateHash); + ExitOnFailure(hr, "Failed to delete binding"); + } + } + + LExit: + ReleaseStr(pwzSiteName); + ReleaseStr(pwzStoreName); + ReleaseStr(pwzEncodedCertificateHash); + ReleaseObject(pSiteElem); + + return hr; +} + +//------------------------------------------------------------------------------------------------- +// Helper Functions +// +// +// +//------------------------------------------------------------------------------------------------- + +static HRESULT GetNextAvailableSiteId( + IAppHostElementCollection *pCollection, + DWORD *plSiteId + ) +{ + HRESULT hr = S_OK; + IAppHostElement *pElement = NULL; + IAppHostProperty *pProperty = NULL; + + DWORD cSites; + DWORD plNextAvailSite = 0; + VARIANT vPropValue; + VARIANT vtIndex; + + VariantInit(&vPropValue); + VariantInit(&vtIndex); + + *plSiteId = 0; + + hr = pCollection->get_Count(&cSites); + ExitOnFailure(hr, "Failed get sites collection count"); + + vtIndex.vt = VT_UI4; + for (DWORD i = 0; i < cSites; ++i) + { + vtIndex.ulVal = i; + hr = pCollection->get_Item(vtIndex , &pElement); + ExitOnFailure(hr, "Failed get sites collection item"); + + hr = pElement->GetPropertyByName(ScopeBSTR(IIS_CONFIG_ID), &pProperty); + ExitOnFailure(hr, "Failed get site property"); + + hr = pProperty->get_Value(&vPropValue); + ExitOnFailure(hr, "Failed get site property value"); + + *plSiteId = vPropValue.lVal; + if (*plSiteId > plNextAvailSite) + { + plNextAvailSite = *plSiteId; + } + ReleaseNullObject(pElement); + ReleaseNullObject(pProperty); + } + *plSiteId = ++plNextAvailSite; + +LExit: + ReleaseVariant(vPropValue); + ReleaseVariant(vtIndex); + + ReleaseObject(pElement); + ReleaseObject(pProperty); + + return hr; +} + +static HRESULT GetSiteElement( + IAppHostWritableAdminManager *pAdminMgr, + LPCWSTR swSiteName, + IAppHostElement **ppSiteElement, + BOOL* fFound + ) +{ + HRESULT hr = S_OK; + IAppHostElement *pSites = NULL; + IAppHostElementCollection *pCollection = NULL; + + *fFound = FALSE; + + hr = pAdminMgr->GetAdminSection(ScopeBSTR(IIS_CONFIG_SITES_SECTION), ScopeBSTR(IIS_CONFIG_APPHOST_ROOT), &pSites); + ExitOnFailure(hr, "Failed get sites section"); + ExitOnNull(pSites, hr, ERROR_FILE_NOT_FOUND, "Failed get sites section object"); + + hr = pSites->get_Collection(&pCollection); + ExitOnFailure(hr, "Failed get sites collection"); + + hr = Iis7FindAppHostElementString(pCollection, IIS_CONFIG_SITE, IIS_CONFIG_NAME, swSiteName, ppSiteElement, NULL); + ExitOnFailure(hr, "Failed to find site %ls", swSiteName); + + *fFound = ppSiteElement != NULL && *ppSiteElement != NULL; + +LExit: + ReleaseObject(pSites); + ReleaseObject(pCollection); + + return hr; +} + +static HRESULT GetApplicationElement( IAppHostElement *pSiteElement, + LPCWSTR swAppPath, + IAppHostElement **ppAppElement, + BOOL* fFound) +{ + HRESULT hr = S_OK; + IAppHostElementCollection *pCollection = NULL; + + *fFound = FALSE; + + hr = pSiteElement->get_Collection( &pCollection); + ExitOnFailure(hr, "Failed get site app collection"); + + hr = Iis7FindAppHostElementString(pCollection, IIS_CONFIG_APPLICATION, IIS_CONFIG_PATH, swAppPath, ppAppElement, NULL); + ExitOnFailure(hr, "Failed to find app %ls", swAppPath); + + *fFound = ppAppElement != NULL && *ppAppElement != NULL; + +LExit: + ReleaseObject(pCollection); + + return hr; +} + +static HRESULT GetApplicationElementForVDir( IAppHostElement *pSiteElement, + LPCWSTR pwzVDirPath, + IAppHostElement **ppAppElement, + LPCWSTR *ppwzVDirSubPath, + BOOL* fFound) +{ + HRESULT hr = S_OK; + IAppHostElementCollection *pCollection = NULL; + LPWSTR pwzAppPath = NULL; + *fFound = FALSE; + *ppwzVDirSubPath = NULL; + + hr = pSiteElement->get_Collection( &pCollection); + ExitOnFailure(hr, "Failed get site app collection"); + + // Start with full path + int iLastPathIndex = lstrlenW(pwzVDirPath) - 1; + hr = StrAllocString(&pwzAppPath, pwzVDirPath, 0); + ExitOnFailure(hr, "Failed allocate application path"); + + for (int iSubPathIndex = iLastPathIndex; (iSubPathIndex >= 0) && (!*fFound); --iSubPathIndex) + { + // We are looking at the full path, or at a directory boundary, or at the root + if (iSubPathIndex == iLastPathIndex || + '/' == pwzAppPath[iSubPathIndex] || + 0 == iSubPathIndex) + { + // break the path if needed + if ('/' == pwzAppPath[iSubPathIndex]) + { + pwzAppPath[iSubPathIndex] = '\0'; + } + + // Special case for root path, need an empty app path + LPCWSTR pwzAppSearchPath = 0 == iSubPathIndex ? L"/" : pwzAppPath; + + // Try to find an app with the specified path + hr = Iis7FindAppHostElementString(pCollection, IIS_CONFIG_APPLICATION, IIS_CONFIG_PATH, pwzAppSearchPath, ppAppElement, NULL); + ExitOnFailure(hr, "Failed to search for app %ls", pwzAppSearchPath); + *fFound = ppAppElement != NULL && *ppAppElement != NULL; + + if (*fFound) + { + // set return value for sub path + // special case for app path == vdir path, need an empty subpath. + *ppwzVDirSubPath = (iSubPathIndex == iLastPathIndex) ? L"/" : pwzVDirPath + iSubPathIndex; + } + } + } + +LExit: + ReleaseObject(pCollection); + ReleaseStr(pwzAppPath); + + return hr; +} + +static HRESULT CreateSite( + __in IAppHostElementCollection *pCollection, + __in LPCWSTR swSiteName, + __out IAppHostElement **pSiteElement + ) +{ + HRESULT hr = S_OK; + IAppHostElement *pNewElement = NULL; + + hr = pCollection->CreateNewElement(ScopeBSTR(IIS_CONFIG_SITE), &pNewElement); + ExitOnFailure(hr, "Failed create site element"); + + hr = Iis7PutPropertyString(pNewElement, IIS_CONFIG_NAME, swSiteName); + ExitOnFailure(hr, "Failed set site name property"); + + DWORD lSiteId = 0; + hr = GetNextAvailableSiteId(pCollection, &lSiteId); + ExitOnFailure(hr, "Failed get next site id"); + + Iis7PutPropertyInteger(pNewElement, IIS_CONFIG_ID, lSiteId); + ExitOnFailure(hr, "Failed set site id property"); + + hr = pCollection->AddElement(pNewElement); + ExitOnFailure(hr, "Failed add site element"); + + *pSiteElement = pNewElement; + pNewElement = NULL; + +LExit: + ReleaseObject(pNewElement); + + return hr; +} + +static HRESULT CreateApplication( + IAppHostElement *pSiteElement, + LPCWSTR swAppPath, + IAppHostElement **pAppElement + ) +{ + HRESULT hr = S_OK; + IAppHostElement *pNewElement = NULL; + IAppHostElementCollection *pCollection = NULL; + + hr = pSiteElement->get_Collection(&pCollection); + ExitOnFailure(hr, "Failed get application collection"); + + hr = pCollection->CreateNewElement(ScopeBSTR(IIS_CONFIG_APPLICATION), &pNewElement); + ExitOnFailure(hr, "Failed get application element"); + + hr = Iis7PutPropertyString(pNewElement, IIS_CONFIG_PATH, swAppPath); + ExitOnFailure(hr, "Failed set application path property"); + + hr = pCollection->AddElement(pNewElement); + ExitOnFailure(hr, "Failed add application to collection"); + + *pAppElement = pNewElement; + pNewElement = NULL; + +LExit: + ReleaseObject(pCollection); + ReleaseObject(pNewElement); + + return hr; +} + +static HRESULT DeleteApplication( + IAppHostElement *pSiteElement, + LPCWSTR swAppPath + ) +{ + HRESULT hr = S_OK; + IAppHostElementCollection *pCollection = NULL; + + hr = pSiteElement->get_Collection(&pCollection); + ExitOnFailure(hr, "Failed get application collection"); + + hr = DeleteCollectionElement(pCollection, IIS_CONFIG_APPLICATION, IIS_CONFIG_PATH, swAppPath); + ExitOnFailure(hr, "Failed to delete website"); + +LExit: + ReleaseObject(pCollection); + + return hr; +} + +static HRESULT SetAppPool( + IAppHostElement *pAppElement, + LPCWSTR pwzAppPool + ) +{ + HRESULT hr = S_OK; + + if (*pwzAppPool != 0) + { + hr = Iis7PutPropertyString(pAppElement, IIS_CONFIG_APPPOOL, pwzAppPool); + ExitOnFailure(hr, "Failed set application appPool property"); + } +LExit: + return hr; +} + +static HRESULT CreateVdir( + IAppHostElement *pAppElement, + LPCWSTR pwzVDirPath, + LPCWSTR pwzVDirPhyDir + ) +{ + HRESULT hr = S_OK; + IAppHostElement *pElement = NULL; + IAppHostElementCollection *pCollection = NULL; + BOOL fFound; + + hr = pAppElement->get_Collection(&pCollection); + ExitOnFailure(hr, "Failed get application VDir collection"); + + hr = Iis7FindAppHostElementString(pCollection, IIS_CONFIG_VDIR, IIS_CONFIG_PATH, pwzVDirPath, &pElement, NULL); + ExitOnFailure(hr, "Failed while finding virtualDir"); + fFound = (NULL != pElement); + + if (!fFound) + { + hr = pCollection->CreateNewElement(ScopeBSTR(IIS_CONFIG_VDIR), &pElement); + ExitOnFailure(hr, "Failed create application VDir collection"); + + hr = Iis7PutPropertyString(pElement, IIS_CONFIG_PATH, pwzVDirPath); + ExitOnFailure(hr, "Failed set VDir path property"); + } + + hr = Iis7PutPropertyString(pElement, IIS_CONFIG_PHYSPATH, pwzVDirPhyDir); + ExitOnFailure(hr, "Failed set VDir phys path property"); + + if (!fFound) + { + hr = pCollection->AddElement(pElement); + ExitOnFailure(hr, "Failed add application VDir element"); + } + +LExit: + ReleaseObject(pCollection); + ReleaseObject(pElement); + + return hr; +} + +static HRESULT DeleteVdir( + IAppHostElement *pAppElement, + LPCWSTR pwzVDirPath + ) +{ + HRESULT hr = S_OK; + IAppHostElementCollection *pCollection = NULL; + + hr = pAppElement->get_Collection(&pCollection); + ExitOnFailure(hr, "Failed get application VDir collection"); + + hr = DeleteCollectionElement(pCollection, IIS_CONFIG_VDIR, IIS_CONFIG_PATH, pwzVDirPath); + ExitOnFailure(hr, "Failed to delete vdir"); + +LExit: + ReleaseObject(pCollection); + + return hr; +} + +static HRESULT CreateBinding( + IAppHostElement *pSiteElem, + LPCWSTR pwzProtocol, + LPCWSTR pwzInfo + ) +{ + HRESULT hr = S_OK; + IAppHostChildElementCollection *pChildElems = NULL; + IAppHostElement *pBindings = NULL; + IAppHostElement *pBindingElement = NULL; + IAppHostElementCollection *pCollection = NULL; + + VARIANT vtProp; + + VariantInit(&vtProp); + + hr = pSiteElem->get_ChildElements(&pChildElems); + ExitOnFailure(hr, "Failed get site child elements collection"); + + vtProp.vt = VT_BSTR; + vtProp.bstrVal = ::SysAllocString(IIS_CONFIG_BINDINGS); + hr = pChildElems->get_Item(vtProp, &pBindings); + ExitOnFailure(hr, "Failed get bindings element"); + ReleaseVariant(vtProp); + + hr = pBindings->get_Collection(&pCollection); + ExitOnFailure(hr, "Failed get bindings collection"); + + hr = pCollection->CreateNewElement(ScopeBSTR(IIS_CONFIG_BINDING), &pBindingElement); + ExitOnFailure(hr, "Failed get binding element"); + + hr = Iis7PutPropertyString(pBindingElement, IIS_CONFIG_PROTOCOL, pwzProtocol); + ExitOnFailure(hr, "Failed set binding protocol property"); + + hr = Iis7PutPropertyString(pBindingElement, IIS_CONFIG_BINDINGINFO, pwzInfo); + ExitOnFailure(hr, "Failed set binding information property"); + + hr = pCollection->AddElement(pBindingElement); + if (hr == HRESULT_FROM_WIN32(ERROR_ALREADY_EXISTS)) + { + //Eat this error. Binding is there and nothing to repair since + //identity == protocol + info so all is OK + hr = S_OK; + } + else + { + ExitOnFailure(hr, "Failed add binding to site"); + } + +LExit: + ReleaseVariant(vtProp); + + ReleaseObject(pCollection); + ReleaseObject(pChildElems); + ReleaseObject(pBindingElement); + ReleaseObject(pBindings); + + return hr; +} +static HRESULT CreateWebLog( + IAppHostElement *pSiteElem, + LPCWSTR pwzFormat + ) +{ + HRESULT hr = S_OK; + IAppHostChildElementCollection *pChildElems = NULL; + IAppHostElement *pLogFile = NULL; + + VARIANT vtProp; + + VariantInit(&vtProp); + + hr = pSiteElem->get_ChildElements(&pChildElems); + ExitOnFailure(hr, "Failed get site child elements collection"); + + vtProp.vt = VT_BSTR; + vtProp.bstrVal = ::SysAllocString(IIS_CONFIG_WEBLOG); + hr = pChildElems->get_Item(vtProp, &pLogFile); + ExitOnFailure(hr, "Failed get logfile element"); + ReleaseVariant(vtProp); + + if (CSTR_EQUAL != ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, pwzFormat, -1, L"none", -1)) + { + hr = Iis7PutPropertyString(pLogFile, IIS_CONFIG_LOGFORMAT, pwzFormat); + ExitOnFailure(hr, "Failed set logfile format property"); + hr = Iis7PutPropertyString(pLogFile, IIS_CONFIG_ENABLED, IIS_CONFIG_TRUE); + ExitOnFailure(hr, "Failed set logfile enabled property"); + } + else + { + hr = Iis7PutPropertyString(pLogFile, IIS_CONFIG_ENABLED, IIS_CONFIG_FALSE); + ExitOnFailure(hr, "Failed set logfile enabled property"); + } + +LExit: + ReleaseVariant(vtProp); + + ReleaseObject(pLogFile); + ReleaseObject(pChildElems); + + return hr; +} + +static HRESULT DeleteBinding( + IAppHostElement* /*pSiteElem*/, + LPCWSTR /*pwzProtocol*/, + LPCWSTR /*pwzInfo*/ + ) +{ + HRESULT hr = S_OK; + // + //this isn't supported right now, we should support this for the SiteSearch scenario + return hr; +} + +struct SCA_SSLBINDINGINFO +{ + IIS7_APPHOSTELEMENTCOMPARISON comparison; + LPCWSTR pwzStoreName; + LPCWSTR pwzEncodedCertificateHash; + HRESULT hr; +}; + +static BOOL AddSslCertificateToBindingCallback(IAppHostElement *pBindingElement, LPVOID pContext) +{ + HRESULT hr = S_OK; + VARIANT vtProp; + VariantInit(&vtProp); + SCA_SSLBINDINGINFO* pBindingInfo = (SCA_SSLBINDINGINFO*)pContext; + IAppHostMethodCollection *pAppHostMethodCollection = NULL; + IAppHostMethod *pAddSslMethod = NULL; + IAppHostMethodInstance *pAddSslMethodInstance = NULL; + IAppHostElement *pAddSslInput = NULL; + int iWsaError = 0; + WSADATA wsaData = {}; + BOOL fWsaInitialized = FALSE; + + // IIS's AddSslCertificate doesn't initialize WinSock on 2008 before using it to parse the IP + // Initialize before calling to workaround the failure. + iWsaError = WSAStartup(MAKEWORD(2, 2), &wsaData); + if (0 != iWsaError) + { + ExitOnWin32Error(iWsaError, hr, "Failed to initialize WinSock"); + } + + fWsaInitialized = TRUE; + + if (Iis7IsMatchingAppHostElement(pBindingElement, &pBindingInfo->comparison)) + { + hr = pBindingElement->get_Methods(&pAppHostMethodCollection); + ExitOnFailure(hr, "failed to get binding method collection"); + + hr = Iis7FindAppHostMethod(pAppHostMethodCollection, L"AddSslCertificate", &pAddSslMethod, NULL); + if (FAILED(hr)) + { + WcaLog(LOGMSG_STANDARD, "The AddSslCertificate method is not supported by the binding element, SSL certificate will not be associated with the website"); + ExitFunction(); + } + + pAddSslMethod->CreateInstance(&pAddSslMethodInstance); + ExitOnFailure(hr, "failed to create an instance of AddSslCertificate method"); + + pAddSslMethodInstance->get_Input(&pAddSslInput); + ExitOnFailure(hr, "failed to get input element of AddSslCertificate method"); + + Iis7PutPropertyString(pAddSslInput, IIS_CONFIG_CERTIFICATESTORENAME, pBindingInfo->pwzStoreName); + ExitOnFailure(hr, "failed to set certificateStoreName input parameter of AddSslCertificate method"); + + Iis7PutPropertyString(pAddSslInput, IIS_CONFIG_CERTIFICATEHASH, pBindingInfo->pwzEncodedCertificateHash); + ExitOnFailure(hr, "failed to set certificateHash input parameter of AddSslCertificate method"); + + hr = pAddSslMethodInstance->Execute(); + ExitOnFailure(hr, "failed to execute AddSslCertificate method"); + } +LExit: + pBindingInfo->hr = hr; + ReleaseObject(pAppHostMethodCollection); + ReleaseObject(pAddSslMethod); + ReleaseObject(pAddSslMethodInstance); + ReleaseObject(pAddSslInput); + if (fWsaInitialized) + { + WSACleanup(); + } + + return FAILED(hr); +} + +static HRESULT CreateSslBinding( IAppHostElement *pSiteElem, LPCWSTR pwzStoreName, LPCWSTR pwzEncodedCertificateHash) +{ + HRESULT hr = S_OK; + IAppHostChildElementCollection *pChildElems = NULL; + IAppHostElement *pBindingsElement = NULL; + IAppHostElementCollection *pBindingsCollection = NULL; + SCA_SSLBINDINGINFO bindingInfo = {}; + VARIANT vtProp; + VariantInit(&vtProp); + + hr = pSiteElem->get_ChildElements(&pChildElems); + ExitOnFailure(hr, "Failed get site child elements collection"); + + vtProp.vt = VT_BSTR; + vtProp.bstrVal = ::SysAllocString(IIS_CONFIG_BINDINGS); + hr = pChildElems->get_Item(vtProp, &pBindingsElement); + ExitOnFailure(hr, "Failed get bindings element"); + ReleaseVariant(vtProp); + + hr = pBindingsElement->get_Collection(&pBindingsCollection); + ExitOnFailure(hr, "Failed get bindings collection"); + + bindingInfo.comparison.sczElementName = IIS_CONFIG_BINDING; + bindingInfo.comparison.sczAttributeName = IIS_CONFIG_PROTOCOL; + vtProp.vt = VT_BSTR; + vtProp.bstrVal = ::SysAllocString(L"https"); + bindingInfo.comparison.pvAttributeValue = &vtProp; + bindingInfo.pwzStoreName = pwzStoreName; + bindingInfo.pwzEncodedCertificateHash = pwzEncodedCertificateHash; + + // Our current IISWebSiteCertificates schema does not allow specification of the website binding + // to associate the certificate with. For now just associate it with all secure bindings. + + hr = Iis7EnumAppHostElements(pBindingsCollection, AddSslCertificateToBindingCallback, &bindingInfo, NULL, NULL); + ExitOnFailure(hr, "Failed to enumerate bindings collection"); + hr = bindingInfo.hr; + ExitOnFailure(hr, "Failed to add ssl binding"); + +LExit: + ReleaseVariant(vtProp); + + ReleaseObject(pChildElems); + ReleaseObject(pBindingsElement); + ReleaseObject(pBindingsCollection); + + return hr; +} + +static HRESULT DeleteSslBinding( + IAppHostElement * /*pSiteElem*/, + LPCWSTR /*pwzStoreName*/, + LPCWSTR /*pwzEncodedCertificateHash*/ + ) +{ + HRESULT hr = S_OK; + // + //this isn't supported right now, we should support this for the SiteSearch scenario + return hr; +} + +static HRESULT DeleteAppPool( IAppHostWritableAdminManager *pAdminMgr, + LPCWSTR swAppPoolName) +{ + HRESULT hr = S_OK; + IAppHostElement *pAppPools = NULL; + IAppHostElementCollection *pCollection = NULL; + + hr = pAdminMgr->GetAdminSection(ScopeBSTR(IIS_CONFIG_APPPOOL_SECTION), ScopeBSTR(IIS_CONFIG_APPHOST_ROOT), &pAppPools); + ExitOnFailure(hr, "Failed get AppPools section"); + ExitOnNull(pAppPools, hr, E_UNEXPECTED, "Failed get appPools section object"); + + hr = pAppPools->get_Collection( &pCollection); + ExitOnFailure(hr, "Failed get AppPools collection"); + + hr = DeleteCollectionElement(pCollection, IIS_CONFIG_ADD, IIS_CONFIG_NAME, swAppPoolName); + ExitOnFailure(hr, "Failed to delete app pool %ls", swAppPoolName); + +LExit: + ReleaseObject(pAppPools); + ReleaseObject(pCollection); + + return hr; +} + +static HRESULT CreateAppPool( + __inout LPWSTR *ppwzCustomActionData, + IAppHostWritableAdminManager *pAdminMgr, + LPCWSTR swAppPoolName + ) +{ + HRESULT hr = S_OK; + IAppHostElement *pAppPools = NULL; + IAppHostElement *pAppPoolElement = NULL; + IAppHostElement *pElement = NULL; + IAppHostElement *pElement2 = NULL; + IAppHostElement *pElement3 = NULL; + IAppHostElementCollection *pCollection = NULL; + IAppHostElementCollection *pCollection2 = NULL; + int iAction = -1; + int iData = 0; + LPWSTR pwzData = NULL; + WCHAR wcData[512]; + WCHAR wcTime[60]; + BOOL fFound = FALSE; + + hr = pAdminMgr->GetAdminSection(ScopeBSTR(IIS_CONFIG_APPPOOL_SECTION), ScopeBSTR(IIS_CONFIG_APPHOST_ROOT), &pAppPools); + ExitOnFailure(hr, "Failed get AppPools section"); + ExitOnNull(pAppPools, hr, ERROR_FILE_NOT_FOUND, "Failed get AppPools section object"); + + hr = pAppPools->get_Collection( &pCollection); + ExitOnFailure(hr, "Failed get AppPools collection"); + + hr = Iis7FindAppHostElementString(pCollection, IIS_CONFIG_ADD, IIS_CONFIG_NAME, swAppPoolName, &pAppPoolElement, NULL); + ExitOnFailure(hr, "Failed find AppPool element"); + fFound = (NULL != pAppPoolElement); + + if (!fFound) + { + hr = pCollection->CreateNewElement(ScopeBSTR(IIS_CONFIG_ADD), &pAppPoolElement); + ExitOnFailure(hr, "Failed create AppPool element"); + } + + hr = Iis7PutPropertyString(pAppPoolElement, IIS_CONFIG_NAME, swAppPoolName); + ExitOnFailure(hr, "Failed set AppPool name property"); + + //For WiX II6 /ABO compat we will default managedPipelineMode="Classic" + hr = Iis7PutPropertyString(pAppPoolElement, IIS_CONFIG_PIPELINEMODE, L"Classic"); + ExitOnFailure(hr, "Failed set AppPool managedPipelineMode property"); + //For WiX II6 /ABO compat we will be hardcoding autostart="true" + hr = Iis7PutPropertyString(pAppPoolElement, IIS_CONFIG_APPPOOL_AUTO, L"true"); + ExitOnFailure(hr, "Failed set AppPool autoStart property"); + + if (!fFound) + { + hr = pCollection->AddElement(pAppPoolElement); + ExitOnFailure(hr, "Failed to add appPool element"); + } + + // Get AppPool Property action + hr = WcaReadIntegerFromCaData(ppwzCustomActionData, &iAction); + ExitOnFailure(hr, "Failed to read AppPool Property action"); + while (IIS_APPPOOL_END != iAction) + { + //Process property action + switch (iAction) + { + case IIS_APPPOOL_RECYCLE_MIN : + { + // /recycling / periodicRestart | time + hr = WcaReadIntegerFromCaData(ppwzCustomActionData, &iData); + ExitOnFailure(hr, "Failed to read AppPool recycle min"); + hr = pAppPoolElement->GetElementByName(ScopeBSTR(IIS_CONFIG_RECYCLING), &pElement); + ExitOnFailure(hr, "Failed to get AppPool recycling element"); + hr = pElement->GetElementByName(ScopeBSTR(IIS_CONFIG_PEROIDRESTART), &pElement2); + ExitOnFailure(hr, "Failed to get AppPool periodicRestart element"); + *wcTime = '\0'; + ConvSecToHMS(iData * 60, wcTime, countof(wcTime)); + hr = Iis7PutPropertyString(pElement2, IIS_CONFIG_TIME, wcTime); + ExitOnFailure(hr, "Failed to set AppPool periodicRestart time value"); + ReleaseNullObject(pElement); + ReleaseNullObject(pElement2); + break; + } + case IIS_APPPOOL_RECYCLE_REQ : + { + // /recycling / periodicRestart | requests + hr = WcaReadIntegerFromCaData(ppwzCustomActionData, &iData); + ExitOnFailure(hr, "Failed to read AppPool recycle req"); + hr = pAppPoolElement->GetElementByName(ScopeBSTR(IIS_CONFIG_RECYCLING), &pElement); + ExitOnFailure(hr, "Failed to get AppPool recycling element"); + hr = pElement->GetElementByName(ScopeBSTR(IIS_CONFIG_PEROIDRESTART), &pElement2); + ExitOnFailure(hr, "Failed to get AppPool periodicRestart element"); + hr = Iis7PutPropertyInteger(pElement2, IIS_CONFIG_REQUESTS, iData); + ExitOnFailure(hr, "Failed to set AppPool periodicRestart time value"); + ReleaseNullObject(pElement); + ReleaseNullObject(pElement2); + break; + } + case IIS_APPPOOL_RECYCLE_TIMES : + { + // /recycling / periodicRestart | schedule + hr = WcaReadStringFromCaData(ppwzCustomActionData, &pwzData); + ExitOnFailure(hr, "Failed to read AppPool recycle times"); + hr = pAppPoolElement->GetElementByName(ScopeBSTR(IIS_CONFIG_RECYCLING), &pElement); + ExitOnFailure(hr, "Failed to get AppPool recycling element"); + hr = pElement->GetElementByName(ScopeBSTR(IIS_CONFIG_PEROIDRESTART), &pElement2); + ExitOnFailure(hr, "Failed to get AppPool periodicRestart element"); + hr = pElement2->GetElementByName(ScopeBSTR(IIS_CONFIG_SCHEDULE), &pElement3); + ExitOnFailure(hr, "Failed to get AppPool schedule element"); + hr = pElement3->get_Collection(&pCollection2); + ExitOnFailure(hr, "Failed to get AppPool schedule collection"); + + WCHAR wcDelim[] = L","; + const WCHAR *wszToken = NULL; + WCHAR *wszNextToken = NULL; + wszToken = wcstok_s( pwzData, wcDelim, &wszNextToken); + + while (wszToken) + { + *wcData = '\0'; + hr = ::StringCchCopyW(wcData, countof(wcData), wszToken); + ExitOnFailure(hr, "failed to copy AppPool schedule"); + hr = ::StringCchCatW(wcData, countof(wcData), L":00"); + ExitOnFailure(hr, "failed to append AppPool schedule"); + + hr = pCollection2->CreateNewElement(ScopeBSTR(IIS_CONFIG_ADD), &pElement3); + ExitOnFailure(hr, "Failed to create AppPool schedule element"); + + hr = Iis7PutPropertyString(pElement3, IIS_CONFIG_VALUE, wcData); + ExitOnFailure(hr, "Failed to set AppPool schedule value"); + + hr = pCollection2->AddElement(pElement3); + if (hr == HRESULT_FROM_WIN32(ERROR_ALREADY_EXISTS)) + { + //Eat this error, recycle time already exists NBD + hr = S_OK; + } + ExitOnFailure(hr, "Failed to add win auth providers element"); + ReleaseNullObject(pElement3); + wszToken = wcstok_s( NULL, wcDelim, &wszNextToken); + } + ReleaseNullObject(pElement); + ReleaseNullObject(pElement2); + ReleaseNullObject(pElement3); + ReleaseNullObject(pCollection2); + break; + } + case IIS_APPPOOL_RECYCLE_VIRMEM : + { + // /recycling / periodicRestart | memory + hr = WcaReadIntegerFromCaData(ppwzCustomActionData, &iData); + ExitOnFailure(hr, "Failed to read AppPool recycle vir memory"); + hr = pAppPoolElement->GetElementByName(ScopeBSTR(IIS_CONFIG_RECYCLING), &pElement); + ExitOnFailure(hr, "Failed to get AppPool recycling element"); + hr = pElement->GetElementByName(ScopeBSTR(IIS_CONFIG_PEROIDRESTART), &pElement2); + ExitOnFailure(hr, "Failed to get AppPool periodicRestart element"); + hr = Iis7PutPropertyInteger(pElement2, IIS_CONFIG_MEMORY, iData); + ExitOnFailure(hr, "Failed to set AppPool periodicRestart memory"); + ReleaseNullObject(pElement); + ReleaseNullObject(pElement2); + break; + } + case IIS_APPPOOL_RECYCLE_PRIVMEM : + { + // /recycling / periodicRestart | privateMemory + hr = WcaReadIntegerFromCaData(ppwzCustomActionData, &iData); + ExitOnFailure(hr, "Failed to read AppPool recycle priv mem"); + hr = pAppPoolElement->GetElementByName(ScopeBSTR(IIS_CONFIG_RECYCLING), &pElement); + ExitOnFailure(hr, "Failed to get AppPool recycling element"); + hr = pElement->GetElementByName(ScopeBSTR(IIS_CONFIG_PEROIDRESTART), &pElement2); + ExitOnFailure(hr, "Failed to get AppPool periodicRestart element"); + hr = Iis7PutPropertyInteger(pElement2, IIS_CONFIG_PRIVMEMORY, iData); + ExitOnFailure(hr, "Failed to set AppPool periodicRestart private memory"); + ReleaseNullObject(pElement); + ReleaseNullObject(pElement2); + break; + } + case IIS_APPPOOL_RECYCLE_IDLTIMEOUT : + { + // /processModel | idleTimeout + hr = WcaReadIntegerFromCaData(ppwzCustomActionData, &iData); + ExitOnFailure(hr, "Failed to read AppPool idle timeout"); + hr = pAppPoolElement->GetElementByName(ScopeBSTR(IIS_CONFIG_PROCESSMODEL), &pElement); + ExitOnFailure(hr, "Failed to get AppPool processModel element"); + *wcTime = '\0'; + ConvSecToHMS(iData * 60, wcTime, countof(wcTime)); + hr = Iis7PutPropertyString(pElement, IIS_CONFIG_IDLETIMEOUT, wcTime); + ExitOnFailure(hr, "Failed to set AppPool processModel idle timeout value"); + ReleaseNullObject(pElement); + break; + } + case IIS_APPPOOL_RECYCLE_QUEUELIMIT : + { + // /applicationPools | queueLength + hr = WcaReadIntegerFromCaData(ppwzCustomActionData, &iData); + ExitOnFailure(hr, "Failed to read AppPool recycle queue limit"); + hr = Iis7PutPropertyInteger(pAppPoolElement, IIS_CONFIG_QUEUELENGTH, iData); + ExitOnFailure(hr, "Failed to set AppPool recycle queue limit value"); + break; + } + case IIS_APPPOOL_MAXPROCESS : + { + // /processModel | maxProcesses + hr = WcaReadIntegerFromCaData(ppwzCustomActionData, &iData); + ExitOnFailure(hr, "Failed to read AppPool max processes"); + hr = pAppPoolElement->GetElementByName(ScopeBSTR(IIS_CONFIG_PROCESSMODEL), &pElement); + ExitOnFailure(hr, "Failed to get AppPool processModel element"); + hr = Iis7PutPropertyInteger(pElement, IIS_CONFIG_MAXWRKPROCESSES, iData); + ExitOnFailure(hr, "Failed to set AppPool processModel maxProcesses value"); + ReleaseNullObject(pElement); + break; + } + case IIS_APPPOOL_IDENTITY : + { + //"LocalSystem" 0 + //"LocalService" 1 + //"NetworkService" 2 + //"SpecificUser" 3 + //"ApplicationPoolIdentity" 4 + // /processModel | identityType + hr = WcaReadIntegerFromCaData(ppwzCustomActionData, &iData); + ExitOnFailure(hr, "Failed to read AppPool identity"); + hr = pAppPoolElement->GetElementByName(ScopeBSTR(IIS_CONFIG_PROCESSMODEL), &pElement); + ExitOnFailure(hr, "Failed to get AppPool processModel element"); + if (iData == 0) + { + hr = Iis7PutPropertyString(pElement, IIS_CONFIG_IDENITITYTYPE, IIS_CONFIG_LOCALSYSTEM); + } + else if (iData == 1) + { + hr = Iis7PutPropertyString(pElement, IIS_CONFIG_IDENITITYTYPE, IIS_CONFIG_LOCALSERVICE); + } + else if (iData == 2) + { + hr = Iis7PutPropertyString(pElement, IIS_CONFIG_IDENITITYTYPE, IIS_CONFIG_NETWORKSERVICE); + } + else if (iData == 3) + { + hr = Iis7PutPropertyString(pElement, IIS_CONFIG_IDENITITYTYPE, IIS_CONFIG_SPECIFICUSER); + } + else if (iData == 4) + { + hr = Iis7PutPropertyString(pElement, IIS_CONFIG_IDENITITYTYPE, IIS_CONFIG_APPLICATIONPOOLIDENTITY); + } + ExitOnFailure(hr, "Failed to set AppPool processModel identityType value"); + ReleaseNullObject(pElement); + break; + } + case IIS_APPPOOL_USER : + { + // /processModel | userName + hr = WcaReadStringFromCaData(ppwzCustomActionData, &pwzData); + ExitOnFailure(hr, "Failed to read AppPool user"); + hr = pAppPoolElement->GetElementByName(ScopeBSTR(IIS_CONFIG_PROCESSMODEL), &pElement); + ExitOnFailure(hr, "Failed to get AppPool processModel element"); + hr = Iis7PutPropertyString(pElement, IIS_CONFIG_USERNAME, pwzData); + ExitOnFailure(hr, "Failed to set AppPool processModel username value"); + ReleaseNullObject(pElement); + break; + } + case IIS_APPPOOL_PWD : + { + // /processModel | password + hr = WcaReadStringFromCaData(ppwzCustomActionData, &pwzData); + ExitOnFailure(hr, "Failed to read AppPool pwd"); + hr = pAppPoolElement->GetElementByName(ScopeBSTR(IIS_CONFIG_PROCESSMODEL), &pElement); + ExitOnFailure(hr, "Failed to get AppPool processModel element"); + hr = Iis7PutPropertyString(pElement, IIS_CONFIG_PASSWORD, pwzData); + ExitOnFailure(hr, "Failed to set AppPool processModel password value"); + ReleaseNullObject(pElement); + break; + } + case IIS_APPPOOL_RECYCLE_CPU_PCT: + { + // /cpu | limit + hr = WcaReadIntegerFromCaData(ppwzCustomActionData, &iData); + ExitOnFailure(hr, "Failed to read cpu pct"); + hr = pAppPoolElement->GetElementByName(ScopeBSTR(IIS_CONFIG_CPU), &pElement); + ExitOnFailure(hr, "Failed to get AppPool cpu element"); + // limit is maximum percentage of CPU time (in 1/1000ths of one percent) + hr = Iis7PutPropertyInteger(pElement, IIS_CONFIG_LIMIT, iData * 1000); + ExitOnFailure(hr, "Failed to set AppPool cpu limit"); + ReleaseNullObject(pElement); + break; + } + case IIS_APPPOOL_RECYCLE_CPU_REFRESH: + { + // /cpu | resetInterval + hr = WcaReadIntegerFromCaData(ppwzCustomActionData, &iData); + ExitOnFailure(hr, "Failed to read cpu refresh pwd"); + hr = pAppPoolElement->GetElementByName(ScopeBSTR(IIS_CONFIG_CPU), &pElement); + ExitOnFailure(hr, "Failed to get AppPool cpu element"); + *wcTime = '\0'; + ConvSecToHMS(iData * 60, wcTime, countof(wcTime)); + hr = Iis7PutPropertyString(pElement, IIS_CONFIG_RESETINTERVAL, wcTime); + ExitOnFailure(hr, "Failed to set AppPool cpu resetInterval value"); + ReleaseNullObject(pElement); + break; + } + case IIS_APPPOOL_RECYCLE_CPU_ACTION: + { + // /cpu | action + //"NoAction" 0 + //"KillW3wp" 1 + hr = WcaReadIntegerFromCaData(ppwzCustomActionData, &iData); + ExitOnFailure(hr, "Failed to read cpu action"); + hr = pAppPoolElement->GetElementByName(ScopeBSTR(IIS_CONFIG_CPU), &pElement); + ExitOnFailure(hr, "Failed to get AppPool cpu element"); + if (iData) + { + hr = Iis7PutPropertyString(pElement, IIS_CONFIG_CPU_ACTION, IIS_CONFIG_KILLW3WP); + } + else + { + hr = Iis7PutPropertyString(pElement, IIS_CONFIG_CPU_ACTION, IIS_CONFIG_NOACTION); + } + ExitOnFailure(hr, "Failed to set AppPool cpu action value"); + ReleaseNullObject(pElement); + break; + } + case IIS_APPPOOL_32BIT: + { + hr = WcaReadIntegerFromCaData(ppwzCustomActionData, &iData); + ExitOnFailure(hr, "Failed to read enable32BitAppOnWin64 value"); + // enable32BitAppOnWin64 + hr = Iis7PutPropertyBool(pAppPoolElement, IIS_CONFIG_ENABLE32, iData ? TRUE : FALSE); + ExitOnFailure(hr, "Failed to set AppPool enable32BitAppOnWin64 value"); + break; + } + case IIS_APPPOOL_MANAGED_PIPELINE_MODE: + { + // managedPipelineMode + hr = WcaReadStringFromCaData(ppwzCustomActionData, &pwzData); + ExitOnFailure(hr, "Failed to read AppPool managedRuntimeVersion"); + hr = Iis7PutPropertyString(pAppPoolElement, IIS_CONFIG_PIPELINEMODE, pwzData); + ExitOnFailure(hr, "Failed set AppPool managedPipelineMode property"); + break; + } + case IIS_APPPOOL_MANAGED_RUNTIME_VERSION: + { + // managedRuntimeVersion + hr = WcaReadStringFromCaData(ppwzCustomActionData, &pwzData); + ExitOnFailure(hr, "Failed to read AppPool managedRuntimeVersion"); + hr = Iis7PutPropertyString(pAppPoolElement, IIS_CONFIG_MANAGEDRUNTIMEVERSION, pwzData); + ExitOnFailure(hr, "Failed set AppPool managedRuntimeVersion property"); + break; + } + + default: + ExitOnFailure(hr = E_UNEXPECTED, "Unexpected IIS Config action specified for AppPool"); + break; + + } + // Get AppPool property action + hr = WcaReadIntegerFromCaData(ppwzCustomActionData, &iAction); + ExitOnFailure(hr, "Failed to read AppPool Property action"); + } + +LExit: + ReleaseObject(pAppPools); + ReleaseObject(pCollection); + ReleaseObject(pCollection2); + ReleaseObject(pAppPoolElement); + ReleaseObject(pElement); + ReleaseObject(pElement2); + ReleaseObject(pElement3); + + return hr; +} + +static HRESULT SetDirPropAuthentications(IAppHostWritableAdminManager *pAdminMgr, + LPCWSTR wszConfigPath, + DWORD dwData) +{ + HRESULT hr = S_OK; + IAppHostElement *pSection = NULL; + + //dwData contains bit flags for /security/authentication/<...> + // Anonymous = 1 + // Basic = 2 + // Windows = 4 + // Digest =16 + // Passport =64 *not supported + //translation required from bit map to section name + // E.G security/authentication/windowsAuthentication [property enabled true|false] + + // AnonymousAuthentication = 1 + hr = pAdminMgr->GetAdminSection(ScopeBSTR(L"system.webServer/security/authentication/anonymousAuthentication"), ScopeBSTR(wszConfigPath), &pSection); + ExitOnFailure(hr, "Failed get AnonymousAuthentication section for DirProp"); + if (!pSection) + { + hr = HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND); + ExitOnFailure(hr, "Failed get AnonymousAuthentication section object for DirProps"); + } + + hr = Iis7PutPropertyBool(pSection, L"enabled", (BOOL)(dwData & 0x1)); + ExitOnFailure(hr, "Failed set AnonymousAuthentication enabled for DirProps"); + ReleaseNullObject(pSection); + + // basicAuthentication = 2 + hr = pAdminMgr->GetAdminSection(ScopeBSTR(L"system.webServer/security/authentication/basicAuthentication"), ScopeBSTR(wszConfigPath), &pSection); + ExitOnFailure(hr, "Failed get basicAuthentication section for DirProp"); + if (!pSection) + { + hr = HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND); + ExitOnFailure(hr, "Failed get basicAuthentication section object for DirProps"); + } + + hr = Iis7PutPropertyBool(pSection, L"enabled", (BOOL)(dwData & 0x2)); + ExitOnFailure(hr, "Failed set basicAuthentication enabled for DirProps"); + ReleaseNullObject(pSection); + + // WindowsAuthentication = 4 + hr = pAdminMgr->GetAdminSection(ScopeBSTR(L"system.webServer/security/authentication/windowsAuthentication"), ScopeBSTR(wszConfigPath), &pSection); + ExitOnFailure(hr, "Failed get windowsAuthentication section for DirProp"); + if (!pSection) + { + hr = HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND); + ExitOnFailure(hr, "Failed get windowsAuthentication section object for DirProps"); + } + + hr = Iis7PutPropertyBool(pSection, L"enabled", (BOOL)(dwData & 0x4)); + ExitOnFailure(hr, "Failed set windowsAuthentication enabled for DirProps"); + ReleaseNullObject(pSection); + + // digestAuthentication = 16 + hr = pAdminMgr->GetAdminSection(ScopeBSTR(L"system.webServer/security/authentication/digestAuthentication"), ScopeBSTR(wszConfigPath), &pSection); + ExitOnFailure(hr, "Failed get digestAuthentication section for DirProp"); + if (!pSection) + { + hr = HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND); + ExitOnFailure(hr, "Failed get digestAuthentication section object for DirProps"); + } + + hr = Iis7PutPropertyBool(pSection, L"enabled", (BOOL)(dwData & 0x10)); + ExitOnFailure(hr, "Failed set digestAuthentication enabled for DirProps"); + ReleaseNullObject(pSection); + +LExit: + ReleaseObject(pSection); + + return hr; +} + +static HRESULT SetDirPropAuthProvider(IAppHostWritableAdminManager *pAdminMgr, + LPCWSTR wszConfigPath, + __in LPWSTR wszData) +{ + HRESULT hr = S_OK; + IAppHostElement *pSection = NULL; + IAppHostElement *pElement = NULL; + IAppHostElement *pNewElement = NULL; + IAppHostElementCollection *pCollection = NULL; + + WCHAR wcDelim[] = L","; + const WCHAR *wszToken = NULL; + WCHAR *wszNextToken = NULL; + + hr = pAdminMgr->GetAdminSection(ScopeBSTR(L"system.webServer/security/authentication/windowsAuthentication"), ScopeBSTR(wszConfigPath), &pSection); + ExitOnFailure(hr, "Failed get windowsAuthentication section for DirProp providers"); + + hr = pSection->GetElementByName(ScopeBSTR(L"providers"), &pElement); + ExitOnFailure(hr, "Failed get win auth providers section"); + + hr = pElement->get_Collection(&pCollection); + ExitOnFailure(hr, "Failed get win auth providers collection"); + + hr = pCollection->Clear(); + ExitOnFailure(hr, "Failed to clear win auth providers collection"); + + //Clear out inherited items - add clear + hr = pCollection->CreateNewElement(ScopeBSTR(IIS_CONFIG_CLEAR), &pNewElement); + ExitOnFailure(hr, "Failed to create win auth providers clear element"); + hr = pCollection->AddElement(pNewElement); + ExitOnFailure(hr, "Failed to add win auth providers clear element"); + ReleaseNullObject(pNewElement); + + wszToken = wcstok_s( wszData, wcDelim, &wszNextToken); + for (int i = 0; (wszToken); ++i) + { + hr = pCollection->CreateNewElement(ScopeBSTR(IIS_CONFIG_ADD), &pNewElement); + ExitOnFailure(hr, "Failed to create win auth providers element"); + + hr = Iis7PutPropertyString( pNewElement, IIS_CONFIG_VALUE, wszToken); + ExitOnFailure(hr, "Failed to set win auth providers value"); + + hr = pCollection->AddElement(pNewElement, i); + ExitOnFailure(hr, "Failed to add win auth providers element"); + ReleaseNullObject(pNewElement); + + wszToken = wcstok_s( NULL, wcDelim, &wszNextToken); + } + +LExit: + ReleaseObject(pSection); + ReleaseObject(pCollection); + ReleaseObject(pElement); + ReleaseObject(pNewElement); + + return hr; +} + +static HRESULT SetDirPropDefDoc( + IAppHostWritableAdminManager *pAdminMgr, + LPCWSTR wszConfigPath, + __in LPWSTR wszData) +{ + HRESULT hr = S_OK; + IAppHostElement *pSection = NULL; + IAppHostElement *pElement = NULL; + IAppHostElement *pNewElement = NULL; + IAppHostElementCollection *pCollection = NULL; + + WCHAR wcDelim[] = L","; + const WCHAR *wszToken = NULL; + WCHAR *wszNextToken = NULL; + + hr = pAdminMgr->GetAdminSection(ScopeBSTR(IIS_CONFIG_DEFAULTDOC_SECTION), ScopeBSTR(wszConfigPath), &pSection); + ExitOnFailure(hr, "Failed get defaultDocument section for DirProp"); + + hr = pSection->GetElementByName(ScopeBSTR(L"files"), &pElement); + ExitOnFailure(hr, "Failed get win files section"); + + hr = pElement->get_Collection(&pCollection); + ExitOnFailure(hr, "Failed get files collection"); + + hr = pCollection->Clear(); + ExitOnFailure(hr, "Failed clear files collection"); + + //Clear out inherited items - add clear + hr = pCollection->CreateNewElement(ScopeBSTR(IIS_CONFIG_CLEAR), &pNewElement); + ExitOnFailure(hr, "Failed to create files clear element"); + hr = pCollection->AddElement(pNewElement); + ExitOnFailure(hr, "Failed to add files clear element"); + + wszToken = wcstok_s( wszData, wcDelim, &wszNextToken); + for (int i = 0; (wszToken); ++i) + { + hr = pCollection->CreateNewElement(ScopeBSTR(IIS_CONFIG_ADD), &pNewElement); + ExitOnFailure(hr, "Failed to create win auth providers element"); + + hr = Iis7PutPropertyString( pNewElement, IIS_CONFIG_VALUE, wszToken); + ExitOnFailure(hr, "Failed to set win auth providers value"); + + hr = pCollection->AddElement(pNewElement, i); + ExitOnFailure(hr, "Failed to add defaultDocument Files element"); + ReleaseNullObject(pNewElement); + + wszToken = wcstok_s( NULL, wcDelim, &wszNextToken); + } + +LExit: + ReleaseObject(pSection); + ReleaseObject(pCollection); + ReleaseObject(pNewElement); + + return hr; +} + +static HRESULT ClearLocationTag( + IAppHostWritableAdminManager *pAdminMgr, + LPCWSTR swLocationPath + ) +{ + HRESULT hr = S_OK; + IAppHostConfigManager *pConfigMgr = NULL; + IAppHostConfigFile *pConfigFile = NULL; + IAppHostConfigLocationCollection *pLocationCollection = NULL; + IAppHostConfigLocation *pLocation = NULL; + + DWORD dwCount = 0; + BSTR bstrLocationPath = NULL; + + hr = pAdminMgr->get_ConfigManager(&pConfigMgr); + ExitOnFailure(hr, "Failed to get IIS ConfigManager interface"); + + hr = pConfigMgr->GetConfigFile(ScopeBSTR(IIS_CONFIG_APPHOST_ROOT), &pConfigFile); + ExitOnFailure(hr, "Failed to get IIS ConfigFile interface"); + + hr = pConfigFile->get_Locations(&pLocationCollection); + ExitOnFailure(hr, "Failed to get IIS location tag collection"); + + hr = pLocationCollection->get_Count(&dwCount); + ExitOnFailure(hr, "Failed to get IIS location collection count"); + + VARIANT vtIndex; + vtIndex.vt = VT_UI4; + for (DWORD i = 0; i < dwCount; ++i) + { + vtIndex.ulVal = i; + hr = pLocationCollection->get_Item(vtIndex, &pLocation); + ExitOnFailure(hr, "Failed to get IIS location collection count"); + + hr = pLocation->get_Path(&bstrLocationPath); + if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, swLocationPath, -1, bstrLocationPath, -1)) + { + hr = pLocationCollection->DeleteLocation(vtIndex); + ExitOnFailure(hr, "Failed to delete IIS location tag %ls",swLocationPath); + break; + } + + ReleaseNullObject(pLocation); + ::SysFreeString(bstrLocationPath); + bstrLocationPath = NULL; + } +LExit: + ReleaseObject(pConfigMgr); + ReleaseObject(pConfigFile); + ReleaseObject(pLocationCollection); + ReleaseObject(pLocation); + ReleaseBSTR(bstrLocationPath); + + return hr; + +} + +static HRESULT DeleteCollectionElement( + __in IAppHostElementCollection *pCollection, + __in LPCWSTR pwzElementName, + __in LPCWSTR pwzAttributeName, + __in LPCWSTR pwzAttributeValue + ) +{ + HRESULT hr = S_OK; + + DWORD dwIndex; + VARIANT vtIndex; + VariantInit(&vtIndex); + + hr = Iis7FindAppHostElementString(pCollection, pwzElementName, pwzAttributeName, pwzAttributeValue, NULL, &dwIndex); + ExitOnFailure(hr, "Failed while finding IAppHostElement %ls/@%ls=%ls", pwzElementName, pwzAttributeName, pwzAttributeValue); + + if (MAXDWORD != dwIndex) + { + vtIndex.vt = VT_UI4; + vtIndex.ulVal = dwIndex; + hr = pCollection->DeleteElement(vtIndex); + ExitOnFailure(hr, "Failed to delete IAppHostElement %ls/@%ls=%ls", pwzElementName, pwzAttributeName, pwzAttributeValue); + } + // else : nothing to do, already deleted +LExit: + ReleaseVariant(vtIndex); + + return hr; +} +static void ConvSecToHMS( int Sec, __out_ecount(cchDest) LPWSTR wcTime, size_t cchDest) +{ + int ZH, ZM, ZS = 0; + + ZH = (Sec / 3600); + Sec = Sec - ZH * 3600; + ZM = (Sec / 60) ; + Sec = Sec - ZM * 60; + ZS = Sec ; + + HRESULT hr = ::StringCchPrintfW(wcTime, cchDest, L"%02d:%02d:%02d", ZH, ZM, ZS); + if (S_OK != hr) + { + *wcTime = '\0'; + } +} +static void ConvSecToDHMS( unsigned int Sec, __out_ecount(cchDest) LPWSTR wcTime, size_t cchDest) +{ + int ZD, ZH, ZM, ZS = 0; + + ZD = Sec / 86400; + Sec = Sec - ZD * 86400; + ZH = (Sec / 3600); + Sec = Sec - ZH * 3600; + ZM = (Sec / 60) ; + Sec = Sec - ZM * 60; + ZS = Sec ; + + HRESULT hr = ::StringCchPrintfW(wcTime, cchDest, L"%d.%02d:%02d:%02d", ZD, ZH, ZM, ZS); + if (S_OK != hr) + { + *wcTime = '\0'; + } +} diff --git a/src/ext/Iis/ca/scaexecIIS7.h b/src/ext/Iis/ca/scaexecIIS7.h new file mode 100644 index 00000000..ec31f202 --- /dev/null +++ b/src/ext/Iis/ca/scaexecIIS7.h @@ -0,0 +1,5 @@ +#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 IIS7ConfigChanges(MSIHANDLE hInstall, __inout LPWSTR pwzData); diff --git a/src/ext/Iis/ca/scafilter.cpp b/src/ext/Iis/ca/scafilter.cpp new file mode 100644 index 00000000..9d9014fd --- /dev/null +++ b/src/ext/Iis/ca/scafilter.cpp @@ -0,0 +1,510 @@ +// 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" + +// prototypes +static HRESULT ReadFilterLoadOrder( + __in IMSAdminBase* piMetabase, + __in LPCWSTR wzFilterRoot, + __out LPWSTR *ppwzLoadOrder + ); +static HRESULT AddFilterToLoadOrder( + __in LPCWSTR wzFilter, + __in int iLoadOrder, + __inout LPWSTR *ppwzLoadOrder + ); +static HRESULT RemoveFilterFromLoadOrder( + __in LPCWSTR wzFilter, + __inout LPWSTR *ppwzLoadOrder + ); + + +UINT __stdcall ScaFiltersRead( + __in IMSAdminBase* piMetabase, + __in SCA_WEB* pswList, + __in WCA_WRAPQUERY_HANDLE hWebBaseQuery, + __inout SCA_FILTER** ppsfList, + __inout LPWSTR *ppwzCustomActionData + ) +{ + HRESULT hr = S_OK; + MSIHANDLE hRec; + INSTALLSTATE isInstalled = INSTALLSTATE_UNKNOWN; + INSTALLSTATE isAction = INSTALLSTATE_UNKNOWN; + + LPWSTR pwzData = NULL; + + SCA_FILTER* psf = NULL; + WCA_WRAPQUERY_HANDLE hWrapQuery = NULL; + + hr = WcaBeginUnwrapQuery(&hWrapQuery, ppwzCustomActionData); + ExitOnFailure(hr, "Failed to unwrap query for ScaAppPoolRead"); + + if (0 == WcaGetQueryRecords(hWrapQuery)) + { + WcaLog(LOGMSG_VERBOSE, "Skipping ScaFiltersRead() - no IIsFilter table"); + ExitFunction1(hr = S_FALSE); + } + + // loop through all the filters + while (S_OK == (hr = WcaFetchWrappedRecord(hWrapQuery, &hRec))) + { + // Get the Component first. If the component is not being modified during + // this transaction, skip processing this whole record. + hr = WcaGetRecordString(hRec, fqComponent, &pwzData); + ExitOnFailure(hr, "failed to get IIsFilter.Component"); + + hr = WcaGetRecordInteger(hRec, fqInstalled, (int *)&isInstalled); + ExitOnFailure(hr, "Failed to get Component installed state for IIs filter"); + + hr = WcaGetRecordInteger(hRec, fqAction, (int *)&isAction); + ExitOnFailure(hr, "Failed to get Component action state for IIs filter"); + + if (!WcaIsInstalling(isInstalled, isAction) && + !WcaIsReInstalling(isInstalled, isAction) && + !WcaIsUninstalling(isInstalled, isAction)) + { + continue; // skip this record. + } + + hr = AddFilterToList(ppsfList); + ExitOnFailure(hr, "failed to add filter to list"); + + psf = *ppsfList; + + hr = ::StringCchCopyW(psf->wzComponent, countof(psf->wzComponent), pwzData); + ExitOnFailure(hr, "failed to copy component name: %ls", pwzData); + + psf->isInstalled = isInstalled; + psf->isAction = isAction; + + hr = WcaGetRecordString(hRec, fqWeb, &pwzData); + ExitOnFailure(hr, "Failed to get Web for VirtualDir"); + + if (*pwzData) + { + hr = ScaWebsGetBase(piMetabase, pswList, pwzData, psf->wzWebBase, countof(psf->wzWebBase), hWebBaseQuery); + if (FAILED(hr) && WcaIsUninstalling(isInstalled, isAction)) + { + // If we're uninstalling, don't bother finding the existing web, just leave the filter root empty + hr = S_OK; + } + ExitOnFailure(hr, "Failed to get base of web for Filter"); + + if (0 != lstrlenW(psf->wzWebBase)) + { + hr = ::StringCchPrintfW(psf->wzFilterRoot, countof(psf->wzFilterRoot), L"%s/Filters", psf->wzWebBase); + ExitOnFailure(hr, "Failed to allocate filter base string"); + } + } + else + { + hr = ::StringCchCopyW(psf->wzFilterRoot, countof(psf->wzFilterRoot), L"/LM/W3SVC/Filters"); + ExitOnFailure(hr, "Failed to allocate global filter base string"); + } + + // filter key + hr = WcaGetRecordString(hRec, fqFilter, &pwzData); + ExitOnFailure(hr, "Failed to get Filter.Filter"); + hr = ::StringCchCopyW(psf->wzKey, countof(psf->wzKey), pwzData); + ExitOnFailure(hr, "Failed to copy key string to filter object"); + + // filter path + hr = WcaGetRecordString(hRec, fqPath, &pwzData); + ExitOnFailure(hr, "Failed to get Filter.Path"); + hr = ::StringCchCopyW(psf->wzPath, countof(psf->wzPath), pwzData); + ExitOnFailure(hr, "Failed to copy path string to filter object"); + + // filter description + hr = WcaGetRecordString(hRec, fqDescription, &pwzData); + ExitOnFailure(hr, "Failed to get Filter.Description"); + hr = ::StringCchCopyW(psf->wzDescription, countof(psf->wzDescription), pwzData); + ExitOnFailure(hr, "Failed to copy description string to filter object"); + + // filter flags + hr = WcaGetRecordInteger(hRec, fqFlags, &psf->iFlags); + ExitOnFailure(hr, "Failed to get Filter.Flags"); + + // filter load order + hr = WcaGetRecordInteger(hRec, fqLoadOrder, &psf->iLoadOrder); + ExitOnFailure(hr, "Failed to get Filter.LoadOrder"); + } + + if (E_NOMOREITEMS == hr) + { + hr = S_OK; + } + ExitOnFailure(hr, "Failure while processing filters"); + +LExit: + WcaFinishUnwrapQuery(hWrapQuery); + + ReleaseStr(pwzData); + return hr; +} + + +HRESULT ScaFiltersInstall( + __in IMSAdminBase* piMetabase, + __in SCA_FILTER* psfList + ) +{ + HRESULT hr = S_OK; + SCA_FILTER* psf = psfList; + LPCWSTR wzPreviousFilterRoot = NULL; + LPWSTR pwzLoadOrder = NULL; + + while (psf) + { + if (WcaIsInstalling(psf->isInstalled, psf->isAction)) + { + if (!wzPreviousFilterRoot || CSTR_EQUAL != ::CompareStringW(LOCALE_INVARIANT, 0, wzPreviousFilterRoot, -1, psf->wzFilterRoot, -1)) + { + if (pwzLoadOrder) + { + hr = ScaWriteMetabaseValue(piMetabase, wzPreviousFilterRoot, L"", MD_FILTER_LOAD_ORDER, METADATA_NO_ATTRIBUTES, IIS_MD_UT_SERVER, STRING_METADATA, (LPVOID)pwzLoadOrder); + ExitOnFailure(hr, "Failed to write filter load order to metabase"); + + ReleaseNullStr(pwzLoadOrder); + } + + hr = ReadFilterLoadOrder(piMetabase, psf->wzFilterRoot, &pwzLoadOrder); + ExitOnFailure(hr, "Failed to read filter load order."); + + wzPreviousFilterRoot = psf->wzFilterRoot; + } + + hr = ScaCreateMetabaseKey(piMetabase, psf->wzFilterRoot, psf->wzKey); + ExitOnFailure(hr, "Failed to create key for filter '%ls'", psf->wzKey); + + hr = ScaWriteMetabaseValue(piMetabase, psf->wzFilterRoot, psf->wzKey, MD_KEY_TYPE, METADATA_NO_ATTRIBUTES, IIS_MD_UT_SERVER, STRING_METADATA, (LPVOID)L"IIsFilter"); + ExitOnFailure(hr, "Failed to write key type for filter '%ls'", psf->wzKey); + + // filter path + hr = ScaWriteMetabaseValue(piMetabase, psf->wzFilterRoot, psf->wzKey, MD_FILTER_IMAGE_PATH, METADATA_NO_ATTRIBUTES, IIS_MD_UT_SERVER, STRING_METADATA, (LPVOID)psf->wzPath); + ExitOnFailure(hr, "Failed to write Path for filter '%ls'", psf->wzKey); + + // filter description + hr = ScaWriteMetabaseValue(piMetabase, psf->wzFilterRoot, psf->wzKey, MD_FILTER_DESCRIPTION, METADATA_NO_ATTRIBUTES, IIS_MD_UT_SERVER, STRING_METADATA, (LPVOID)psf->wzDescription); + ExitOnFailure(hr, "Failed to write Description for filter '%ls'", psf->wzKey); + + // filter flags + if (MSI_NULL_INTEGER != psf->iFlags) + { + hr = ScaWriteMetabaseValue(piMetabase, psf->wzFilterRoot, psf->wzKey, MD_FILTER_FLAGS, METADATA_NO_ATTRIBUTES, IIS_MD_UT_SERVER, DWORD_METADATA, (LPVOID)((DWORD_PTR)psf->iFlags)); + ExitOnFailure(hr, "Failed to write Flags for filter '%ls'", psf->wzKey); + } + + // filter load order + if (MSI_NULL_INTEGER != psf->iLoadOrder) + { + hr = AddFilterToLoadOrder(psf->wzKey, psf->iLoadOrder, &pwzLoadOrder); + ExitOnFailure(hr, "Failed to add filter '%ls' to load order.", psf->wzKey); + } + } + + psf = psf->psfNext; + } + + if (pwzLoadOrder) + { + Assert(wzPreviousFilterRoot && *wzPreviousFilterRoot); + + hr = ScaWriteMetabaseValue(piMetabase, wzPreviousFilterRoot, L"", MD_FILTER_LOAD_ORDER, METADATA_NO_ATTRIBUTES, IIS_MD_UT_SERVER, STRING_METADATA, (LPVOID)pwzLoadOrder); + ExitOnFailure(hr, "Failed to write filter load order to metabase"); + } + +LExit: + ReleaseStr(pwzLoadOrder); + return hr; +} + + +HRESULT ScaFiltersUninstall( + __in IMSAdminBase* piMetabase, + __in SCA_FILTER* psfList + ) +{ + HRESULT hr = S_OK; + SCA_FILTER* psf = psfList; + LPCWSTR wzPreviousFilterRoot = NULL; + LPWSTR pwzLoadOrder = NULL; + + while (psf) + { + if (WcaIsUninstalling(psf->isInstalled, psf->isAction)) + { + if (!wzPreviousFilterRoot || CSTR_EQUAL != ::CompareStringW(LOCALE_INVARIANT, 0, wzPreviousFilterRoot, -1, psf->wzFilterRoot, -1)) + { + if (pwzLoadOrder) + { + hr = ScaWriteMetabaseValue(piMetabase, wzPreviousFilterRoot, L"", MD_FILTER_LOAD_ORDER, METADATA_NO_ATTRIBUTES, IIS_MD_UT_SERVER, STRING_METADATA, (LPVOID)pwzLoadOrder); + ExitOnFailure(hr, "Failed to write filter load order to metabase"); + + ReleaseNullStr(pwzLoadOrder); + } + + hr = ReadFilterLoadOrder(piMetabase, psf->wzFilterRoot, &pwzLoadOrder); + ExitOnFailure(hr, "Failed to read filter load order."); + + wzPreviousFilterRoot = psf->wzFilterRoot; + } + + hr = RemoveFilterFromLoadOrder(psf->wzKey, &pwzLoadOrder); + ExitOnFailure(hr, "Failed to remove filter '%ls' from load order", psf->wzKey); + + // remove the filter from the load order and remove the filter's key + if (0 != lstrlenW(psf->wzFilterRoot)) + { + hr = ScaDeleteMetabaseKey(piMetabase, psf->wzFilterRoot, psf->wzKey); + ExitOnFailure(hr, "Failed to remove web '%ls' from metabase", psf->wzKey); + } + } + + psf = psf->psfNext; + } + + if (pwzLoadOrder) + { + Assert(wzPreviousFilterRoot && *wzPreviousFilterRoot); + + hr = ScaWriteMetabaseValue(piMetabase, wzPreviousFilterRoot, L"", MD_FILTER_LOAD_ORDER, METADATA_NO_ATTRIBUTES, IIS_MD_UT_SERVER, STRING_METADATA, (LPVOID)pwzLoadOrder); + ExitOnFailure(hr, "Failed to write filter load order to metabase"); + } + +LExit: + return hr; +} + + +void ScaFiltersFreeList( + __in SCA_FILTER* psfList + ) +{ + SCA_FILTER* psfDelete = psfList; + while (psfList) + { + psfDelete = psfList; + psfList = psfList->psfNext; + + MemFree(psfDelete); + } +} + +HRESULT AddFilterToList( + __inout SCA_FILTER** ppsfList) +{ + HRESULT hr = S_OK; + SCA_FILTER* psf = static_cast(MemAlloc(sizeof(SCA_FILTER), TRUE)); + ExitOnNull(psf, hr, E_OUTOFMEMORY, "failed to add filter to filter list"); + + psf->psfNext = *ppsfList; + *ppsfList = psf; + +LExit: + return hr; +} + +// private helper functions + + +static HRESULT ReadFilterLoadOrder( + __in IMSAdminBase* piMetabase, + __in LPCWSTR wzFilterRoot, + __out LPWSTR *ppwzLoadOrder + ) +{ + HRESULT hr = S_OK; + METADATA_HANDLE mhRoot = NULL; + + METADATA_RECORD mr; + DWORD dwRequired = 0; + DWORD cchData = 255; + + ::ZeroMemory(&mr, sizeof(mr)); + mr.dwMDIdentifier = MD_FILTER_LOAD_ORDER; + mr.dwMDAttributes = METADATA_NO_ATTRIBUTES; + mr.dwMDUserType = IIS_MD_UT_SERVER; + mr.dwMDDataType = ALL_METADATA; + mr.dwMDDataLen = cchData; + mr.pbMDData = static_cast(MemAlloc(mr.dwMDDataLen * sizeof(WCHAR), TRUE)); + ExitOnNull(mr.pbMDData, hr, E_OUTOFMEMORY, "failed to allocate memory for MDData in metadata record"); + + hr = piMetabase->OpenKey(METADATA_MASTER_ROOT_HANDLE, wzFilterRoot, METADATA_PERMISSION_READ | METADATA_PERMISSION_WRITE, 10, &mhRoot); + for (int i = 30; i > 0 && HRESULT_FROM_WIN32(ERROR_PATH_BUSY) == hr; i--) + { + ::Sleep(1000); + WcaLog(LOGMSG_STANDARD, "Failed to open root key, retrying %d time(s)...", i); + hr = piMetabase->OpenKey(METADATA_MASTER_ROOT_HANDLE, wzFilterRoot, METADATA_PERMISSION_READ | METADATA_PERMISSION_WRITE, 10, &mhRoot); + } + + if (SUCCEEDED(hr)) + { + hr = piMetabase->GetData(mhRoot, L"", &mr, &dwRequired); + if (HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER) == hr) + { + mr.dwMDDataLen = cchData = dwRequired; + + LPVOID pv = MemReAlloc(mr.pbMDData, mr.dwMDDataLen * sizeof(WCHAR), TRUE); + ExitOnNull(pv, hr, E_OUTOFMEMORY, "failed to allocate memory for MDData in metadata record"); + mr.pbMDData = static_cast(pv); + + hr = piMetabase->GetData(mhRoot, L"", &mr, &dwRequired); + } + } + + // The /Filters node or /Filters/FilterLoadOrder property might not exist (yet). + if (HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND) == hr || MD_ERROR_DATA_NOT_FOUND == hr) + { + hr = S_OK; + } + ExitOnFailure(hr, "Failed to get filter load order."); + + hr = StrAllocString(ppwzLoadOrder, reinterpret_cast(mr.pbMDData), 0); + ExitOnFailure(hr, "Failed to allocate string for filter load order."); + +LExit: + ReleaseMem(mr.pbMDData); + + if (mhRoot) + { + piMetabase->CloseKey(mhRoot); + } + + return hr; +} + + +static HRESULT AddFilterToLoadOrder( + __in LPCWSTR wzFilter, + __in int iLoadOrder, + __inout LPWSTR *ppwzLoadOrder + ) +{ + HRESULT hr = S_OK; + LPCWSTR wzLoadOrder = *ppwzLoadOrder; + int cchFilter = lstrlenW(wzFilter); + LPWSTR pwzTemp = NULL; + + // If the filter name ends with '\0' or ',' and + // the filter name begins at the beginning of the list or with ',' + // Then we've found the exact filter by name. + // + // If the filter isn't already in the load order, add it + if (wzLoadOrder && *wzLoadOrder) + { + LPCWSTR pwz = wcsstr(wzLoadOrder, wzFilter); + + if (NULL != pwz && + (L'\0' == *(pwz + cchFilter) || L',' == *(pwz + cchFilter)) && + (pwz == wzLoadOrder || L',' == *(pwz - 1))) + { + // Filter already in the load order, no work to do. + } + else + { + pwz = NULL; + if (0 <= iLoadOrder) + { + pwz = wzLoadOrder; + for (int i = 0; i < iLoadOrder && pwz; ++i) + { + pwz = wcsstr(pwz, L","); + } + } + + if (NULL == pwz) // put the filter at the end of the order + { + Assert(wzLoadOrder && *wzLoadOrder); + + // tack on a comma since there are other filters in the order + hr = StrAllocConcat(ppwzLoadOrder, L",", 1); + ExitOnFailure(hr, "Failed to append a comma to filter load order."); + + hr = StrAllocConcat(ppwzLoadOrder, wzFilter, cchFilter); + ExitOnFailure(hr, "Failed to append the filter on to the load order."); + } + else if (L',' == *pwz) // put the filter in the middle of the order + { + hr = StrAllocString(&pwzTemp, wzLoadOrder, pwz - wzLoadOrder + 1); + ExitOnFailure(hr, "Failed to copy first half of filter load order to temp string."); + + hr = StrAllocConcat(&pwzTemp, wzFilter, 0); + ExitOnFailure(hr, "Failed to copy filter into load order."); + + hr = StrAllocConcat(&pwzTemp, pwz, 0); + ExitOnFailure(hr, "Failed to copy remaining filter load order back onto load order."); + + hr = StrAllocString(ppwzLoadOrder, pwzTemp, 0); + ExitOnFailure(hr, "Failed to copy temp string to load order string."); + } + else // put the filter at the beginning of the order + { + hr = StrAllocPrefix(ppwzLoadOrder, L",", 1); + ExitOnFailure(hr, "Failed to prepend a comma to filter load order."); + + hr = StrAllocPrefix(ppwzLoadOrder, wzFilter, cchFilter); + ExitOnFailure(hr, "Failed to prepend the filter on to the load order."); + } + } + } + else + { + hr = StrAllocString(ppwzLoadOrder, wzFilter, cchFilter); + ExitOnFailure(hr, "Failed to add filter to load order."); + } + +LExit: + ReleaseStr(pwzTemp); + return hr; +} + + +static HRESULT RemoveFilterFromLoadOrder( + __in LPCWSTR wzFilter, + __inout LPWSTR *ppwzLoadOrder + ) +{ + HRESULT hr = S_OK; + int cchFilter = lstrlenW(wzFilter); + + LPCWSTR pwzStart = NULL; + LPWSTR pwzFind = NULL; + + pwzStart = pwzFind = *ppwzLoadOrder; + while (NULL != (pwzFind = wcsstr(pwzFind, wzFilter))) + { + // Make sure to only match [wzFilter] and NOT foo[wzFilter]bar + if (pwzFind == pwzStart || L',' == *(pwzFind - 1)) + { + int cchRemainder = lstrlenW(pwzFind) - cchFilter + 1; // add one to include the null terminator + + if (L'\0' == *(pwzFind + cchFilter)) + { + if (pwzFind == pwzStart) + { + memmove(pwzFind, pwzFind + cchFilter, cchRemainder * sizeof(WCHAR)); // copy down the null terminator + } + else + { + memmove(pwzFind - 1, pwzFind + cchFilter, cchRemainder * sizeof(WCHAR)); // copy down the null terminator over top the trailing "," + } + } + else if (L',' == *(pwzFind + cchFilter)) + { + memmove(pwzFind, pwzFind + cchFilter + 1, (cchRemainder - 1) * sizeof(WCHAR)); // skip copying the "," + } + else // skip past the partial match + { + pwzFind = pwzFind + cchFilter; + } + } + else // skip past the partial match + { + pwzFind = pwzFind + cchFilter; + } + } + +//LExit: + return hr; +} diff --git a/src/ext/Iis/ca/scafilter.h b/src/ext/Iis/ca/scafilter.h new file mode 100644 index 00000000..d072bf22 --- /dev/null +++ b/src/ext/Iis/ca/scafilter.h @@ -0,0 +1,46 @@ +#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. + + +#include "scaweb.h" + +enum eFilterQuery { fqWeb = 1, fqFilter, fqComponent , fqPath, fqDescription, fqFlags, fqLoadOrder, fqInstalled, fqAction }; + +struct SCA_FILTER +{ + // darwin information + WCHAR wzKey[MAX_DARWIN_KEY + 1]; + WCHAR wzComponent[MAX_DARWIN_KEY + 1]; + INSTALLSTATE isInstalled; + INSTALLSTATE isAction; + + // metabase information + WCHAR wzWebKey[MAX_DARWIN_KEY + 1]; + WCHAR wzWebBase[METADATA_MAX_NAME_LEN + 1]; + WCHAR wzFilterRoot[METADATA_MAX_NAME_LEN + 1]; + + // iis configuation information + WCHAR wzPath[MAX_PATH]; + WCHAR wzDescription[MAX_DARWIN_COLUMN + 1]; + int iFlags; + int iLoadOrder; + + SCA_FILTER* psfNext; +}; + + +// prototypes +HRESULT AddFilterToList( + __in SCA_FILTER** ppsfList + ); + +UINT __stdcall ScaFiltersRead(IMSAdminBase* piMetabase, + SCA_WEB* pswList, __in WCA_WRAPQUERY_HANDLE hWebBaseQuery, SCA_FILTER** ppsfList, + __inout LPWSTR *ppwzCustomActionData); + +HRESULT ScaFiltersInstall(IMSAdminBase* piMetabase, SCA_FILTER* psfList); + +HRESULT ScaFiltersUninstall(IMSAdminBase* piMetabase, SCA_FILTER* psfList); + +void ScaFiltersFreeList(SCA_FILTER* psfList); + diff --git a/src/ext/Iis/ca/scafilter7.cpp b/src/ext/Iis/ca/scafilter7.cpp new file mode 100644 index 00000000..dda91c4d --- /dev/null +++ b/src/ext/Iis/ca/scafilter7.cpp @@ -0,0 +1,284 @@ +// 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" + +static HRESULT WriteFilter(const SCA_FILTER* psf); + +UINT __stdcall ScaFiltersRead7( + __in SCA_WEB7* pswList, + __in WCA_WRAPQUERY_HANDLE /*hWebBaseQuery*/, + __inout SCA_FILTER** ppsfList, + __inout LPWSTR *ppwzCustomActionData + ) +{ + HRESULT hr = S_OK; + MSIHANDLE hRec; + INSTALLSTATE isInstalled = INSTALLSTATE_UNKNOWN; + INSTALLSTATE isAction = INSTALLSTATE_UNKNOWN; + SCA_FILTER* psf; + + LPWSTR pwzData = NULL; + WCA_WRAPQUERY_HANDLE hWrapQuery = NULL; + hr = WcaBeginUnwrapQuery(&hWrapQuery, ppwzCustomActionData); + ExitOnFailure(hr, "Failed to unwrap query for ScaAppPoolRead"); + + if (0 == WcaGetQueryRecords(hWrapQuery)) + { + WcaLog(LOGMSG_VERBOSE, "Skipping ScaFiltersRead() - no IIsFilter table"); + ExitFunction1(hr = S_FALSE); + } + + // loop through all the filters + while (S_OK == (hr = WcaFetchWrappedRecord(hWrapQuery, &hRec))) + { + // Get the Component first. If the component is not being modified during + // this transaction, skip processing this whole record. + // get the darwin information + hr = WcaGetRecordString(hRec, fqComponent, &pwzData); + ExitOnFailure(hr, "failed to get IIsFilter.Component"); + + hr = WcaGetRecordInteger(hRec, fqInstalled, (int *)&isInstalled); + ExitOnFailure(hr, "Failed to get Component installed state for IIs filter"); + + hr = WcaGetRecordInteger(hRec, fqAction, (int *)&isAction); + ExitOnFailure(hr, "Failed to get Component action state for IIs filter"); + + if (!WcaIsInstalling(isInstalled, isAction) && + !WcaIsReInstalling(isInstalled, isAction) && + !WcaIsUninstalling(isInstalled, isAction)) + { + continue; // skip this record. + } + + hr = AddFilterToList(ppsfList); + ExitOnFailure(hr, "failed to add filter to list"); + + psf = *ppsfList; + + hr = ::StringCchCopyW(psf->wzComponent, countof(psf->wzComponent), pwzData); + ExitOnFailure(hr, "failed to copy component name: %ls", pwzData); + + psf->isInstalled = isInstalled; + psf->isAction = isAction; + + hr = WcaGetRecordString(hRec, fqWeb, &pwzData); + ExitOnFailure(hr, "Failed to get Web for VirtualDir"); + + if (*pwzData) + { + hr = ScaWebsGetBase7(pswList, pwzData, psf->wzFilterRoot, countof(psf->wzFilterRoot)); + if (FAILED(hr)) + { + WcaLog(LOGMSG_VERBOSE, "Could not find site for filter: %ls. Result 0x%x ", psf->wzFilterRoot, hr); + hr = S_OK; + } + } + else + { + hr = ::StringCchCopyW(psf->wzFilterRoot, countof(psf->wzFilterRoot), L"/"); + ExitOnFailure(hr, "Failed to allocate global filter base string"); + } + + // filter Name key + hr = WcaGetRecordString(hRec, fqFilter, &pwzData); + ExitOnFailure(hr, "Failed to get Filter.Filter"); + hr = ::StringCchCopyW(psf->wzKey, countof(psf->wzKey), pwzData); + ExitOnFailure(hr, "Failed to copy key string to filter object"); + + // filter path + hr = WcaGetRecordString(hRec, fqPath, &pwzData); + ExitOnFailure(hr, "Failed to get Filter.Path"); + hr = ::StringCchCopyW(psf->wzPath, countof(psf->wzPath), pwzData); + ExitOnFailure(hr, "Failed to copy path string to filter object"); + + // filter description -- not supported in iis 7 + hr = WcaGetRecordString(hRec, fqDescription, &pwzData); + ExitOnFailure(hr, "Failed to get Filter.Description"); + hr = ::StringCchCopyW(psf->wzDescription, countof(psf->wzDescription), pwzData); + ExitOnFailure(hr, "Failed to copy description string to filter object"); + + // filter flags + //What are these + hr = WcaGetRecordInteger(hRec, fqFlags, &psf->iFlags); + ExitOnFailure(hr, "Failed to get Filter.Flags"); + + // filter load order + hr = WcaGetRecordInteger(hRec, fqLoadOrder, &psf->iLoadOrder); + ExitOnFailure(hr, "Failed to get Filter.LoadOrder"); + } + + if (E_NOMOREITEMS == hr) + { + hr = S_OK; + } + ExitOnFailure(hr, "Failure while processing filters"); + +LExit: + WcaFinishUnwrapQuery(hWrapQuery); + + ReleaseStr(pwzData); + + return hr; +} + + +HRESULT ScaFiltersInstall7( + __in SCA_FILTER* psfList + ) +{ + HRESULT hr = S_OK; + SCA_FILTER* psf = psfList; + + if (!psf) + { + ExitFunction(); + } + //write global filters + hr = ScaWriteConfigID(IIS_FILTER_GLOBAL_BEGIN); + ExitOnFailure(hr, "Failed to write filter begin ID"); + while (psf) + { + if (WcaIsInstalling(psf->isInstalled, psf->isAction)) + { + if (0 == wcscmp(psf->wzFilterRoot, L"/")) + { + hr = WriteFilter(psf); + } + } + psf = psf->psfNext; + } + hr = ScaWriteConfigID(IIS_FILTER_END); + ExitOnFailure(hr, "Failed to write filter ID"); + + psf = psfList; + + //Write Web Site Filters + hr = ScaWriteConfigID(IIS_FILTER_BEGIN); + ExitOnFailure(hr, "Failed to write filter begin ID"); + while (psf) + { + if (WcaIsInstalling(psf->isInstalled, psf->isAction)) + { + if (0 != wcscmp(psf->wzFilterRoot, L"/")) + { + hr = WriteFilter(psf); + } + } + psf = psf->psfNext; + } + hr = ScaWriteConfigID(IIS_FILTER_END); + ExitOnFailure(hr, "Failed to write filter ID"); + +LExit: + + return hr; +} +static HRESULT WriteFilter(const SCA_FILTER* psf) +{ + HRESULT hr = S_OK; + + hr = ScaWriteConfigID(IIS_FILTER); + ExitOnFailure(hr, "Failed to write filter begin ID"); + + hr = ScaWriteConfigID(IIS_CREATE); + ExitOnFailure(hr, "Failed to write filter create ID"); + + //filter Name key + hr = ScaWriteConfigString(psf->wzKey); + ExitOnFailure(hr, "Failed to write key name for filter '%ls'", psf->wzKey); + + //web site name + hr = ScaWriteConfigString(psf->wzFilterRoot); + ExitOnFailure(hr, "Failed to write filter web root "); + + // filter path + hr = ScaWriteConfigString(psf->wzPath); + ExitOnFailure(hr, "Failed to write Path for filter '%ls'", psf->wzKey); + + //filter load order + hr = ScaWriteConfigInteger(psf->iLoadOrder); + ExitOnFailure(hr, "Failed to write load order for filter '%ls'", psf->wzKey); + +LExit: + return hr; +} + + +HRESULT ScaFiltersUninstall7( + __in SCA_FILTER* psfList + ) +{ + HRESULT hr = S_OK; + SCA_FILTER* psf = psfList; + + if (!psf) + { + ExitFunction1(hr = S_OK); + } + + //Uninstall global filters + hr = ScaWriteConfigID(IIS_FILTER_GLOBAL_BEGIN); + ExitOnFailure(hr, "Failed to write filter begin ID"); + + while (psf) + { + if (WcaIsUninstalling(psf->isInstalled, psf->isAction)) + { + if (0 == wcscmp(psf->wzFilterRoot, L"/")) + { + hr = ScaWriteConfigID(IIS_FILTER); + ExitOnFailure(hr, "Failed to write filter begin ID"); + + hr = ScaWriteConfigID(IIS_DELETE); + ExitOnFailure(hr, "Failed to write filter create ID"); + + //filter Name key + hr = ScaWriteConfigString(psf->wzKey); + ExitOnFailure(hr, "Failed to write key name for filter '%ls'", psf->wzKey); + + //web site name + hr = ScaWriteConfigString(psf->wzFilterRoot); + ExitOnFailure(hr, "Failed to write filter web root "); + + } + } + psf = psf->psfNext; + } + + hr = ScaWriteConfigID(IIS_FILTER_END); + ExitOnFailure(hr, "Failed to write filter ID"); + + psf = psfList; + + //Uninstall website filters + hr = ScaWriteConfigID(IIS_FILTER_BEGIN); + ExitOnFailure(hr, "Failed to write filter begin ID"); + while (psf) + { + if (WcaIsUninstalling(psf->isInstalled, psf->isAction)) + { + if (0 != wcscmp(psf->wzFilterRoot, L"/")) + { + hr = ScaWriteConfigID(IIS_FILTER); + ExitOnFailure(hr, "Failed to write filter begin ID"); + + hr = ScaWriteConfigID(IIS_DELETE); + ExitOnFailure(hr, "Failed to write filter create ID"); + + //filter Name key + hr = ScaWriteConfigString(psf->wzKey); + ExitOnFailure(hr, "Failed to write key name for filter '%ls'", psf->wzKey); + + //web site name + hr = ScaWriteConfigString(psf->wzFilterRoot); + ExitOnFailure(hr, "Failed to write filter web root "); + } + } + psf = psf->psfNext; + } + hr = ScaWriteConfigID(IIS_FILTER_END); + ExitOnFailure(hr, "Failed to write filter ID"); + +LExit: + return hr; +} diff --git a/src/ext/Iis/ca/scafilter7.h b/src/ext/Iis/ca/scafilter7.h new file mode 100644 index 00000000..50ff6652 --- /dev/null +++ b/src/ext/Iis/ca/scafilter7.h @@ -0,0 +1,21 @@ +#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. + + +#include "scaweb.h" + +// prototypes +UINT __stdcall ScaFiltersRead7( + __in SCA_WEB7* pswList, + __in WCA_WRAPQUERY_HANDLE hWebBaseQuery, + __inout SCA_FILTER** ppsfList, + __inout LPWSTR *ppwzCustomActionData + ); + +HRESULT ScaFiltersInstall7( + SCA_FILTER* psfList + ); + +HRESULT ScaFiltersUninstall7( + SCA_FILTER* psfList + ); diff --git a/src/ext/Iis/ca/scahttpheader.cpp b/src/ext/Iis/ca/scahttpheader.cpp new file mode 100644 index 00000000..1e134cea --- /dev/null +++ b/src/ext/Iis/ca/scahttpheader.cpp @@ -0,0 +1,323 @@ +// 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" + +enum eHttpHeaderQuery { hhqName = 1, hhqParentType, hhqParentValue, hhqValue, hhqAttributes}; + +static HRESULT AddHttpHeaderToList( + __in SCA_HTTP_HEADER** ppshhList + ); + + +void ScaHttpHeaderFreeList( + __in SCA_HTTP_HEADER* pshhList + ) +{ + SCA_HTTP_HEADER* pshhDelete = pshhList; + while (pshhList) + { + pshhDelete = pshhList; + pshhList = pshhList->pshhNext; + + MemFree(pshhDelete); + } +} + + +HRESULT ScaHttpHeaderRead( + __in SCA_HTTP_HEADER** ppshhList, + __inout LPWSTR *ppwzCustomActionData + ) +{ + Assert(ppshhList); + + HRESULT hr = S_OK; + MSIHANDLE hRec; + LPWSTR pwzData = NULL; + SCA_HTTP_HEADER* pshh = NULL; + WCA_WRAPQUERY_HANDLE hWrapQuery = NULL; + + hr = WcaBeginUnwrapQuery(&hWrapQuery, ppwzCustomActionData); + ExitOnFailure(hr, "Failed to unwrap query for ScaAppPoolRead"); + + if (0 == WcaGetQueryRecords(hWrapQuery)) + { + WcaLog(LOGMSG_VERBOSE, "Skipping ScaHttpHeaderRead() - required tables not present."); + ExitFunction1(hr = S_FALSE); + } + + // loop through all the HTTP headers + while (S_OK == (hr = WcaFetchWrappedRecord(hWrapQuery, &hRec))) + { + hr = AddHttpHeaderToList(ppshhList); + ExitOnFailure(hr, "failed to add http header to list"); + + pshh = *ppshhList; + + hr = WcaGetRecordInteger(hRec, hhqParentType, &(pshh->iParentType)); + ExitOnFailure(hr, "failed to get IIsHttpHeader.ParentType"); + + hr = WcaGetRecordString(hRec, hhqParentValue, &pwzData); + ExitOnFailure(hr, "Failed to get IIsHttpHeader.ParentValue"); + hr = ::StringCchCopyW(pshh->wzParentValue, countof(pshh->wzParentValue), pwzData); + ExitOnFailure(hr, "Failed to copy IIsHttpHeader.ParentValue"); + + hr = WcaGetRecordString(hRec, hhqName, &pwzData); + ExitOnFailure(hr, "Failed to get IIsHttpHeader.Name"); + hr = ::StringCchCopyW(pshh->wzName, countof(pshh->wzName), pwzData); + ExitOnFailure(hr, "Failed to copy IIsHttpHeader.Name"); + + hr = WcaGetRecordString(hRec, hhqValue, &pwzData); + ExitOnFailure(hr, "Failed to get IIsHttpHeader.Value"); + hr = ::StringCchCopyW(pshh->wzValue, countof(pshh->wzValue), pwzData); + ExitOnFailure(hr, "Failed to copy IIsHttpHeader.Value"); + + hr = WcaGetRecordInteger(hRec, hhqAttributes, &(pshh->iAttributes)); + ExitOnFailure(hr, "failed to get IIsHttpHeader.Attributes"); + } + + if (E_NOMOREITEMS == hr) + { + hr = S_OK; + } + ExitOnFailure(hr, "Failure while processing web errors"); + +LExit: + WcaFinishUnwrapQuery(hWrapQuery); + + ReleaseStr(pwzData); + + return hr; +} + + +HRESULT ScaGetHttpHeader( + __in int iParentType, + __in LPCWSTR wzParentValue, + __in SCA_HTTP_HEADER** ppshhList, + __out SCA_HTTP_HEADER** ppshhOut + ) +{ + HRESULT hr = S_OK; + SCA_HTTP_HEADER* pshhAdd = NULL; + SCA_HTTP_HEADER* pshhLast = NULL; + + *ppshhOut = NULL; + + if (!*ppshhList) + { + return hr; + } + + SCA_HTTP_HEADER* pshh = *ppshhList; + while (pshh) + { + if (iParentType == pshh->iParentType && CSTR_EQUAL == ::CompareStringW(LOCALE_SYSTEM_DEFAULT, 0, wzParentValue, -1, pshh->wzParentValue, -1)) + { + // Found a match, take this one out of the list and add it to the matched out list + pshhAdd = pshh; + + if (pshhLast) + { + // If we're not at the beginning of the list tell the last node about it's new next (since we're taking away it's current next) + pshhLast->pshhNext = pshhAdd->pshhNext; + } + else + { + // If we are at the beginning (no pshhLast) update the beginning (since we're taking it) + *ppshhList = pshh->pshhNext; + } + pshh = pshh->pshhNext; // move on + + // Add the one we've removed to the beginning of the out list + pshhAdd->pshhNext = *ppshhOut; + *ppshhOut = pshhAdd; + } + else + { + pshhLast = pshh; // remember the last we that didn't match + pshh = pshh->pshhNext; // move on + } + } + + return hr; +} + + +HRESULT ScaWriteHttpHeader( + __in IMSAdminBase* piMetabase, + __in LPCWSTR wzRoot, + __in SCA_HTTP_HEADER* pshhList + ) +{ + Assert(piMetabase && pshhList); + + HRESULT hr = S_OK; + METADATA_RECORD mr = { 0 }; + DWORD cchData = 0; + LPWSTR pwzSearchKey = NULL; + LPWSTR pwz = NULL; + LPWSTR pwzHeaders = NULL; + LPWSTR pwzNewHeader = NULL; + DWORD_PTR dwFoundHeaderIndex = 0; + LPCWSTR wzFoundHeader = NULL; + BOOL fOldValueFound = FALSE; + + ExitOnNull(wzRoot, hr, E_INVALIDARG, "Failed to write HTTP header, because no root was provided"); + + Assert(*wzRoot); + + // Check if HTTP header already exist here. + mr.dwMDIdentifier = MD_HTTP_CUSTOM; + mr.dwMDAttributes = METADATA_INHERIT; + mr.dwMDUserType = IIS_MD_UT_SERVER; + mr.dwMDDataType = ALL_METADATA; + mr.dwMDDataLen = cchData = 0; + mr.pbMDData = NULL; + + hr = MetaGetValue(piMetabase, METADATA_MASTER_ROOT_HANDLE, wzRoot, &mr); + if (HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND) == hr || MD_ERROR_DATA_NOT_FOUND == hr) + { + // + // If we don't have any HTTP Headers already, move up to get the parent headers. + // TODO: Make it configurable to not inherit HTTP Headers + // + hr = StrAllocConcat(&pwzSearchKey, wzRoot, 0); + ExitOnFailure(hr, "Failed to copy root string: %ls", wzRoot); + + pwz = pwzSearchKey + lstrlenW(pwzSearchKey); + while (NULL == pwzHeaders) + { + // find the last slash + while (*pwz != '/' && pwz != pwzSearchKey) + { + --pwz; + } + + if (pwz == pwzSearchKey) + { + break; + } + + *pwz = L'\0'; + + // Try here. If it's not found, keep walking up the path + hr = MetaGetValue(piMetabase, METADATA_MASTER_ROOT_HANDLE, pwzSearchKey, &mr); + if (HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND) == hr || MD_ERROR_DATA_NOT_FOUND == hr) + { + hr = S_FALSE; + } + ExitOnFailure(hr, "Failed to find search for HTTP headers for web root: %ls while walking up the tree", wzRoot); + + if (S_OK == hr) + { + hr = StrAllocString(&pwzHeaders, reinterpret_cast(mr.pbMDData), 0); + ExitOnFailure(hr, "Failed to allocate parent HTTP header string"); + break; + } + } + } + else + { + hr = StrAllocString(&pwzHeaders, reinterpret_cast(mr.pbMDData), 0); + ExitOnFailure(hr, "Failed to allocate HTTP header string"); + } + ExitOnFailure(hr, "Failed while searching for default HTTP headers to start with for web root: %ls", wzRoot); + + // Loop through the HTTP headers + for (SCA_HTTP_HEADER* pshh = pshhList; pshh; pshh = pshh->pshhNext) + { + fOldValueFound = FALSE; // assume a HTTP Header match will not be found + + hr = StrAllocFormatted(&pwzNewHeader, L"%s: ", pshh->wzName); + ExitOnFailure(hr, "Failed to allocate header name"); + + if (NULL != pwzHeaders && *pwzHeaders) + { + // Try to find a matching header already in the list + hr = MultiSzFindSubstring(pwzHeaders, pwzNewHeader, &dwFoundHeaderIndex, &wzFoundHeader); + ExitOnFailure(hr, "Failed while searching for existing HTTP header."); + + // If there was a substring HTTP header match, make sure the match was at the beginning + // of the string because that is the HTTP header name. + if (S_OK == hr) + { + DWORD cchMatch = lstrlenW(pwzNewHeader); + if (CSTR_EQUAL == ::CompareStringW(LOCALE_SYSTEM_DEFAULT, NORM_IGNORECASE, pwzNewHeader, cchMatch, wzFoundHeader, cchMatch)) + { + fOldValueFound = TRUE; + break; + } + } + } + + // Add the value on to the header name now. + hr = StrAllocConcat(&pwzNewHeader, pshh->wzValue, 0); + ExitOnFailure(hr, "Failed to add value on to HTTP header name."); + + // If we have something to replace, replace it, otherwise, put it at the beginning (order shouldn't matter) + if (fOldValueFound) + { + if (NULL == pwzHeaders) + { + ExitOnFailure(hr = E_INVALIDARG, "While attempting to replace old HTTP header with new HTTP header, it was discovered that the old HTTP header was NULL!"); + } + hr = MultiSzReplaceString(&pwzHeaders, dwFoundHeaderIndex, pwzNewHeader); + ExitOnFailure(hr, "Failed to replace old HTTP header with new HTTP header"); + } + else + { + hr = MultiSzPrepend(&pwzHeaders, NULL, pwzNewHeader); + ExitOnFailure(hr, "Failed to prepend new HTTP header"); + } + } + + // now write the HttpCustom to the metabase + hr = ScaWriteMetabaseValue(piMetabase, wzRoot, NULL, MD_HTTP_CUSTOM, METADATA_INHERIT, IIS_MD_UT_FILE, MULTISZ_METADATA, pwzHeaders); + ExitOnFailure(hr, "Failed to write HTTP Headers to metabase"); + +LExit: + MetaFreeValue(&mr); + + ReleaseStr(pwzNewHeader); + ReleaseStr(pwzHeaders); + ReleaseStr(pwzSearchKey); + + return hr; +} + + +HRESULT ScaHttpHeaderCheckList( + __in SCA_HTTP_HEADER* pshhList + ) +{ + if (!pshhList) + { + return S_OK; + } + + while (pshhList) + { + WcaLog(LOGMSG_STANDARD, "Http Header: %ls for parent: %ls not used!", pshhList->wzName, pshhList->wzParentValue); + pshhList = pshhList->pshhNext; + } + + return E_FAIL; +} + + +static HRESULT AddHttpHeaderToList( + __in SCA_HTTP_HEADER** ppshhList + ) +{ + HRESULT hr = S_OK; + + SCA_HTTP_HEADER* pshh = static_cast(MemAlloc(sizeof(SCA_HTTP_HEADER), TRUE)); + ExitOnNull(pshh, hr, E_OUTOFMEMORY, "failed to allocate memory for new http header list element"); + + pshh->pshhNext = *ppshhList; + *ppshhList = pshh; + +LExit: + return hr; +} diff --git a/src/ext/Iis/ca/scahttpheader.h b/src/ext/Iis/ca/scahttpheader.h new file mode 100644 index 00000000..a4c407a4 --- /dev/null +++ b/src/ext/Iis/ca/scahttpheader.h @@ -0,0 +1,40 @@ +#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 eHttpHeaderParentType { hhptVDir = 1, hhptWeb }; + +struct SCA_HTTP_HEADER +{ + int iParentType; + WCHAR wzParentValue[MAX_DARWIN_KEY + 1]; + + WCHAR wzName[MAX_PATH]; + WCHAR wzValue[MAX_PATH]; + int iAttributes; + + SCA_HTTP_HEADER* pshhNext; +}; + +// prototypes +HRESULT ScaHttpHeaderRead( + __in SCA_HTTP_HEADER **ppshhList, + __inout LPWSTR *ppwzCustomActionData + ); +void ScaHttpHeaderFreeList( + __in SCA_HTTP_HEADER *pshhList + ); +HRESULT ScaHttpHeaderCheckList( + __in SCA_HTTP_HEADER* pshhList + ); +HRESULT ScaGetHttpHeader( + __in int iParentType, + __in LPCWSTR wzParentValue, + __in SCA_HTTP_HEADER** ppshhList, + __out SCA_HTTP_HEADER** ppshhOut + ); +HRESULT ScaWriteHttpHeader( + __in IMSAdminBase* piMetabase, + LPCWSTR wzRoot, + SCA_HTTP_HEADER* pshhList + ); diff --git a/src/ext/Iis/ca/scahttpheader7.cpp b/src/ext/Iis/ca/scahttpheader7.cpp new file mode 100644 index 00000000..645dd8d4 --- /dev/null +++ b/src/ext/Iis/ca/scahttpheader7.cpp @@ -0,0 +1,130 @@ +// 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" + +HRESULT ScaGetHttpHeader7( + __in int iParentType, + __in_z LPCWSTR wzParentValue, + __in SCA_HTTP_HEADER** ppshhList, + __out SCA_HTTP_HEADER** ppshhOut + ) +{ + HRESULT hr = S_OK; + SCA_HTTP_HEADER* pshhAdd = NULL; + SCA_HTTP_HEADER* pshhLast = NULL; + + *ppshhOut = NULL; + + if (!*ppshhList) + { + return hr; + } + + SCA_HTTP_HEADER* pshh = *ppshhList; + while (pshh) + { + if (iParentType == pshh->iParentType && 0 == wcscmp(wzParentValue, pshh->wzParentValue)) + { + // Found a match, take this one out of the list and add it to the matched out list + pshhAdd = pshh; + + if (pshhLast) + { + // If we're not at the beginning of the list tell the last node about it's new next (since we're taking away it's current next) + pshhLast->pshhNext = pshhAdd->pshhNext; + } + else + { + // If we are at the beginning (no pshhLast) update the beginning (since we're taking it) + *ppshhList = pshh->pshhNext; + } + pshh = pshh->pshhNext; // move on + + // Add the one we've removed to the beginning of the out list + pshhAdd->pshhNext = *ppshhOut; + *ppshhOut = pshhAdd; + } + else + { + pshhLast = pshh; // remember the last we that didn't match + pshh = pshh->pshhNext; // move on + } + } + + return hr; +} + + +HRESULT ScaWriteHttpHeader7( + __in_z LPCWSTR wzWebName, + __in_z LPCWSTR wzRoot, + SCA_HTTP_HEADER* pshhList + ) +{ + + HRESULT hr = S_OK; + + hr = ScaWriteConfigID(IIS_HTTP_HEADER_BEGIN); + ExitOnFailure(hr, "Fail to write httpHeader begin ID"); + + hr = ScaWriteConfigString(wzWebName); + ExitOnFailure(hr, "Fail to write httpHeader Web Key"); + + hr = ScaWriteConfigString(wzRoot); + ExitOnFailure(hr, "Fail to write httpHeader Vdir key"); + + // Loop through the HTTP headers + for (SCA_HTTP_HEADER* pshh = pshhList; pshh; pshh = pshh->pshhNext) + { + hr = ScaWriteConfigID(IIS_HTTP_HEADER); + ExitOnFailure(hr, "Fail to write httpHeader ID"); + + hr = ScaWriteConfigString(pshh->wzName); + ExitOnFailure(hr, "Fail to write httpHeader name"); + + hr = ScaWriteConfigString(pshh->wzValue); + ExitOnFailure(hr, "Fail to write httpHeader value"); + } + + hr = ScaWriteConfigID(IIS_HTTP_HEADER_END); + ExitOnFailure(hr, "Fail to write httpHeader end ID"); + +LExit: + return hr; +} + + +HRESULT ScaHttpHeaderCheckList7( + __in SCA_HTTP_HEADER* pshhList + ) +{ + if (!pshhList) + { + return S_OK; + } + + while (pshhList) + { + WcaLog(LOGMSG_STANDARD, "Http Header: %ls for parent: %ls not used!", pshhList->wzName, pshhList->wzParentValue); + pshhList = pshhList->pshhNext; + } + + return E_FAIL; +} + + +//static HRESULT AddHttpHeaderToList( +// __in SCA_HTTP_HEADER** ppshhList +// ) +//{ +// HRESULT hr = S_OK; +// +// SCA_HTTP_HEADER* pshh = static_cast(MemAlloc(sizeof(SCA_HTTP_HEADER), TRUE)); +// ExitOnNull(pshh, hr, E_OUTOFMEMORY, "failed to allocate memory for new http header list element"); +// +// pshh->pshhNext = *ppshhList; +// *ppshhList = pshh; +// +//LExit: +// return hr; +//} diff --git a/src/ext/Iis/ca/scahttpheader7.h b/src/ext/Iis/ca/scahttpheader7.h new file mode 100644 index 00000000..7a873c16 --- /dev/null +++ b/src/ext/Iis/ca/scahttpheader7.h @@ -0,0 +1,15 @@ +#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 ScaGetHttpHeader7( + __in int iParentType, + __in_z LPCWSTR wzParentValue, + __in SCA_HTTP_HEADER** ppshhList, + __out SCA_HTTP_HEADER** ppshhOut + ); +HRESULT ScaWriteHttpHeader7( + __in_z LPCWSTR wzWebName, + __in_z LPCWSTR wzRoot, + SCA_HTTP_HEADER* pshhList + ); diff --git a/src/ext/Iis/ca/scaiis.cpp b/src/ext/Iis/ca/scaiis.cpp new file mode 100644 index 00000000..958051ed --- /dev/null +++ b/src/ext/Iis/ca/scaiis.cpp @@ -0,0 +1,481 @@ +// 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" + +// globals +LPWSTR vpwzCustomActionData = NULL; +DWORD vdwCustomActionCost = 0; + +HRESULT ScaMetabaseTransaction(__in_z LPCWSTR wzBackup) +{ + HRESULT hr = S_OK; + + // TODO: These functions have been reported to hang IIS (O11:51709). They may have been fixed in IIS6, but if not, need to be re-written the hard way + + hr = WcaDoDeferredAction(CUSTOM_ACTION_DECORATION(L"StartMetabaseTransaction"), wzBackup, COST_IIS_TRANSACTIONS); + ExitOnFailure(hr, "Failed to schedule StartMetabaseTransaction"); + + hr = WcaDoDeferredAction(CUSTOM_ACTION_DECORATION(L"RollbackMetabaseTransaction"), wzBackup, 0); // rollback cost is irrelevant + ExitOnFailure(hr, "Failed to schedule RollbackMetabaseTransaction"); + + hr = WcaDoDeferredAction(CUSTOM_ACTION_DECORATION(L"CommitMetabaseTransaction"), wzBackup, 0); // commit is free + ExitOnFailure(hr, "Failed to schedule StartMetabaseTransaction"); + +LExit: + return hr; +} + + +HRESULT ScaCreateWeb(IMSAdminBase* piMetabase, LPCWSTR /*wzWeb*/, LPCWSTR wzWebBase) +{ + Assert(piMetabase); + + HRESULT hr = S_OK; + UINT ui = 0; + + hr = ScaCreateMetabaseKey(piMetabase, wzWebBase, L""); + ExitOnFailure(hr, "Failed to create web"); + + hr = ScaWriteMetabaseValue(piMetabase, wzWebBase, L"", MD_KEY_TYPE, METADATA_NO_ATTRIBUTES, IIS_MD_UT_SERVER, STRING_METADATA, (LPVOID)L"IIsWebServer"); + ExitOnFailure(hr, "Failed to set key type for web"); + + hr = ScaCreateMetabaseKey(piMetabase, wzWebBase, L"Root"); + ExitOnFailure(hr, "Failed to create web root"); + + hr = ScaWriteMetabaseValue(piMetabase, wzWebBase, L"Root", MD_KEY_TYPE, METADATA_NO_ATTRIBUTES, IIS_MD_UT_SERVER, STRING_METADATA, (LPVOID)L"IIsWebVirtualDir"); + ExitOnFailure(hr, "Failed to set key type for web root"); + + ui = 0x4000003e; // 1073741886; // default directory browsing rights + hr = ScaWriteMetabaseValue(piMetabase, wzWebBase, L"Root", MD_DIRECTORY_BROWSING, METADATA_INHERIT, IIS_MD_UT_FILE, DWORD_METADATA, (LPVOID)((DWORD_PTR)ui)); + ExitOnFailure(hr, "Failed to set directory browsing for web"); + + hr = ScaCreateMetabaseKey(piMetabase, wzWebBase, L"Filters"); + ExitOnFailure(hr, "Failed to create web filters root"); + + hr = ScaWriteMetabaseValue(piMetabase, wzWebBase, L"Filters", MD_KEY_TYPE, METADATA_NO_ATTRIBUTES, IIS_MD_UT_SERVER, STRING_METADATA, (LPVOID)L"IIsFilters"); + ExitOnFailure(hr, "Failed to set key for web filters root"); + + hr = ScaWriteMetabaseValue(piMetabase, wzWebBase, L"Filters", MD_FILTER_LOAD_ORDER, METADATA_NO_ATTRIBUTES, IIS_MD_UT_SERVER, STRING_METADATA, (LPVOID)L""); + ExitOnFailure(hr, "Failed to set empty load order for web"); + +LExit: + return hr; +} + + +HRESULT ScaDeleteApp(IMSAdminBase* piMetabase, LPCWSTR wzWebRoot) +{ + Assert(piMetabase); + Unused(piMetabase); + + HRESULT hr = S_OK; + WCHAR wzKey[METADATA_MAX_NAME_LEN]; + + WCHAR* pwzCustomActionData = NULL; + + hr = WcaWriteIntegerToCaData(MBA_DELETEAPP, &pwzCustomActionData); + ExitOnFailure(hr, "Failed to add metabase delete app directive to CustomActionData"); + + hr = ::StringCchCopyW(wzKey, countof(wzKey), wzWebRoot); + ExitOnFailure(hr, "Failed to copy webroot string to key"); + hr = WcaWriteStringToCaData(wzKey, &pwzCustomActionData); + ExitOnFailure(hr, "Failed to add metabase key to CustomActionData"); + + hr = ScaAddToIisConfiguration(pwzCustomActionData, COST_IIS_DELETEAPP); + ExitOnFailure(hr, "Failed to add ScaDeleteApp action data: %ls cost: %d", pwzCustomActionData, COST_IIS_DELETEAPP); + +LExit: + ReleaseStr(pwzCustomActionData); + + return hr; +} + + +HRESULT ScaCreateApp(IMSAdminBase* piMetabase, LPCWSTR wzWebRoot, + DWORD dwIsolation) +{ + Assert(piMetabase); + Unused(piMetabase); + + HRESULT hr = S_OK; + WCHAR wzKey[METADATA_MAX_NAME_LEN]; + BOOL fInProc = FALSE; + + WCHAR* pwzCustomActionData = NULL; + + hr = WcaWriteIntegerToCaData(MBA_CREATEAPP, &pwzCustomActionData); + ExitOnFailure(hr, "Failed to add metabase create app directive to CustomActionData"); + + hr = ::StringCchCopyW(wzKey, countof(wzKey), wzWebRoot); + ExitOnFailure(hr, "Failed to copy webroot string to key"); + hr = WcaWriteStringToCaData(wzKey, &pwzCustomActionData); + ExitOnFailure(hr, "Failed to add metabase key to CustomActionData"); + + if (0 == dwIsolation) + fInProc = TRUE; + else + fInProc = FALSE; + + hr = WcaWriteIntegerToCaData(fInProc, &pwzCustomActionData); + ExitOnFailure(hr, "Failed to add isolation value to CustomActionData"); + + hr = ScaAddToIisConfiguration(pwzCustomActionData, COST_IIS_CREATEAPP); + ExitOnFailure(hr, "Failed to add ScaCreateApp action data: %ls cost: %d", pwzCustomActionData, COST_IIS_CREATEAPP); + +LExit: + ReleaseStr(pwzCustomActionData); + + return hr; +} + + +HRESULT ScaCreateMetabaseKey(IMSAdminBase* piMetabase, LPCWSTR wzRootKey, + LPCWSTR wzSubKey) +{ + Assert(piMetabase); + Unused(piMetabase); + + HRESULT hr = S_OK; + WCHAR wzKey[METADATA_MAX_NAME_LEN]; + WCHAR* pwzCustomActionData = NULL; + + hr = ::StringCchCopyW(wzKey, countof(wzKey), wzRootKey); + ExitOnFailure(hr, "Failed to copy root key string to key"); + if (L'/' != *(wzKey + lstrlenW(wzRootKey))) + { + hr = ::StringCchCatW(wzKey, countof(wzKey), L"/"); + ExitOnFailure(hr, "Failed to concatenate / to key string"); + } + if (wzSubKey && *wzSubKey) + { + if (L'/' == *wzSubKey) + { + hr = ::StringCchCatW(wzKey, countof(wzKey), wzSubKey + 1); + ExitOnFailure(hr, "Failed to concatenate subkey (minus slash) to key string"); + } + else + { + hr = ::StringCchCatW(wzKey, countof(wzKey), wzSubKey); + ExitOnFailure(hr, "Failed to concatenate subkey to key string"); + } + } + + hr = WcaWriteIntegerToCaData(MBA_CREATEKEY, &pwzCustomActionData); + ExitOnFailure(hr, "Failed to add metabase delete key directive to CustomActionData"); + + hr = WcaWriteStringToCaData(wzKey, &pwzCustomActionData); + ExitOnFailure(hr, "Failed to add metabase key to CustomActionData"); + + hr = ScaAddToIisConfiguration(pwzCustomActionData, COST_IIS_CREATEKEY); + ExitOnFailure(hr, "Failed to add ScaCreateMetabaseKey action data: %ls cost: %d", pwzCustomActionData, COST_IIS_CREATEKEY); + +LExit: + ReleaseStr(pwzCustomActionData); + + return hr; +} + + +HRESULT ScaDeleteMetabaseKey(IMSAdminBase* piMetabase, LPCWSTR wzRootKey, + LPCWSTR wzSubKey) +{ + Assert(piMetabase); + Unused(piMetabase); + + HRESULT hr = S_OK; + WCHAR wzKey[METADATA_MAX_NAME_LEN]; + WCHAR* pwzCustomActionData = NULL; + + hr = ::StringCchCopyW(wzKey, countof(wzKey), wzRootKey); + ExitOnFailure(hr, "Failed to copy root key string to key"); + if (L'/' != *(wzKey + lstrlenW(wzRootKey))) + { + hr = ::StringCchCatW(wzKey, countof(wzKey), L"/"); + ExitOnFailure(hr, "Failed to concatenate / to key string"); + } + if (*wzSubKey) + { + if (L'/' == *wzSubKey) + { + hr = ::StringCchCatW(wzKey, countof(wzKey), wzSubKey + 1); + ExitOnFailure(hr, "Failed to concatenate subkey (minus slash) to key string"); + } + else + { + hr = ::StringCchCatW(wzKey, countof(wzKey), wzSubKey); + ExitOnFailure(hr, "Failed to concatenate subkey to key string"); + } + } + + hr = WcaWriteIntegerToCaData(MBA_DELETEKEY, &pwzCustomActionData); + ExitOnFailure(hr, "Failed to add metabase delete key directive to CustomActionData"); + + hr = WcaWriteStringToCaData(wzKey, &pwzCustomActionData); + ExitOnFailure(hr, "Failed to add metabase key to CustomActionData"); + + hr = ScaAddToIisConfiguration(pwzCustomActionData, COST_IIS_DELETEKEY); + ExitOnFailure(hr, "Failed to add ScaDeleteMetabaseKey action data: %ls cost: %d", pwzCustomActionData, COST_IIS_DELETEKEY); + +LExit: + ReleaseStr(pwzCustomActionData); + + return hr; +} + + +HRESULT ScaDeleteMetabaseValue(IMSAdminBase* piMetabase, LPCWSTR wzRootKey, + LPCWSTR wzSubKey, DWORD dwIdentifier, + DWORD dwDataType) +{ + Assert(piMetabase); + Unused(piMetabase); + + HRESULT hr = S_OK; + WCHAR wzKey[METADATA_MAX_NAME_LEN]; + WCHAR* pwzCustomActionData = NULL; + + hr = ::StringCchCopyW(wzKey, countof(wzKey), wzRootKey); + ExitOnFailure(hr, "Failed to copy root key string to key"); + if (L'/' != *(wzKey + lstrlenW(wzRootKey))) + { + hr = ::StringCchCatW(wzKey, countof(wzKey), L"/"); + ExitOnFailure(hr, "Failed to concatenate / to key string"); + } + + if (wzSubKey && *wzSubKey) + { + if (L'/' == *wzSubKey) + { + hr = ::StringCchCatW(wzKey, countof(wzKey), wzSubKey + 1); + ExitOnFailure(hr, "Failed to concatenate subkey (minus slash) to key string"); + } + else + { + hr = ::StringCchCatW(wzKey, countof(wzKey), wzSubKey); + ExitOnFailure(hr, "Failed to concatenate subkey to key string"); + } + } + + hr = WcaWriteIntegerToCaData(MBA_DELETEVALUE, &pwzCustomActionData); + ExitOnFailure(hr, "Failed to add metabase write value directive to CustomActionData"); + + hr = WcaWriteStringToCaData(wzKey, &pwzCustomActionData); + ExitOnFailure(hr, "Failed to add metabase key to CustomActionData"); + + hr = WcaWriteIntegerToCaData(dwIdentifier, &pwzCustomActionData); + ExitOnFailure(hr, "Failed to add metabase identifier to CustomActionData"); + + hr = WcaWriteIntegerToCaData(dwDataType, &pwzCustomActionData); + ExitOnFailure(hr, "Failed to add metabase data type to CustomActionData"); + + hr = ScaAddToIisConfiguration(pwzCustomActionData, COST_IIS_DELETEVALUE); + ExitOnFailure(hr, "Failed to add ScaDeleteMetabaseValue action data: %ls, cost: %d", pwzCustomActionData, COST_IIS_DELETEVALUE); + +LExit: + ReleaseStr(pwzCustomActionData); + + return hr; +} + + +#pragma prefast(push) +#pragma prefast(disable:25120) // Disable "requires count parameter" warning - we do have a way to distinguish buffer sizes in all situations, + // it just depends on the dwDataType, and there's no way to annotate the situation in SAL +HRESULT ScaWriteMetabaseValue(IMSAdminBase* piMetabase, LPCWSTR wzRootKey, + LPCWSTR wzSubKey, DWORD dwIdentifier, + DWORD dwAttributes, DWORD dwUserType, + DWORD dwDataType, LPVOID pvData) +#pragma prefast(pop) +{ + Assert(piMetabase && (pvData || (DWORD_METADATA == dwDataType))); // pvData may be 0 if it is DWORD data + Unused(piMetabase); + + HRESULT hr = S_OK; + WCHAR wzKey[METADATA_MAX_NAME_LEN]; + WCHAR* pwzCustomActionData = NULL; + + if (BINARY_METADATA == dwDataType) + { + ExitOnNull(pvData, hr, E_INVALIDARG, "Failed to write binary metadata - no data available to write"); + } + + hr = ::StringCchCopyW(wzKey, countof(wzKey), wzRootKey); + ExitOnFailure(hr, "Failed to copy root key string to key"); + if (L'/' != *(wzKey + lstrlenW(wzRootKey))) + { + hr = ::StringCchCatW(wzKey, countof(wzKey), L"/"); + ExitOnFailure(hr, "Failed to concatenate / to key string"); + } + if (wzSubKey && *wzSubKey) + { + if (L'/' == *wzSubKey) + { + hr = ::StringCchCatW(wzKey, countof(wzKey), wzSubKey + 1); + ExitOnFailure(hr, "Failed to concatenate subkey (minus slash) to key string"); + } + else + { + hr = ::StringCchCatW(wzKey, countof(wzKey), wzSubKey); + ExitOnFailure(hr, "Failed to concatenate subkey to key string"); + } + } + + hr = WcaWriteIntegerToCaData(MBA_WRITEVALUE, &pwzCustomActionData); + ExitOnFailure(hr, "Failed to add metabase write value directive to CustomActionData"); + + hr = WcaWriteStringToCaData(wzKey, &pwzCustomActionData); + ExitOnFailure(hr, "Failed to add metabase key to CustomActionData"); + + hr = WcaWriteIntegerToCaData(dwIdentifier, &pwzCustomActionData); + ExitOnFailure(hr, "Failed to add metabase identifier to CustomActionData"); + + hr = WcaWriteIntegerToCaData(dwAttributes, &pwzCustomActionData); + ExitOnFailure(hr, "Failed to add metabase attributes to CustomActionData"); + + hr = WcaWriteIntegerToCaData(dwUserType, &pwzCustomActionData); + ExitOnFailure(hr, "Failed to add metabase user type to CustomActionData"); + + hr = WcaWriteIntegerToCaData(dwDataType, &pwzCustomActionData); + ExitOnFailure(hr, "Failed to add metabase data type to CustomActionData"); + + switch (dwDataType) + { + case DWORD_METADATA: + hr = WcaWriteIntegerToCaData((DWORD)((DWORD_PTR)pvData), &pwzCustomActionData); + break; + case STRING_METADATA: + hr = WcaWriteStringToCaData((LPCWSTR)pvData, &pwzCustomActionData); + break; + case MULTISZ_METADATA: + { + // change NULLs to unprintable character to create a 'safe' MULTISZ string + LPWSTR pwz = (LPWSTR)pvData; + for (;;) + { + if ('\0' == *pwz) + { + *pwz = MAGIC_MULTISZ_CHAR; + if ('\0' == *(pwz + 1)) // second null back to back means end of string + break; + } + + ++pwz; + } + + hr = WcaWriteStringToCaData((LPCWSTR)pvData, &pwzCustomActionData); + } + break; + case BINARY_METADATA: + hr = WcaWriteStreamToCaData(((BLOB*) pvData)->pBlobData, ((BLOB*) pvData)->cbSize, &pwzCustomActionData); + break; + default: + hr = E_UNEXPECTED; + } + ExitOnFailure(hr, "Failed to add metabase data to CustomActionData"); + + // TODO: maybe look the key up and make sure we're not just writing the same value that already there + + hr = ScaAddToIisConfiguration(pwzCustomActionData, COST_IIS_WRITEVALUE); + ExitOnFailure(hr, "Failed to add ScaWriteMetabaseValue action data: %ls, cost: %d", pwzCustomActionData, COST_IIS_WRITEVALUE); + +LExit: + ReleaseStr(pwzCustomActionData); + + return hr; +} + + +HRESULT ScaAddToIisConfiguration(LPCWSTR pwzData, DWORD dwCost) +{ + HRESULT hr = S_OK; + + hr = WcaWriteStringToCaData(pwzData, &vpwzCustomActionData); + ExitOnFailure(hr, "failed to add to metabase configuration data string: %ls", pwzData); + + vdwCustomActionCost += dwCost; + +LExit: + return hr; +} + + +HRESULT ScaWriteConfigurationScript(__in LPCWSTR pwzCaScriptKey) +{ + HRESULT hr = S_OK; + WCA_CASCRIPT_HANDLE hScript = NULL; + LPWSTR pwzHashString = NULL; + BYTE rgbActualHash[SHA1_HASH_LEN] = { }; + DWORD dwHashedBytes = SHA1_HASH_LEN; + + // Create CaScript for communication with WriteMetabaseChanges + hr = WcaCaScriptCreate(WCA_ACTION_INSTALL, WCA_CASCRIPT_SCHEDULED, FALSE, pwzCaScriptKey, FALSE, &hScript); + ExitOnFailure(hr, "Failed to write ca script for WriteMetabaseChanges script."); + + if (vpwzCustomActionData && *vpwzCustomActionData) + { + // Write the actual custom action data to the ca script + WcaCaScriptWriteString(hScript, vpwzCustomActionData); + + hr = CrypHashBuffer((BYTE*)vpwzCustomActionData, sizeof(vpwzCustomActionData) * sizeof(WCHAR), PROV_RSA_AES, CALG_SHA1, rgbActualHash, dwHashedBytes); + ExitOnFailure(hr, "Failed to calculate hash of CustomAction data."); + + hr = StrAlloc(&pwzHashString, ((dwHashedBytes * 2) + 1)); + ExitOnFailure(hr, "Failed to allocate string for script hash"); + + hr = StrHexEncode(rgbActualHash, dwHashedBytes, pwzHashString, ((dwHashedBytes * 2) + 1)); + ExitOnFailure(hr, "Failed to convert hash bytes to string."); + + WcaLog(LOGMSG_VERBOSE, "Custom action data hash: %ls", pwzHashString); + WcaLog(LOGMSG_TRACEONLY, "Custom action data being written to ca script: %ls", vpwzCustomActionData); + } + else + hr = S_FALSE; + +LExit: + // Release the string + ReleaseStr(vpwzCustomActionData); + ReleaseStr(pwzHashString); + + // Flush the ca script to disk as best we can + WcaCaScriptFlush(hScript); + + WcaCaScriptClose(hScript, WCA_CASCRIPT_CLOSE_PRESERVE); + + return hr; +} + + +HRESULT ScaLoadMetabase(IMSAdminBase** ppiMetabase) +{ + HRESULT hr = S_OK; + UINT er = ERROR_SUCCESS; + + // if IIS was uninstalled (thus no IID_IMSAdminBase) allow the + // user to still uninstall this package by clicking "Ignore" + do + { + hr = ::CoCreateInstance(CLSID_MSAdminBase, NULL, CLSCTX_ALL, IID_IMSAdminBase, (void**)ppiMetabase); + if (FAILED(hr)) + { + WcaLog(LOGMSG_STANDARD, "failed to get IID_IMSAdminBase Object"); + er = WcaErrorMessage(msierrIISCannotConnect, hr, INSTALLMESSAGE_ERROR | MB_ABORTRETRYIGNORE, 0); + switch (er) + { + case IDABORT: + ExitFunction(); // bail with the error result from the CoCreate to kick off a rollback + case IDRETRY: + hr = S_FALSE; // hit me, baby, one more time + break; + case IDIGNORE: + hr = S_OK; // pretend everything is okay and bail + break; + default: // For failure on uninstall continue + hr = S_OK; + break; + } + } + } while (S_FALSE == hr); + +LExit: + return hr; +} diff --git a/src/ext/Iis/ca/scaiis.h b/src/ext/Iis/ca/scaiis.h new file mode 100644 index 00000000..4c743edf --- /dev/null +++ b/src/ext/Iis/ca/scaiis.h @@ -0,0 +1,33 @@ +#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 ScaMetabaseTransaction(__in_z LPCWSTR wzBackup); + +HRESULT ScaCreateWeb(IMSAdminBase* piMetabase, LPCWSTR wzWeb, LPCWSTR wzWebBase); + +HRESULT ScaDeleteApp(IMSAdminBase* piMetabase, LPCWSTR wzWebRoot); + +HRESULT ScaCreateApp(IMSAdminBase* piMetabase, LPCWSTR wzWebRoot, + DWORD dwIsolation); + +HRESULT ScaCreateMetabaseKey(IMSAdminBase* piMetabase, LPCWSTR wzRootKey, + LPCWSTR wzSubKey); + +HRESULT ScaDeleteMetabaseKey(IMSAdminBase* piMetabase, LPCWSTR wzRootKey, + LPCWSTR wzSubKey); + +HRESULT ScaWriteMetabaseValue(IMSAdminBase* piMetabase, LPCWSTR wzRootKey, + LPCWSTR wzSubKey, DWORD dwIdentifier, + DWORD dwAttributes, DWORD dwUserType, + DWORD dwDataType, LPVOID pvData); + +HRESULT ScaDeleteMetabaseValue(IMSAdminBase* piMetabase, LPCWSTR wzRootKey, + LPCWSTR wzSubKey, DWORD dwIdentifier, + DWORD dwDataType); + +HRESULT ScaWriteConfigurationScript(LPCWSTR pwzCaScriptKey); + +HRESULT ScaAddToIisConfiguration(LPCWSTR pwzData, DWORD dwCost); + +HRESULT ScaLoadMetabase(IMSAdminBase** piMetabase); diff --git a/src/ext/Iis/ca/scaiis7.cpp b/src/ext/Iis/ca/scaiis7.cpp new file mode 100644 index 00000000..aaf307d7 --- /dev/null +++ b/src/ext/Iis/ca/scaiis7.cpp @@ -0,0 +1,74 @@ +// 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" + +#define COST_IIS_WRITEKEY 10 + +HRESULT ScaIIS7ConfigTransaction(LPCWSTR wzBackup) +{ + HRESULT hr = S_OK; + + hr = WcaDoDeferredAction(CUSTOM_ACTION_DECORATION(L"StartIIS7ConfigTransaction"), wzBackup, COST_IIS_TRANSACTIONS); + ExitOnFailure(hr, "Failed to schedule StartIIS7ConfigTransaction"); + + hr = WcaDoDeferredAction(CUSTOM_ACTION_DECORATION(L"RollbackIIS7ConfigTransaction"), wzBackup, 0); // rollback cost is irrelevant + ExitOnFailure(hr, "Failed to schedule RollbackIIS7ConfigTransaction"); + + hr = WcaDoDeferredAction(CUSTOM_ACTION_DECORATION(L"CommitIIS7ConfigTransaction"), wzBackup, 0); // commit is free + ExitOnFailure(hr, "Failed to schedule StartIIS7ConfigTransaction"); + +LExit: + return hr; +} + +HRESULT ScaWriteConfigString(const LPCWSTR wzValue) +{ + HRESULT hr = S_OK; + WCHAR* pwzCustomActionData = NULL; + + hr = WcaWriteStringToCaData(wzValue, &pwzCustomActionData); + ExitOnFailure(hr, "Failed to add metabase delete key directive to CustomActionData"); + + hr = ScaAddToIisConfiguration(pwzCustomActionData, COST_IIS_WRITEKEY); + ExitOnFailure(hr, "Failed to add ScaWriteMetabaseValue action data: %ls, cost: %d", pwzCustomActionData, COST_IIS_WRITEKEY); + +LExit: + ReleaseStr(pwzCustomActionData); + + return hr; +} + +HRESULT ScaWriteConfigInteger(DWORD dwValue) +{ + HRESULT hr = S_OK; + WCHAR* pwzCustomActionData = NULL; + + hr = WcaWriteIntegerToCaData(dwValue, &pwzCustomActionData); + ExitOnFailure(hr, "Failed to add metabase delete key directive to CustomActionData"); + + hr = ScaAddToIisConfiguration(pwzCustomActionData, COST_IIS_WRITEKEY); + ExitOnFailure(hr, "Failed to add ScaWriteMetabaseValue action data: %ls, cost: %d", pwzCustomActionData, COST_IIS_WRITEKEY); + +LExit: + ReleaseStr(pwzCustomActionData); + + return hr; +} + +HRESULT ScaWriteConfigID(IIS_CONFIG_ACTION emID) +{ + HRESULT hr = S_OK; + WCHAR* pwzCustomActionData = NULL; + + hr = WcaWriteIntegerToCaData(emID, &pwzCustomActionData); + ExitOnFailure(hr, "Failed to add metabase delete key directive to CustomActionData"); + + hr = ScaAddToIisConfiguration(pwzCustomActionData, COST_IIS_WRITEKEY); + ExitOnFailure(hr, "Failed to add ScaWriteMetabaseValue action data: %ls, cost: %d", pwzCustomActionData, COST_IIS_WRITEKEY); + +LExit: + ReleaseStr(pwzCustomActionData); + + return hr; +} + diff --git a/src/ext/Iis/ca/scaiis7.h b/src/ext/Iis/ca/scaiis7.h new file mode 100644 index 00000000..f398deca --- /dev/null +++ b/src/ext/Iis/ca/scaiis7.h @@ -0,0 +1,17 @@ +#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 ScaScheduleIIS7Configuration(); + +HRESULT ScaIIS7ConfigTransaction(__in_z LPCWSTR wzBackup); + +HRESULT ScaCreateApp7(__in_z LPCWSTR wzWebRoot, DWORD dwIsolation); + +HRESULT ScaDeleteConfigElement(IIS_CONFIG_ACTION emElement, LPCWSTR wzSubKey); + +HRESULT ScaWriteConfigString(__in_z const LPCWSTR wzValue); + +HRESULT ScaWriteConfigID(IIS_CONFIG_ACTION emID); + +HRESULT ScaWriteConfigInteger(DWORD dwValue); diff --git a/src/ext/Iis/ca/scamimemap.cpp b/src/ext/Iis/ca/scamimemap.cpp new file mode 100644 index 00000000..8afe99f9 --- /dev/null +++ b/src/ext/Iis/ca/scamimemap.cpp @@ -0,0 +1,200 @@ +// 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" + +enum eMimeMapQuery { mmqMimeMap = 1, mmqParentType, mmqParentValue, + mmqMimeType, mmqExtension}; + +// prototypes +static HRESULT AddMimeMapToList(SCA_MIMEMAP** ppsmmList); + + +void ScaMimeMapFreeList(SCA_MIMEMAP* psmmList) +{ + SCA_MIMEMAP* psmmDelete = psmmList; + while (psmmList) + { + psmmDelete = psmmList; + psmmList = psmmList->psmmNext; + + MemFree(psmmDelete); + } +} + + +HRESULT __stdcall ScaMimeMapRead( + SCA_MIMEMAP** ppsmmList, + __inout LPWSTR *ppwzCustomActionData + ) +{ + HRESULT hr = S_OK; + MSIHANDLE hRec; + LPWSTR pwzData = NULL; + SCA_MIMEMAP* psmm; + WCA_WRAPQUERY_HANDLE hWrapQuery = NULL; + + hr = WcaBeginUnwrapQuery(&hWrapQuery, ppwzCustomActionData); + ExitOnFailure(hr, "Failed to unwrap query for ScaMimeMapRead"); + + if (0 == WcaGetQueryRecords(hWrapQuery)) + { + WcaLog(LOGMSG_VERBOSE, "Skipping ScaMimeMapRead() - required table not present"); + ExitFunction1(hr = S_FALSE); + } + + // loop through all the mimemappings + while (S_OK == (hr = WcaFetchWrappedRecord(hWrapQuery, &hRec))) + { + hr = AddMimeMapToList(ppsmmList); + ExitOnFailure(hr, "failed to add mime map to list"); + + psmm = *ppsmmList; + + hr = WcaGetRecordString(hRec, mmqMimeMap, &pwzData); + ExitOnFailure(hr, "Failed to get MimeMap.MimeMap"); + hr = ::StringCchCopyW(psmm->wzMimeMap, countof(psmm->wzMimeMap), pwzData); + ExitOnFailure(hr, "Failed to copy mimemap string to mimemap object"); + + hr = WcaGetRecordInteger(hRec, mmqParentType, &psmm->iParentType); + ExitOnFailure(hr, "Failed to get MimeMap.iParentType"); + + hr = WcaGetRecordString(hRec, mmqParentValue, &pwzData); + ExitOnFailure(hr, "Failed to get MimeMap.ParentValue"); + hr = ::StringCchCopyW(psmm->wzParentValue, countof(psmm->wzParentValue), pwzData); + ExitOnFailure(hr, "Failed to copy parent value string to mimemap object"); + + hr = WcaGetRecordString(hRec, mmqExtension, &pwzData); + ExitOnFailure(hr, "Failed to get MimeMap.Extension"); + hr = ::StringCchCopyW(psmm->wzExtension, countof(psmm->wzExtension), pwzData); + ExitOnFailure(hr, "Failed to copy extension string to mimemap object"); + + hr = WcaGetRecordString(hRec, mmqMimeType, &pwzData); + ExitOnFailure(hr, "Failed to get MimeMap.MimeType"); + hr = ::StringCchCopyW(psmm->wzMimeType, countof(psmm->wzMimeType), pwzData); + ExitOnFailure(hr, "Failed to copy mimetype string to mimemap object"); + } + + if (E_NOMOREITEMS == hr) + hr = S_OK; + ExitOnFailure(hr, "Failure while processing mimemappings"); + +LExit: + WcaFinishUnwrapQuery(hWrapQuery); + + ReleaseStr(pwzData); + + return hr; +} + + +HRESULT ScaGetMimeMap(int iParentType, LPCWSTR wzParentValue, SCA_MIMEMAP **ppsmmList, SCA_MIMEMAP **ppsmmOut) +{ + HRESULT hr = S_OK; + SCA_MIMEMAP* psmmAdd = NULL; + SCA_MIMEMAP* psmmLast = NULL; + + *ppsmmOut = NULL; + + if (!*ppsmmList) + return hr; + + SCA_MIMEMAP* psmm = *ppsmmList; + while (psmm) + { + if (iParentType == psmm->iParentType && 0 == lstrcmpW(wzParentValue, psmm->wzParentValue)) + { + // Found a match, take this one out of the list and add it to the matched out list + psmmAdd = psmm; + + if (psmmLast) + { + // If we're not at the beginning of the list tell the last node about it's new next (since we're taking away it's current next) + psmmLast->psmmNext = psmmAdd->psmmNext; + } + else + { + // If we are at the beginning (no psmmLast) update the beginning (since we're taking it) + *ppsmmList = psmm->psmmNext; + } + psmm = psmm->psmmNext; // move on + + // Add the one we've removed to the beginning of the out list + psmmAdd->psmmNext = *ppsmmOut; + *ppsmmOut = psmmAdd; + } + else + { + psmmLast = psmm; // remember the last we that didn't match + psmm = psmm->psmmNext; // move on + } + } + + return hr; +} + +HRESULT ScaMimeMapCheckList(SCA_MIMEMAP* psmmList) +{ + if (!psmmList) + return S_OK; + + while (psmmList) + { + WcaLog(LOGMSG_STANDARD, "MimeMapping of %ls with ParentType=%d and ParentValue=%ls not used!", psmmList->wzMimeMap, psmmList->iParentType, psmmList->wzParentValue); + psmmList = psmmList->psmmNext; + } + + return E_FAIL; +} + + +HRESULT ScaWriteMimeMap(IMSAdminBase* piMetabase, LPCWSTR wzRootOfWeb, + SCA_MIMEMAP* psmmList) +{ + HRESULT hr = S_OK; + + WCHAR wzMimeMap[8192]; + WCHAR *pwzNext = wzMimeMap; + const WCHAR *pwzMac = wzMimeMap + countof(wzMimeMap); // used to properly create the MULTI_SZ + + // fill the MULTI_SZ wzMimeMap buffer for the MimeMap attribute + ::ZeroMemory(wzMimeMap, sizeof(wzMimeMap)); + + for (SCA_MIMEMAP* psmm = psmmList; psmm; psmm = psmm->psmmNext) + { + hr = ::StringCchPrintfW(pwzNext, max(0, pwzMac - pwzNext), L"%s,%s", psmm->wzExtension, psmm->wzMimeType); + ExitOnFailure(hr, "Failed to set MimeMap string"); + + pwzNext += lstrlenW(pwzNext) + 1; // reserve space for null + Assert(pwzNext <= pwzMac); + } + + if (pwzNext != wzMimeMap) + { + // now write the CustomErrors to the metabase + hr = ScaWriteMetabaseValue(piMetabase, wzRootOfWeb, NULL, MD_MIME_MAP, METADATA_INHERIT, IIS_MD_UT_FILE, MULTISZ_METADATA, wzMimeMap); + ExitOnFailure(hr, "Failed to write MimeMap"); + } + else + { + WcaLog(LOGMSG_VERBOSE, "Skipping ScaWriteMimeMap() - no mappings found."); + ExitFunction1(hr = S_FALSE); + } + +LExit: + return hr; +} + + +static HRESULT AddMimeMapToList(SCA_MIMEMAP** ppsmmList) +{ + HRESULT hr = S_OK; + + SCA_MIMEMAP* psmm = static_cast(MemAlloc(sizeof(SCA_MIMEMAP), TRUE)); + ExitOnNull(psmm, hr, E_OUTOFMEMORY, "failed to allocate memory for new mime map list element"); + + psmm->psmmNext = *ppsmmList; + *ppsmmList = psmm; + +LExit: + return hr; +} diff --git a/src/ext/Iis/ca/scamimemap.h b/src/ext/Iis/ca/scamimemap.h new file mode 100644 index 00000000..b50e82e9 --- /dev/null +++ b/src/ext/Iis/ca/scamimemap.h @@ -0,0 +1,33 @@ +#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 eMimeMapParentType { mmptVDir = 1, mmptWeb = 2 }; + +struct SCA_MIMEMAP +{ + // iis configuation information + WCHAR wzMimeMap[MAX_DARWIN_KEY + 1]; + int iParentType; + WCHAR wzParentValue[MAX_DARWIN_KEY + 1]; + WCHAR wzMimeType[MAX_DARWIN_KEY + 1]; + WCHAR wzExtension[MAX_DARWIN_KEY + 1]; + + + SCA_MIMEMAP* psmmNext; +}; + + +// prototypes + +HRESULT __stdcall ScaMimeMapRead(SCA_MIMEMAP** ppsmmList, __inout LPWSTR *ppwzCustomActionData); + +HRESULT ScaGetMimeMap(int iParentType, LPCWSTR wzParentValue, SCA_MIMEMAP **psmmList, SCA_MIMEMAP **ppsmmOut); + +HRESULT ScaMimeMapCheckList(SCA_MIMEMAP* psmmList); + +void ScaMimeMapFreeList(SCA_MIMEMAP* psmmList); + +HRESULT ScaWriteMimeMap(IMSAdminBase* piMetabase, LPCWSTR wzRootOfWeb, + SCA_MIMEMAP* psmmList); + diff --git a/src/ext/Iis/ca/scamimemap7.cpp b/src/ext/Iis/ca/scamimemap7.cpp new file mode 100644 index 00000000..f6689720 --- /dev/null +++ b/src/ext/Iis/ca/scamimemap7.cpp @@ -0,0 +1,68 @@ +// 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" + +// prototypes +HRESULT ScaWriteMimeMap7( + __in_z LPCWSTR wzWebName, + __in_z LPCWSTR wzRootOfWeb, + SCA_MIMEMAP* psmmList + ) +{ + HRESULT hr = S_OK; + SCA_MIMEMAP* psmm; + + //create the mimemap list for this vdir application + //all go to same web/root location tag + hr = ScaWriteConfigID(IIS_MIMEMAP_BEGIN); + ExitOnFailure(hr, "Failed to write mimemap begin id"); + hr = ScaWriteConfigString(wzWebName); //site name key + ExitOnFailure(hr, "Failed to write mimemap web key"); + hr = ScaWriteConfigString(wzRootOfWeb); //app path key + ExitOnFailure(hr, "Failed to write mimemap app key"); + + psmm = psmmList; + + while (psmm) + { + //create the Extension for this vdir application + hr = ScaWriteConfigID(IIS_MIMEMAP); + ExitOnFailure(hr, "Failed to write mimemap id"); + + if (*psmm->wzExtension) + { + hr = ScaWriteConfigString(psmm->wzExtension); + } + else // blank means "*" (all) + { + hr = ScaWriteConfigString(L"*"); + } + ExitOnFailure(hr, "Failed to write mimemap extension"); + + hr = ScaWriteConfigString(psmm->wzMimeType); + ExitOnFailure(hr, "Failed to write mimemap type"); + + psmm = psmm->psmmNext; + } + + hr = ScaWriteConfigID(IIS_MIMEMAP_END); + ExitOnFailure(hr, "Failed to write mimemap end id"); + +LExit: + return hr; +} + + +//static HRESULT AddMimeMapToList(SCA_MIMEMAP** ppsmmList) +//{ +// HRESULT hr = S_OK; +// +// SCA_MIMEMAP* psmm = static_cast(MemAlloc(sizeof(SCA_MIMEMAP), TRUE)); +// ExitOnNull(psmm, hr, E_OUTOFMEMORY, "failed to allocate memory for new mime map list element"); +// +// psmm->psmmNext = *ppsmmList; +// *ppsmmList = psmm; +// +//LExit: +// return hr; +//} diff --git a/src/ext/Iis/ca/scamimemap7.h b/src/ext/Iis/ca/scamimemap7.h new file mode 100644 index 00000000..88fcdc39 --- /dev/null +++ b/src/ext/Iis/ca/scamimemap7.h @@ -0,0 +1,10 @@ +#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 ScaWriteMimeMap7( + __in_z LPCWSTR wzWebName, + __in_z LPCWSTR wzRootOfWeb, + SCA_MIMEMAP* psmmList + ); + diff --git a/src/ext/Iis/ca/scaproperty.cpp b/src/ext/Iis/ca/scaproperty.cpp new file mode 100644 index 00000000..d0e0d8a4 --- /dev/null +++ b/src/ext/Iis/ca/scaproperty.cpp @@ -0,0 +1,252 @@ +// 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" + +/*------------------------------------------------------------------ +IIsProperty table: + +Property Component_ Attributes Value +s72 s72 i4 s255 +------------------------------------------------------------------*/ + +// sql queries +enum ePropertyQuery { pqProperty = 1, pqComponent, pqAttributes, pqValue, pqInstalled, pqAction }; + + +// prototypes +static HRESULT AddPropertyToList( + SCA_PROPERTY** ppspList + ); + + +// functions +void ScaPropertyFreeList( + SCA_PROPERTY* pspList + ) +{ + SCA_PROPERTY* pspDelete = pspList; + while (pspList) + { + pspDelete = pspList; + pspList = pspList->pspNext; + + MemFree(pspDelete); + } +} + + +HRESULT ScaPropertyRead( + SCA_PROPERTY** ppspList, + __inout LPWSTR *ppwzCustomActionData + ) +{ + HRESULT hr = S_OK; + MSIHANDLE hRec; + + LPWSTR pwzData = NULL; + SCA_PROPERTY* pss; + + WCA_WRAPQUERY_HANDLE hWrapQuery = NULL; + + ExitOnNull(ppspList, hr, E_INVALIDARG, "Failed to read property, because no property to read was provided"); + + hr = WcaBeginUnwrapQuery(&hWrapQuery, ppwzCustomActionData); + ExitOnFailure(hr, "Failed to unwrap query for ScaAppPoolRead"); + + if (0 == WcaGetQueryRecords(hWrapQuery)) + { + WcaLog(LOGMSG_VERBOSE, "Skipping ScaInstallProperty() - required table not present"); + ExitFunction1(hr = S_FALSE); + } + + // loop through all the Settings + while (S_OK == (hr = WcaFetchWrappedRecord(hWrapQuery, &hRec))) + { + hr = AddPropertyToList(ppspList); + ExitOnFailure(hr, "failed to add property to list"); + + pss = *ppspList; + + hr = WcaGetRecordString(hRec, pqProperty, &pwzData); + ExitOnFailure(hr, "failed to get IIsProperty.Property"); + hr = ::StringCchCopyW(pss->wzProperty, countof(pss->wzProperty), pwzData); + ExitOnFailure(hr, "failed to copy Property name: %ls", pwzData); + + hr = WcaGetRecordString(hRec, pqValue, &pwzData); + ExitOnFailure(hr, "failed to get IIsProperty.Value"); + hr = ::StringCchCopyW(pss->wzValue, countof(pss->wzValue), pwzData); + ExitOnFailure(hr, "failed to copy Property value: %ls", pwzData); + + hr = WcaGetRecordInteger(hRec, pqAttributes, &pss->iAttributes); + ExitOnFailure(hr, "failed to get IIsProperty.Attributes"); + + hr = WcaGetRecordString(hRec, pqComponent, &pwzData); + ExitOnFailure(hr, "failed to get IIsProperty.Component"); + hr = ::StringCchCopyW(pss->wzComponent, countof(pss->wzComponent), pwzData); + ExitOnFailure(hr, "failed to copy component name: %ls", pwzData); + + hr = WcaGetRecordInteger(hRec, pqInstalled, (int *)&pss->isInstalled); + ExitOnFailure(hr, "Failed to get Component installed state for filter"); + + hr = WcaGetRecordInteger(hRec, pqAction, (int *)&pss->isAction); + ExitOnFailure(hr, "Failed to get Component action state for filter"); + } + + if (E_NOMOREITEMS == hr) + { + hr = S_OK; + } + ExitOnFailure(hr, "failure while processing IIsProperty table"); + +LExit: + WcaFinishUnwrapQuery(hWrapQuery); + + ReleaseStr(pwzData); + + return hr; +} + + +HRESULT ScaPropertyInstall( + IMSAdminBase* piMetabase, + SCA_PROPERTY* pspList + ) +{ + Assert(piMetabase); + + HRESULT hr = S_OK; + + for (SCA_PROPERTY* psp = pspList; psp; psp = psp->pspNext) + { + // if we are installing the web site + if (WcaIsInstalling(psp->isInstalled, psp->isAction)) + { + hr = ScaWriteProperty(piMetabase, psp); + ExitOnFailure(hr, "failed to write Property '%ls' to metabase", psp->wzProperty); + } + } + +LExit: + return hr; +} + + +HRESULT ScaPropertyUninstall( + IMSAdminBase* piMetabase, + SCA_PROPERTY* pspList + ) +{ + Assert(piMetabase); + + HRESULT hr = S_OK; + + for (SCA_PROPERTY* psp = pspList; psp; psp = psp->pspNext) + { + // if we are uninstalling the web site + if (WcaIsUninstalling(psp->isInstalled, psp->isAction)) + { + hr = ScaRemoveProperty(piMetabase, psp); + ExitOnFailure(hr, "Failed to remove Property '%ls' from metabase", psp->wzProperty); + } + } + +LExit: + return hr; +} + + +HRESULT ScaWriteProperty( + IMSAdminBase* piMetabase, + SCA_PROPERTY* psp + ) +{ + Assert(piMetabase); + + HRESULT hr = S_OK; + DWORD dwValue; + LPWSTR wz = NULL; + + ExitOnNull(psp, hr, E_INVALIDARG, "Failed to write property because no property to write was given"); + + // + // Figure out what setting we're writing and write it + // + if (0 == lstrcmpW(psp->wzProperty, wzIISPROPERTY_IIS5_ISOLATION_MODE)) + { + dwValue = 1; + hr = ScaWriteMetabaseValue(piMetabase, L"/LM/W3SVC", NULL, MD_GLOBAL_STANDARD_APP_MODE_ENABLED, METADATA_NO_ATTRIBUTES, IIS_MD_UT_SERVER, DWORD_METADATA, (LPVOID)((DWORD_PTR)dwValue)); + ExitOnFailure(hr, "failed to set IIs5IsolationMode"); + } + else if (0 == lstrcmpW(psp->wzProperty, wzIISPROPERTY_MAX_GLOBAL_BANDWIDTH)) + { + dwValue = wcstoul(psp->wzValue, &wz, 10) * 1024; // remember, the value shown is in kilobytes, the value saved is in bytes + hr = ScaWriteMetabaseValue(piMetabase, L"/LM/W3SVC", NULL, MD_MAX_GLOBAL_BANDWIDTH, METADATA_NO_ATTRIBUTES, IIS_MD_UT_SERVER, DWORD_METADATA, (LPVOID)((DWORD_PTR)dwValue)); + ExitOnFailure(hr, "failed to set MaxGlobalBandwidth"); + } + else if (0 == lstrcmpW(psp->wzProperty, wzIISPROPERTY_LOG_IN_UTF8)) + { + dwValue = 1; + hr = ScaWriteMetabaseValue(piMetabase, L"/LM/W3SVC", NULL, MD_GLOBAL_LOG_IN_UTF_8, METADATA_NO_ATTRIBUTES, IIS_MD_UT_SERVER, DWORD_METADATA, (LPVOID)((DWORD_PTR)dwValue)); + ExitOnFailure(hr, "failed to set LogInUTF8"); + } + else if (0 == lstrcmpW(psp->wzProperty, wzIISPROPERTY_ETAG_CHANGENUMBER)) + { + dwValue = wcstoul(psp->wzValue, &wz, 10); + hr = ScaWriteMetabaseValue(piMetabase, L"/LM/W3SVC", NULL, /*MD_ETAG_CHANGENUMBER*/ 2039, METADATA_INHERIT, IIS_MD_UT_SERVER, DWORD_METADATA, (LPVOID)((DWORD_PTR)dwValue)); + ExitOnFailure(hr, "failed to set EtagChangenumber"); + } +LExit: + return hr; +} + + +HRESULT ScaRemoveProperty( + IMSAdminBase* piMetabase, + SCA_PROPERTY* psp + ) +{ + Assert(piMetabase); + + HRESULT hr = S_OK; + DWORD dwValue; + + ExitOnNull(psp, hr, E_INVALIDARG, "Failed to remove property because no property to remove was given"); + + if (0 == lstrcmpW(psp->wzProperty, wzIISPROPERTY_IIS5_ISOLATION_MODE)) + { + dwValue = 0; + hr = ScaWriteMetabaseValue(piMetabase, L"/LM/W3SVC", NULL, MD_GLOBAL_STANDARD_APP_MODE_ENABLED, METADATA_NO_ATTRIBUTES, IIS_MD_UT_SERVER, DWORD_METADATA, (LPVOID)((DWORD_PTR)dwValue)); + ExitOnFailure(hr, "failed to clear IIs5IsolationMode"); + } + else if (0 == lstrcmpW(psp->wzProperty, wzIISPROPERTY_MAX_GLOBAL_BANDWIDTH)) + { + dwValue = 0xFFFFFFFF; // This unchecks the box + hr = ScaWriteMetabaseValue(piMetabase, L"/LM/W3SVC", NULL, MD_MAX_GLOBAL_BANDWIDTH, METADATA_NO_ATTRIBUTES , IIS_MD_UT_SERVER, DWORD_METADATA, (LPVOID)((DWORD_PTR)dwValue)); + ExitOnFailure(hr, "failed to clear MaxGlobalBandwidth"); + } + else if (0 == lstrcmpW(psp->wzProperty, wzIISPROPERTY_LOG_IN_UTF8)) + { + dwValue = 0; + hr = ScaWriteMetabaseValue(piMetabase, L"/LM/W3SVC", NULL, MD_GLOBAL_LOG_IN_UTF_8, METADATA_NO_ATTRIBUTES, IIS_MD_UT_SERVER, DWORD_METADATA, (LPVOID)((DWORD_PTR)dwValue)); + ExitOnFailure(hr, "failed to clear LogInUTF8"); + } + +LExit: + return hr; +} + + +static HRESULT AddPropertyToList( + SCA_PROPERTY** ppspList + ) +{ + HRESULT hr = S_OK; + SCA_PROPERTY* psp = static_cast(MemAlloc(sizeof(SCA_PROPERTY), TRUE)); + ExitOnNull(psp, hr, E_OUTOFMEMORY, "failed to allocate memory for new property list element"); + + psp->pspNext = *ppspList; + *ppspList = psp; + +LExit: + return hr; +} diff --git a/src/ext/Iis/ca/scaproperty.h b/src/ext/Iis/ca/scaproperty.h new file mode 100644 index 00000000..21e7cfc7 --- /dev/null +++ b/src/ext/Iis/ca/scaproperty.h @@ -0,0 +1,54 @@ +#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 wzIISPROPERTY_IIS5_ISOLATION_MODE L"IIs5IsolationMode" +#define wzIISPROPERTY_MAX_GLOBAL_BANDWIDTH L"MaxGlobalBandwidth" +#define wzIISPROPERTY_LOG_IN_UTF8 L"LogInUTF8" +#define wzIISPROPERTY_ETAG_CHANGENUMBER L"ETagChangeNumber" + +struct SCA_PROPERTY +{ + // iis configuation information + WCHAR wzProperty[MAX_DARWIN_KEY + 1]; + WCHAR wzComponent[MAX_DARWIN_KEY + 1]; + INSTALLSTATE isInstalled; + INSTALLSTATE isAction; + INT iAttributes; + WCHAR wzValue[MAX_DARWIN_COLUMN + 1]; + + SCA_PROPERTY *pspNext; +}; + + +// prototypes + +HRESULT ScaPropertyRead( + SCA_PROPERTY** ppspList, + __inout LPWSTR *ppwzCustomActionData + ); + +void ScaPropertyFreeList( + SCA_PROPERTY* pspList + ); + +HRESULT ScaPropertyInstall( + IMSAdminBase* piMetabase, + SCA_PROPERTY* pspList + ); + +HRESULT ScaPropertyUninstall( + IMSAdminBase* piMetabase, + SCA_PROPERTY* pspList + ); + +HRESULT ScaWriteProperty( + IMSAdminBase* piMetabase, + SCA_PROPERTY* psp + ); + +HRESULT ScaRemoveProperty( + IMSAdminBase* piMetabase, + SCA_PROPERTY* psp + ); + diff --git a/src/ext/Iis/ca/scaproperty7.cpp b/src/ext/Iis/ca/scaproperty7.cpp new file mode 100644 index 00000000..d17eeb68 --- /dev/null +++ b/src/ext/Iis/ca/scaproperty7.cpp @@ -0,0 +1,108 @@ +// 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" + +HRESULT ScaPropertyInstall7( + SCA_PROPERTY* pspList + ) +{ + HRESULT hr = S_OK; + + for (SCA_PROPERTY* psp = pspList; psp; psp = psp->pspNext) + { + // if we are installing the web site + if (WcaIsInstalling(psp->isInstalled, psp->isAction)) + { + hr = ScaWriteProperty7(psp); + ExitOnFailure(hr, "failed to write Property '%ls' ", psp->wzProperty); + } + } + +LExit: + return hr; +} + + +HRESULT ScaPropertyUninstall7( + SCA_PROPERTY* pspList + ) +{ + HRESULT hr = S_OK; + + for (SCA_PROPERTY* psp = pspList; psp; psp = psp->pspNext) + { + // if we are uninstalling the web site + if (WcaIsUninstalling(psp->isInstalled, psp->isAction)) + { + hr = ScaRemoveProperty7(psp); + ExitOnFailure(hr, "Failed to remove Property '%ls'", psp->wzProperty); + } + } + +LExit: + return hr; +} + + +HRESULT ScaWriteProperty7( + const SCA_PROPERTY* psp + ) +{ + HRESULT hr = S_OK; + DWORD dwValue; + LPWSTR wz = NULL; + + ExitOnNull(psp, hr, E_INVALIDARG, "Failed to write property because no property to write was given"); + // + // Figure out what setting we're writing and write it + // + if (0 == wcscmp(psp->wzProperty, wzIISPROPERTY_IIS5_ISOLATION_MODE)) + { + // IIs5IsolationMode not supported + WcaLog(LOGMSG_VERBOSE, "Not supported by IIS7: IIs5IsolationMode, ignoring"); + } + else if (0 == wcscmp(psp->wzProperty, wzIISPROPERTY_MAX_GLOBAL_BANDWIDTH)) + { + dwValue = wcstoul(psp->wzValue, &wz, 10) * 1024; // remember, the value shown is in kilobytes, the value saved is in bytes + hr = ScaWriteConfigID(IIS_PROPERTY); + ExitOnFailure(hr, "failed to set Property ID"); + hr = ScaWriteConfigID(IIS_PROPERTY_MAXBAND); + ExitOnFailure(hr, "failed to set Property MSXBAND ID"); + hr = ScaWriteConfigInteger(dwValue); + ExitOnFailure(hr, "failed to set Property MSXBAND value"); + } + else if (0 == wcscmp(psp->wzProperty, wzIISPROPERTY_LOG_IN_UTF8)) + { + dwValue = 1; + hr = ScaWriteConfigID(IIS_PROPERTY); + ExitOnFailure(hr, "failed to set Property ID"); + hr = ScaWriteConfigID(IIS_PROPERTY_LOGUTF8); + ExitOnFailure(hr, "failed to set Property LOG ID"); + hr = ScaWriteConfigInteger(dwValue); + ExitOnFailure(hr, "failed to set Property Log value"); + } + else if (0 == wcscmp(psp->wzProperty, wzIISPROPERTY_ETAG_CHANGENUMBER)) + { + //EtagChangenumber not supported + WcaLog(LOGMSG_VERBOSE, "Not supported by IIS7: EtagChangenumber, ignoring"); + } + +LExit: + return hr; +} + +HRESULT ScaRemoveProperty7( + __in SCA_PROPERTY* /*psp*/ + ) +{ + + // NOP function for now + //The two global values being set by WebProperty: + // + // + // should should not be removed on uninstall. + + HRESULT hr = S_OK; + + return hr; +} diff --git a/src/ext/Iis/ca/scaproperty7.h b/src/ext/Iis/ca/scaproperty7.h new file mode 100644 index 00000000..bbf811e8 --- /dev/null +++ b/src/ext/Iis/ca/scaproperty7.h @@ -0,0 +1,26 @@ +#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 wzIISPROPERTY_IIS5_ISOLATION_MODE L"IIs5IsolationMode" +#define wzIISPROPERTY_MAX_GLOBAL_BANDWIDTH L"MaxGlobalBandwidth" +#define wzIISPROPERTY_LOG_IN_UTF8 L"LogInUTF8" +#define wzIISPROPERTY_ETAG_CHANGENUMBER L"ETagChangeNumber" + +// prototypes +HRESULT ScaPropertyInstall7( + SCA_PROPERTY* pspList + ); + +HRESULT ScaPropertyUninstall7( + SCA_PROPERTY* pspList + ); + +HRESULT ScaWriteProperty7( + const SCA_PROPERTY* psp + ); + +HRESULT ScaRemoveProperty7( + SCA_PROPERTY* psp + ); + diff --git a/src/ext/Iis/ca/scasched.cpp b/src/ext/Iis/ca/scasched.cpp new file mode 100644 index 00000000..de021275 --- /dev/null +++ b/src/ext/Iis/ca/scasched.cpp @@ -0,0 +1,823 @@ +// 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" + +const int ConfigureIIsCost = 8; +const int WriteMetabaseChangesCost = 20; +const int WriteIIS7ConfigChangesCost = 20; + +// sql queries +LPCWSTR vcsUserDeferredQuery = L"SELECT `User`, `Component_`, `Name`, `Domain`, `Password` FROM `User`"; + +LPCWSTR vcsWebSvcExtQuery = L"SELECT `Component_`, `File`, `Description`, `Group`, `Attributes` FROM `IIsWebServiceExtension`"; + +LPCWSTR vcsAppPoolQuery = L"SELECT `AppPool`, `Name`, `Component_`, `Attributes`, `User_`, `RecycleMinutes`, `RecycleRequests`, `RecycleTimes`, `VirtualMemory`, `PrivateMemory`, `IdleTimeout`, `QueueLimit`, `CPUMon`, `MaxProc`, `ManagedRuntimeVersion`, `ManagedPipelineMode` FROM `IIsAppPool`"; + +LPCWSTR vcsComponentAttrQuery = L"SELECT `Component`,`Attributes` FROM `Component`"; + +LPCWSTR vcsMimeMapQuery = L"SELECT `MimeMap`, `ParentType`, `ParentValue`, `MimeType`, `Extension` FROM `IIsMimeMap`"; + +LPCWSTR vcsHttpHeaderQuery = L"SELECT `Name`, `ParentType`, `ParentValue`, `Value`, `Attributes` FROM `IIsHttpHeader` ORDER BY `Sequence`"; + +LPCWSTR vcsWebErrorQuery = + L"SELECT `ErrorCode`, `SubCode`, `ParentType`, `ParentValue`, `File`, `URL` " + L"FROM `IIsWebError` ORDER BY `ErrorCode`, `SubCode`"; + +LPCWSTR vcsWebDirPropertiesQuery = L"SELECT `DirProperties`, `Access`, `Authorization`, `AnonymousUser_`, `IIsControlledPassword`, `LogVisits`, `Index`, `DefaultDoc`, `AspDetailedError`, `HttpExpires`, `CacheControlMaxAge`, `CacheControlCustom`, `NoCustomError`, `AccessSSLFlags`, `AuthenticationProviders` " + L"FROM `IIsWebDirProperties`"; + +LPCWSTR vcsSslCertificateQuery = L"SELECT `Certificate`.`StoreName`, `CertificateHash`.`Hash`, `IIsWebSiteCertificates`.`Web_` FROM `Certificate`, `CertificateHash`, `IIsWebSiteCertificates` WHERE `Certificate`.`Certificate`=`CertificateHash`.`Certificate_` AND `CertificateHash`.`Certificate_`=`IIsWebSiteCertificates`.`Certificate_`"; + +LPCWSTR vcsWebLogQuery = L"SELECT `Log`, `Format` " + L"FROM `IIsWebLog`"; + +LPCWSTR vcsWebApplicationQuery = L"SELECT `Name`, `Isolation`, `AllowSessions`, `SessionTimeout`, " + L"`Buffer`, `ParentPaths`, `DefaultScript`, `ScriptTimeout`, " + L"`ServerDebugging`, `ClientDebugging`, `AppPool_`, `Application` " + L"FROM `IIsWebApplication`"; + +LPCWSTR vcsWebAppExtensionQuery = L"SELECT `Extension`, `Verbs`, `Executable`, `Attributes`, `Application_` FROM `IIsWebApplicationExtension`"; + +LPCWSTR vcsWebQuery = L"SELECT `Web`, `Component_`, `Id`, `Description`, `ConnectionTimeout`, `Directory_`, `State`, `Attributes`, `DirProperties_`, `Application_`, " + L"`Address`, `IP`, `Port`, `Header`, `Secure`, `Log_` FROM `IIsWebSite`, `IIsWebAddress` " + L"WHERE `KeyAddress_`=`Address` ORDER BY `Sequence`"; + +LPCWSTR vcsWebAddressQuery = L"SELECT `Address`, `Web_`, `IP`, `Port`, `Header`, `Secure` " + L"FROM `IIsWebAddress`"; + +LPCWSTR vcsWebBaseQuery = L"SELECT `Web`, `Id`, `IP`, `Port`, `Header`, `Secure`, `Description` " + L"FROM `IIsWebSite`, `IIsWebAddress` " + L"WHERE `KeyAddress_`=`Address`"; + +LPCWSTR vcsWebDirQuery = L"SELECT `Web_`, `WebDir`, `Component_`, `Path`, `DirProperties_`, `Application_` " + L"FROM `IIsWebDir`"; + +LPCWSTR vcsVDirQuery = L"SELECT `Web_`, `VirtualDir`, `Component_`, `Alias`, `Directory_`, `DirProperties_`, `Application_` " + L"FROM `IIsWebVirtualDir`"; + +LPCWSTR vcsFilterQuery = L"SELECT `Web_`, `Name`, `Component_`, `Path`, `Description`, `Flags`, `LoadOrder` FROM `IIsFilter` ORDER BY `Web_`"; + +LPCWSTR vcsPropertyQuery = L"SELECT `Property`, `Component_`, `Attributes`, `Value` " + L"FROM `IIsProperty`"; + +#define IIS7CONDITION L"VersionNT >= 600" +#define USEIIS7CONDITION IIS7CONDITION L"AND NOT UseIis6Compatibility" + +/******************************************************************** +ConfigureIIs - CUSTOM ACTION ENTRY POINT for installing IIs settings + +********************************************************************/ +extern "C" UINT __stdcall ConfigureIIs( + __in MSIHANDLE hInstall + ) +{ + //AssertSz(FALSE, "debug ConfigureIIs here"); + HRESULT hr = S_OK; + UINT er = ERROR_SUCCESS; + LPWSTR pwzScriptKey = NULL; + LPWSTR pwzBackupId = NULL; + LPWSTR pwzCustomActionData = NULL; // CustomActionData for ConfigureIIs custom action + + // initialize + hr = WcaInitialize(hInstall, "ConfigureIIs"); + ExitOnFailure(hr, "Failed to initialize"); + + // check for the prerequsite tables + if (S_OK != WcaTableExists(L"IIsWebSite") && S_OK != WcaTableExists(L"IIsFilter") && S_OK != WcaTableExists(L"IIsProperty") && + S_OK != WcaTableExists(L"IIsWebServiceExtension") && S_OK != WcaTableExists(L"IIsAppPool")) + { + WcaLog(LOGMSG_VERBOSE, "skipping IIs CustomAction, no IIsWebSite table, no IIsFilter table, no IIsProperty table, no IIsWebServiceExtension, and no IIsAppPool table"); + ExitFunction1(hr = S_FALSE); + } + + // Get a CaScript key + hr = WcaCaScriptCreateKey(&pwzScriptKey); + ExitOnFailure(hr, "Failed to get encoding key."); + + // Generate a unique string to be used for this product's transaction + // This prevents a name collision when doing a major upgrade + hr = WcaGetProperty(L"ProductCode", &pwzBackupId); + ExitOnFailure(hr, "failed to get ProductCode"); + + hr = StrAllocConcat(&pwzBackupId, L"ScaConfigureIIs", 0); + ExitOnFailure(hr, "failed to concat ScaConfigureIIs"); + + // make sure the operations below are wrapped in a "transaction" + // use IIS7 transaction logic even if using Iis6 compat because Backup/Restore don't work with metabase compatibility + if (MSICONDITION_TRUE == ::MsiEvaluateConditionW(hInstall, IIS7CONDITION)) + { + hr = ScaIIS7ConfigTransaction(pwzBackupId); + MessageExitOnFailure(hr, msierrIISFailedSchedTransaction, "failed to start IIS7 transaction"); + } + else + { + hr = ScaMetabaseTransaction(pwzBackupId); + MessageExitOnFailure(hr, msierrIISFailedSchedTransaction, "failed to start IIS transaction"); + } + + // Write the CaScript key to the ConfigureIIS custom action data + hr = WcaWriteStringToCaData(pwzScriptKey, &pwzCustomActionData); + ExitOnFailure(hr, "Failed to add encoding key to CustomActionData."); + + // Wrap vcsUserDeferredQuery to send to deferred CA + if (S_OK == WcaTableExists(L"User")) + { + hr = WcaWrapQuery(vcsUserDeferredQuery, &pwzCustomActionData, efmcColumn3 | efmcColumn4 | efmcColumn5, 0xFFFFFFFF, 0xFFFFFFFF); + ExitOnFailure(hr, "Failed to wrap User query"); + } + else + { + hr = WcaWrapEmptyQuery(&pwzCustomActionData); + ExitOnFailure(hr, "Failed to wrap User empty query"); + } + + // Wrap vcsWebSvcExtQuery to send to deferred CA + if (S_OK == WcaTableExists(L"IIsWebServiceExtension")) + { + hr = WcaWrapQuery(vcsWebSvcExtQuery, &pwzCustomActionData, efmcColumn2 | efmcColumn3 | efmcColumn4, 1, 0xFFFFFFFF); + ExitOnFailure(hr, "Failed to wrap IIsWebServiceExtension query"); + } + else + { + hr = WcaWrapEmptyQuery(&pwzCustomActionData); + ExitOnFailure(hr, "Failed to wrap IIsWebServiceExtension empty query"); + } + + // Wrap vcsAppPoolQuery to send to deferred CA + if (S_OK == WcaTableExists(L"IIsAppPool")) + { + hr = WcaWrapQuery(vcsAppPoolQuery, &pwzCustomActionData, efmcColumn2 | efmcColumn15 | efmcColumn16, 3, 0xFFFFFFFF); + ExitOnFailure(hr, "Failed to wrap IIsAppPool query"); + + hr = WcaWrapQuery(vcsComponentAttrQuery, &pwzCustomActionData, 0, 0xFFFFFFFF, 0xFFFFFFFF); + ExitOnFailure(hr, "Failed to wrap Component query"); + } + else + { + hr = WcaWrapEmptyQuery(&pwzCustomActionData); + ExitOnFailure(hr, "Failed to wrap IIsAppPool empty query"); + } + + // Wrap vcsMimeMapQuery to send to deferred CA + if (S_OK == WcaTableExists(L"IIsMimeMap")) + { + hr = WcaWrapQuery(vcsMimeMapQuery, &pwzCustomActionData, efmcColumn4 | efmcColumn5, 0xFFFFFFFF, 0xFFFFFFFF); + ExitOnFailure(hr, "Failed to wrap IIsMimeMap query"); + } + else + { + hr = WcaWrapEmptyQuery(&pwzCustomActionData); + ExitOnFailure(hr, "Failed to wrap IIsMimeMap empty query"); + } + + // Wrap vcsHttpHeaderQuery to send to deferred CA + if (S_OK == WcaTableExists(L"IIsHttpHeader")) + { + hr = WcaWrapQuery(vcsHttpHeaderQuery, &pwzCustomActionData, efmcColumn1 | efmcColumn4, 0xFFFFFFFF, 0xFFFFFFFF); + ExitOnFailure(hr, "Failed to wrap IIsHttpHeader query"); + } + else + { + hr = WcaWrapEmptyQuery(&pwzCustomActionData); + ExitOnFailure(hr, "Failed to wrap IIsHttpHeader empty query"); + } + + // Wrap vcsWebErrorQuery to send to deferred CA + if (S_OK == WcaTableExists(L"IIsWebError")) + { + hr = WcaWrapQuery(vcsWebErrorQuery, &pwzCustomActionData, efmcColumn5 | efmcColumn6, 0xFFFFFFFF, 0xFFFFFFFF); + ExitOnFailure(hr, "Failed to wrap IIsWebError query"); + } + else + { + hr = WcaWrapEmptyQuery(&pwzCustomActionData); + ExitOnFailure(hr, "Failed to wrap IIsWebError empty query"); + } + + // Wrap vcsWebDirPropertiesQuery to send to deferred CA + if (S_OK == WcaTableExists(L"IIsWebDirProperties")) + { + hr = WcaWrapQuery(vcsWebDirPropertiesQuery, &pwzCustomActionData, efmcColumn8 | efmcColumn10 | efmcColumn12 | efmcColumn15, 0xFFFFFFFF, 0xFFFFFFFF); + ExitOnFailure(hr, "Failed to wrap IIsWebDirProperties query"); + } + else + { + hr = WcaWrapEmptyQuery(&pwzCustomActionData); + ExitOnFailure(hr, "Failed to wrap IIsWebDirProperties empty query"); + } + + // Wrap vcsSslCertificateQuery to send to deferred CA + if (S_OK == WcaTableExists(L"Certificate") && S_OK == WcaTableExists(L"CertificateHash") && S_OK == WcaTableExists(L"IIsWebSiteCertificates")) + { + hr = WcaWrapQuery(vcsSslCertificateQuery, &pwzCustomActionData, 0, 0xFFFFFFFF, 0xFFFFFFFF); + ExitOnFailure(hr, "Failed to wrap SslCertificate query"); + } + else + { + hr = WcaWrapEmptyQuery(&pwzCustomActionData); + ExitOnFailure(hr, "Failed to wrap SslCertificate empty query"); + } + + // Wrap vcsWebLogQuery to send to deferred CA + if (S_OK == WcaTableExists(L"IIsWebLog")) + { + hr = WcaWrapQuery(vcsWebLogQuery, &pwzCustomActionData, efmcColumn2, 0xFFFFFFFF, 0xFFFFFFFF); + ExitOnFailure(hr, "Failed to wrap IIsWebLog query"); + } + else + { + hr = WcaWrapEmptyQuery(&pwzCustomActionData); + ExitOnFailure(hr, "Failed to wrap IIsWebLog empty query"); + } + + // Wrap vcsWebApplicationQuery to send to deferred CA + if (S_OK == WcaTableExists(L"IIsWebApplication")) + { + hr = WcaWrapQuery(vcsWebApplicationQuery, &pwzCustomActionData, efmcColumn1, 0xFFFFFFFF, 0xFFFFFFFF); + ExitOnFailure(hr, "Failed to wrap IIsWebApplication query"); + } + else + { + hr = WcaWrapEmptyQuery(&pwzCustomActionData); + ExitOnFailure(hr, "Failed to wrap IIsWebApplication empty query"); + } + + // Wrap vcsWebAppExtensionQuery to send to deferred CA + if (S_OK == WcaTableExists(L"IIsWebApplicationExtension")) + { + hr = WcaWrapQuery(vcsWebAppExtensionQuery, &pwzCustomActionData, efmcColumn2 | efmcColumn3, 0xFFFFFFFF, 0xFFFFFFFF); + ExitOnFailure(hr, "Failed to wrap IIsWebApplicationExtension query"); + } + else + { + hr = WcaWrapEmptyQuery(&pwzCustomActionData); + ExitOnFailure(hr, "Failed to wrap IIsWebApplicationExtension empty query"); + } + + // Wrap vcsWebQuery, vcsWebAddressQuery, and vcsWebBaseQuery to send to deferred CA + if (S_OK == WcaTableExists(L"IIsWebAddress") && S_OK == WcaTableExists(L"IIsWebSite")) + { + hr = WcaWrapQuery(vcsWebQuery, &pwzCustomActionData, efmcColumn3 | efmcColumn4 | efmcColumn12 | efmcColumn13 | efmcColumn14, 2, 6); + ExitOnFailure(hr, "Failed to wrap IIsWebSite query"); + + hr = WcaWrapQuery(vcsWebAddressQuery, &pwzCustomActionData, efmcColumn3 | efmcColumn4 | efmcColumn5, 0xFFFFFFFF, 0xFFFFFFFF); + ExitOnFailure(hr, "Failed to wrap IIsWebAddress query"); + + hr = WcaWrapQuery(vcsWebBaseQuery, &pwzCustomActionData, efmcColumn2 | efmcColumn3 | efmcColumn4 | efmcColumn5 | efmcColumn7, 0xFFFFFFFF, 0xFFFFFFFF); + ExitOnFailure(hr, "Failed to wrap IIsWebBase query"); + } + else + { + hr = WcaWrapEmptyQuery(&pwzCustomActionData); + ExitOnFailure(hr, "Failed to wrap IIsWebSite empty query"); + + hr = WcaWrapEmptyQuery(&pwzCustomActionData); + ExitOnFailure(hr, "Failed to wrap IIsWebAddress empty query"); + + hr = WcaWrapEmptyQuery(&pwzCustomActionData); + ExitOnFailure(hr, "Failed to wrap IIsWebBase empty query"); + } + + // Wrap vcsWebDirQuery to send to deferred CA + if (S_OK == WcaTableExists(L"IIsWebDir")) + { + hr = WcaWrapQuery(vcsWebDirQuery, &pwzCustomActionData, efmcColumn4, 3, 0xFFFFFFFF); + ExitOnFailure(hr, "Failed to wrap IIsWebDir query"); + } + else + { + hr = WcaWrapEmptyQuery(&pwzCustomActionData); + ExitOnFailure(hr, "Failed to wrap IIsWebDir empty query"); + } + + // Wrap vcsVDirQuery to send to deferred CA + if (S_OK == WcaTableExists(L"IIsWebVirtualDir")) + { + hr = WcaWrapQuery(vcsVDirQuery, &pwzCustomActionData, efmcColumn4, 3, 5); + ExitOnFailure(hr, "Failed to wrap IIsWebVirtualDir query"); + } + else + { + hr = WcaWrapEmptyQuery(&pwzCustomActionData); + ExitOnFailure(hr, "Failed to wrap IIsWebVirtualDir empty query"); + } + + // Wrap vcsFilterQuery to send to deferred CA + if (S_OK == WcaTableExists(L"IIsFilter")) + { + hr = WcaWrapQuery(vcsFilterQuery, &pwzCustomActionData, efmcColumn4 | efmcColumn5, 3, 0xFFFFFFFF); + ExitOnFailure(hr, "Failed to wrap IIsFilter query"); + } + else + { + hr = WcaWrapEmptyQuery(&pwzCustomActionData); + ExitOnFailure(hr, "Failed to wrap IIsFilter empty query"); + } + + // Wrap vcsPropertyQuery to send to deferred CA + if (S_OK == WcaTableExists(L"IIsProperty")) + { + hr = WcaWrapQuery(vcsPropertyQuery, &pwzCustomActionData, efmcColumn4, 2, 0xFFFFFFFF); + ExitOnFailure(hr, "Failed to wrap IIsProperty query"); + } + else + { + hr = WcaWrapEmptyQuery(&pwzCustomActionData); + ExitOnFailure(hr, "Failed to wrap IIsProperty empty query"); + } + + if (MSICONDITION_TRUE == ::MsiEvaluateConditionW(hInstall, USEIIS7CONDITION)) + { + // This must remain trace only, CA data may contain password + WcaLog(LOGMSG_TRACEONLY, "Custom Action Data for ConfigureIIS7Exec will be: %ls", pwzCustomActionData); + + hr = WcaDoDeferredAction(CUSTOM_ACTION_DECORATION(L"ConfigureIIs7Exec"), pwzCustomActionData, ConfigureIIsCost); + ExitOnFailure(hr, "Failed to schedule ConfigureIIs7Exec custom action"); + + ReleaseNullStr(pwzCustomActionData); + + // Write the CaScript key to the ConfigureIIS custom action data + hr = WcaWriteStringToCaData(pwzScriptKey, &pwzCustomActionData); + ExitOnFailure(hr, "Failed to add script key to CustomActionData."); + + hr = WcaDoDeferredAction(CUSTOM_ACTION_DECORATION(L"WriteIIS7ConfigChanges"), pwzCustomActionData, WriteIIS7ConfigChangesCost); + ExitOnFailure(hr, "Failed to schedule WriteMetabaseChanges custom action"); + } + else + { + // This must remain trace only, CA data may contain password + WcaLog(LOGMSG_TRACEONLY, "Custom Action Data for ConfigureIISExec will be: %ls", pwzCustomActionData); + + hr = WcaDoDeferredAction(CUSTOM_ACTION_DECORATION(L"ConfigureIIsExec"), pwzCustomActionData, ConfigureIIsCost); + ExitOnFailure(hr, "Failed to schedule ConfigureIISExec custom action"); + + ReleaseNullStr(pwzCustomActionData); + + // Write the CaScript key to the ConfigureIIS custom action data + hr = WcaWriteStringToCaData(pwzScriptKey, &pwzCustomActionData); + ExitOnFailure(hr, "Failed to add script key to CustomActionData."); + + hr = WcaDoDeferredAction(CUSTOM_ACTION_DECORATION(L"WriteMetabaseChanges"), pwzCustomActionData, WriteMetabaseChangesCost); + ExitOnFailure(hr, "Failed to schedule WriteMetabaseChanges custom action"); + } + +LExit: + ReleaseStr(pwzScriptKey); + ReleaseStr(pwzBackupId); + ReleaseStr(pwzCustomActionData); + + er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE; + return WcaFinalize(er); +} + +/******************************************************************** +ConfigureIIsExec - custom action for installing IIs settings - table +data will be wrapped and passed in from immediate CA +ReadIIsTables + +********************************************************************/ +extern "C" UINT __stdcall ConfigureIIsExec( + __in MSIHANDLE hInstall + ) +{ + //AssertSz(FALSE, "debug ConfigureIIsExec here"); + HRESULT hr = S_OK; + UINT er = ERROR_SUCCESS; + + BOOL fInitializedCom = FALSE; + IMSAdminBase* piMetabase = NULL; + + SCA_WEB* pswList = NULL; + SCA_WEBDIR* pswdList = NULL; + SCA_VDIR* psvdList = NULL; + SCA_FILTER* psfList = NULL; + SCA_APPPOOL *psapList = NULL; + SCA_MIMEMAP* psmmList = NULL; + SCA_HTTP_HEADER* pshhList = NULL; + SCA_PROPERTY *pspList = NULL; + SCA_WEBSVCEXT* psWseList = NULL; + SCA_WEB_ERROR* psweList = NULL; + + LPWSTR pwzScriptKey = NULL; + LPWSTR pwzCustomActionData = NULL; + + WCA_WRAPQUERY_HANDLE hUserQuery = NULL; + WCA_WRAPQUERY_HANDLE hWebBaseQuery = NULL; + WCA_WRAPQUERY_HANDLE hWebDirPropQuery = NULL; + WCA_WRAPQUERY_HANDLE hSslCertQuery = NULL; + WCA_WRAPQUERY_HANDLE hWebLogQuery = NULL; + WCA_WRAPQUERY_HANDLE hWebAppQuery = NULL; + WCA_WRAPQUERY_HANDLE hWebAppExtQuery = NULL; + + // initialize + hr = WcaInitialize(hInstall, "ConfigureIIsExec"); + ExitOnFailure(hr, "Failed to initialize"); + + hr = WcaGetProperty(L"CustomActionData", &pwzCustomActionData); + ExitOnFailure(hr, "failed to get CustomActionData"); + + // Get the CaScript key + hr = WcaReadStringFromCaData(&pwzCustomActionData, &pwzScriptKey); + ExitOnFailure(hr, "Failed to get CaScript key from custom action data"); + + hr = ::CoInitialize(NULL); + ExitOnFailure(hr, "failed to initialize COM"); + fInitializedCom = TRUE; + + // if IIS was uninstalled (thus no IID_IMSAdminBase) allow the + // user to still uninstall this package by clicking "Ignore" + do + { + hr = ::CoCreateInstance(CLSID_MSAdminBase, NULL, CLSCTX_ALL, IID_IMSAdminBase, (void**)&piMetabase); + if (FAILED(hr)) + { + WcaLog(LOGMSG_STANDARD, "failed to get IID_IMSAdminBase Object"); + er = WcaErrorMessage(msierrIISCannotConnect, hr, INSTALLMESSAGE_ERROR | MB_ABORTRETRYIGNORE, 0); + switch (er) + { + case IDABORT: + ExitFunction(); // bail with the error result from the CoCreate to kick off a rollback + case IDRETRY: + hr = S_FALSE; // hit me, baby, one more time + break; + case IDIGNORE: + __fallthrough; + default: + WcaLog(LOGMSG_STANDARD, "ignoring absent IIS"); + // We need to write the empty script to communicate to other deferred CA that there is noting to do. + hr = ScaWriteConfigurationScript(pwzScriptKey); + ExitOnFailure(hr, "failed to schedule metabase configuration"); + + ExitFunction1(hr = S_OK); // pretend everything is okay + break; + } + } + } while (S_FALSE == hr); + + // read the msi tables + hr = WcaBeginUnwrapQuery(&hUserQuery, &pwzCustomActionData); + ExitOnFailure(hr, "Failed to unwrap user query"); + + hr = ScaWebSvcExtRead(&psWseList, &pwzCustomActionData); + MessageExitOnFailure(hr, msierrIISFailedReadWebSvcExt, "failed while processing WebServiceExtensions"); + + hr = ScaAppPoolRead(&psapList, hUserQuery, &pwzCustomActionData); + MessageExitOnFailure(hr, msierrIISFailedReadAppPool, "failed while processing WebAppPools"); + + // MimeMap, Error and HttpHeader need to be read before the virtual directory and web read + hr = ScaMimeMapRead(&psmmList, &pwzCustomActionData); + MessageExitOnFailure(hr, msierrIISFailedReadMimeMap, "failed while processing MimeMaps"); + + hr = ScaHttpHeaderRead(&pshhList, &pwzCustomActionData); + MessageExitOnFailure(hr, msierrIISFailedReadHttpHeader, "failed while processing HttpHeaders"); + + hr = ScaWebErrorRead(&psweList, &pwzCustomActionData); + MessageExitOnFailure(hr, msierrIISFailedReadWebError, "failed while processing WebErrors"); + + hr = WcaBeginUnwrapQuery(&hWebDirPropQuery, &pwzCustomActionData); + ExitOnFailure(hr, "Failed to unwrap web dir properties query"); + + hr = WcaBeginUnwrapQuery(&hSslCertQuery, &pwzCustomActionData); + ExitOnFailure(hr, "Failed to unwrap ssl certificate query"); + + hr = WcaBeginUnwrapQuery(&hWebLogQuery, &pwzCustomActionData); + ExitOnFailure(hr, "Failed to unwrap web log query"); + + hr = WcaBeginUnwrapQuery(&hWebAppQuery, &pwzCustomActionData); + ExitOnFailure(hr, "Failed to unwrap web application query"); + + hr = WcaBeginUnwrapQuery(&hWebAppExtQuery, &pwzCustomActionData); + ExitOnFailure(hr, "Failed to unwrap web application extension query"); + + hr = ScaWebsRead(piMetabase, &psmmList, &pswList, &pshhList, &psweList, hUserQuery, hWebDirPropQuery, hSslCertQuery, hWebLogQuery, hWebAppQuery, hWebAppExtQuery, &pwzCustomActionData); + MessageExitOnFailure(hr, msierrIISFailedReadWebSite, "failed while processing WebSites"); + + hr = WcaBeginUnwrapQuery(&hWebBaseQuery, &pwzCustomActionData); + ExitOnFailure(hr, "Failed to unwrap web base query"); + + hr = ScaWebDirsRead(piMetabase, pswList, hUserQuery, hWebBaseQuery, hWebDirPropQuery, hWebAppQuery, hWebAppExtQuery, &pwzCustomActionData, &pswdList); + MessageExitOnFailure(hr, msierrIISFailedReadWebDirs, "failed while processing WebDirs"); + + hr = ScaVirtualDirsRead(piMetabase, pswList, &psvdList, &psmmList, &pshhList, &psweList, hUserQuery, hWebBaseQuery, hWebDirPropQuery, hWebAppQuery, hWebAppExtQuery, &pwzCustomActionData); + MessageExitOnFailure(hr, msierrIISFailedReadVDirs, "failed while processing WebVirtualDirs"); + + hr = ScaFiltersRead(piMetabase, pswList, hWebBaseQuery, &psfList, &pwzCustomActionData); + MessageExitOnFailure(hr, msierrIISFailedReadFilters, "failed while processing WebFilters"); + + hr = ScaPropertyRead(&pspList, &pwzCustomActionData); + MessageExitOnFailure(hr, msierrIISFailedReadProp, "failed while processing WebProperties"); + + // do uninstall actions (order is important!) + hr = ScaPropertyUninstall(piMetabase, pspList); + MessageExitOnFailure(hr, msierrIISFailedSchedUninstallProp, "failed to uninstall IIS properties"); + + hr = ScaFiltersUninstall(piMetabase, psfList); + MessageExitOnFailure(hr, msierrIISFailedSchedUninstallFilters, "failed to schedule uninstall of filters"); + + hr = ScaVirtualDirsUninstall(piMetabase, psvdList); + MessageExitOnFailure(hr, msierrIISFailedSchedUninstallVDirs, "failed to schedule uninstall of virtual directories"); + + hr = ScaWebDirsUninstall(piMetabase, pswdList); + MessageExitOnFailure(hr, msierrIISFailedSchedUninstallWebDirs, "failed to schedule uninstall of web directories"); + + hr = ScaWebsUninstall(piMetabase, pswList); + MessageExitOnFailure(hr, msierrIISFailedSchedUninstallWebs, "failed to schedule uninstall of webs"); + + hr = ScaAppPoolUninstall(piMetabase, psapList); + MessageExitOnFailure(hr, msierrIISFailedSchedUninstallAppPool, "failed to schedule uninstall of AppPools"); + + + // do install actions (order is important!) + // ScaWebSvcExtCommit contains both uninstall and install actions. + hr = ScaWebSvcExtCommit(piMetabase, psWseList); + MessageExitOnFailure(hr, msierrIISFailedSchedInstallWebSvcExt, "failed to schedule install/uninstall of WebSvcExt"); + + hr = ScaAppPoolInstall(piMetabase, psapList); + MessageExitOnFailure(hr, msierrIISFailedSchedInstallAppPool, "failed to schedule install of AppPools"); + + hr = ScaWebsInstall(piMetabase, pswList, psapList); + MessageExitOnFailure(hr, msierrIISFailedSchedInstallWebs, "failed to schedule install of webs"); + + hr = ScaWebDirsInstall(piMetabase, pswdList, psapList); + MessageExitOnFailure(hr, msierrIISFailedSchedInstallWebDirs, "failed to schedule install of web directories"); + + hr = ScaVirtualDirsInstall(piMetabase, psvdList, psapList); + MessageExitOnFailure(hr, msierrIISFailedSchedInstallVDirs, "failed to schedule install of virtual directories"); + + hr = ScaFiltersInstall(piMetabase, psfList); + MessageExitOnFailure(hr, msierrIISFailedSchedInstallFilters, "failed to schedule install of filters"); + + hr = ScaPropertyInstall(piMetabase, pspList); + MessageExitOnFailure(hr, msierrIISFailedSchedInstallProp, "failed to schedule install of properties"); + + hr = ScaWriteConfigurationScript(pwzScriptKey); + ExitOnFailure(hr, "failed to schedule metabase configuration"); + +LExit: + ReleaseStr(pwzScriptKey); + ReleaseStr(pwzCustomActionData); + + WcaFinishUnwrapQuery(hUserQuery); + WcaFinishUnwrapQuery(hWebBaseQuery); + WcaFinishUnwrapQuery(hWebDirPropQuery); + WcaFinishUnwrapQuery(hSslCertQuery); + WcaFinishUnwrapQuery(hWebLogQuery); + WcaFinishUnwrapQuery(hWebAppQuery); + WcaFinishUnwrapQuery(hWebAppExtQuery); + + if (psWseList) + { + ScaWebSvcExtFreeList(psWseList); + } + + if (psfList) + { + ScaFiltersFreeList(psfList); + } + + if (psvdList) + { + ScaVirtualDirsFreeList(psvdList); + } + + if (pswdList) + { + ScaWebDirsFreeList(pswdList); + } + + if (pswList) + { + ScaWebsFreeList(pswList); + } + + if (psmmList) + { + ScaMimeMapCheckList(psmmList); + ScaMimeMapFreeList(psmmList); + } + + if (pshhList) + { + ScaHttpHeaderCheckList(pshhList); + ScaHttpHeaderFreeList(pshhList); + } + + if (psweList) + { + ScaWebErrorCheckList(psweList); + ScaWebErrorFreeList(psweList); + } + + ReleaseObject(piMetabase); + + if (fInitializedCom) + { + ::CoUninitialize(); + } + + er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE; + return WcaFinalize(er); +} + + +/******************************************************************** +ConfigureIIs - CUSTOM ACTION ENTRY POINT for installing IIs settings + +********************************************************************/ +extern "C" UINT __stdcall ConfigureIIs7Exec( + __in MSIHANDLE hInstall + ) +{ + //AssertSz(FALSE, "debug ConfigureIIs7Exec here"); + HRESULT hr = S_OK; + UINT er = ERROR_SUCCESS; + + LPWSTR pwzScriptKey = NULL; + LPWSTR pwzCustomActionData = NULL; + + SCA_WEB7* pswList = NULL; + SCA_WEBDIR7* pswdList = NULL; + SCA_VDIR7* psvdList = NULL; + SCA_FILTER* psfList = NULL; + SCA_APPPOOL *psapList = NULL; + SCA_MIMEMAP* psmmList = NULL; + SCA_HTTP_HEADER* pshhList = NULL; + SCA_PROPERTY *pspList = NULL; + SCA_WEBSVCEXT* psWseList = NULL; + SCA_WEB_ERROR* psweList = NULL; + + WCA_WRAPQUERY_HANDLE hUserQuery = NULL; + WCA_WRAPQUERY_HANDLE hWebBaseQuery = NULL; + WCA_WRAPQUERY_HANDLE hWebDirPropQuery = NULL; + WCA_WRAPQUERY_HANDLE hSslCertQuery = NULL; + WCA_WRAPQUERY_HANDLE hWebLogQuery = NULL; + WCA_WRAPQUERY_HANDLE hWebAppQuery = NULL; + WCA_WRAPQUERY_HANDLE hWebAppExtQuery = NULL; + + // initialize + hr = WcaInitialize(hInstall, "ConfigureIIs7Exec"); + ExitOnFailure(hr, "Failed to initialize"); + + hr = WcaGetProperty(L"CustomActionData", &pwzCustomActionData); + ExitOnFailure(hr, "failed to get CustomActionData"); + + // Get the CaScript key + hr = WcaReadStringFromCaData(&pwzCustomActionData, &pwzScriptKey); + ExitOnFailure(hr, "Failed to get CaScript key from custom action data"); + + // read the msi tables + hr = WcaBeginUnwrapQuery(&hUserQuery, &pwzCustomActionData); + ExitOnFailure(hr, "Failed to unwrap user query"); + + hr = ScaWebSvcExtRead(&psWseList, &pwzCustomActionData); + MessageExitOnFailure(hr, msierrIISFailedReadWebSvcExt, "failed while processing WebServiceExtensions"); + + hr = ScaAppPoolRead(&psapList, hUserQuery, &pwzCustomActionData); + MessageExitOnFailure(hr, msierrIISFailedReadAppPool, "failed while processing WebAppPools"); + + // MimeMap, Error and HttpHeader need to be read before the virtual directory and web read + hr = ScaMimeMapRead(&psmmList, &pwzCustomActionData); + MessageExitOnFailure(hr, msierrIISFailedReadMimeMap, "failed while processing MimeMaps"); + + hr = ScaHttpHeaderRead(&pshhList, &pwzCustomActionData); + MessageExitOnFailure(hr, msierrIISFailedReadHttpHeader, "failed while processing HttpHeaders"); + + hr = ScaWebErrorRead(&psweList, &pwzCustomActionData); + MessageExitOnFailure(hr, msierrIISFailedReadWebError, "failed while processing WebErrors"); + + hr = WcaBeginUnwrapQuery(&hWebDirPropQuery, &pwzCustomActionData); + ExitOnFailure(hr, "Failed to unwrap web dir properties query"); + + hr = WcaBeginUnwrapQuery(&hSslCertQuery, &pwzCustomActionData); + ExitOnFailure(hr, "Failed to unwrap ssl certificate query"); + + hr = WcaBeginUnwrapQuery(&hWebLogQuery, &pwzCustomActionData); + ExitOnFailure(hr, "Failed to unwrap web log query"); + + hr = WcaBeginUnwrapQuery(&hWebAppQuery, &pwzCustomActionData); + ExitOnFailure(hr, "Failed to unwrap web application query"); + + hr = WcaBeginUnwrapQuery(&hWebAppExtQuery, &pwzCustomActionData); + ExitOnFailure(hr, "Failed to unwrap web application extension query"); + + hr = ScaWebsRead7(&pswList, &pshhList, &psweList, hUserQuery, hWebDirPropQuery, hSslCertQuery, hWebLogQuery, hWebAppQuery, hWebAppExtQuery, &pwzCustomActionData); + MessageExitOnFailure(hr, msierrIISFailedReadWebSite, "failed while processing WebSites"); + + hr = WcaBeginUnwrapQuery(&hWebBaseQuery, &pwzCustomActionData); + ExitOnFailure(hr, "Failed to unwrap web base query"); + + hr = ScaWebDirsRead7(pswList, hUserQuery, hWebBaseQuery, hWebDirPropQuery, hWebAppQuery, hWebAppExtQuery, &pwzCustomActionData, &pswdList); + MessageExitOnFailure(hr, msierrIISFailedReadWebDirs, "failed while processing WebDirs"); + + hr = ScaVirtualDirsRead7(pswList, &psvdList, &psmmList, &pshhList, &psweList, hUserQuery, hWebBaseQuery, hWebDirPropQuery, hWebAppQuery, hWebAppExtQuery, &pwzCustomActionData); + MessageExitOnFailure(hr, msierrIISFailedReadVDirs, "failed while processing WebVirtualDirs"); + + hr = ScaFiltersRead7(pswList, hWebBaseQuery, &psfList, &pwzCustomActionData); + MessageExitOnFailure(hr, msierrIISFailedReadFilters, "failed while processing WebFilters"); + + hr = ScaPropertyRead(&pspList, &pwzCustomActionData); + MessageExitOnFailure(hr, msierrIISFailedReadProp, "failed while processing WebProperties"); + + // do uninstall actions (order is important!) + hr = ScaPropertyUninstall7(pspList); + MessageExitOnFailure(hr, msierrIISFailedSchedUninstallProp, "failed to uninstall IIS properties"); + + hr = ScaFiltersUninstall7(psfList); + MessageExitOnFailure(hr, msierrIISFailedSchedUninstallFilters, "failed to schedule uninstall of filters"); + + hr = ScaVirtualDirsUninstall7(psvdList); + MessageExitOnFailure(hr, msierrIISFailedSchedUninstallVDirs, "failed to schedule uninstall of virtual directories"); + + hr = ScaWebDirsUninstall7(pswdList); + MessageExitOnFailure(hr, msierrIISFailedSchedUninstallWebDirs, "failed to schedule uninstall of web directories"); + + hr = ScaWebsUninstall7(pswList); + MessageExitOnFailure(hr, msierrIISFailedSchedUninstallWebs, "failed to schedule uninstall of webs"); + + hr = ScaAppPoolUninstall7(psapList); + MessageExitOnFailure(hr, msierrIISFailedSchedUninstallAppPool, "failed to schedule uninstall of AppPools"); + + + // do install actions (order is important!) + // ScaWebSvcExtCommit contains both uninstall and install actions. + hr = ScaWebSvcExtCommit7(psWseList); + MessageExitOnFailure(hr, msierrIISFailedSchedInstallWebSvcExt, "failed to schedule install/uninstall of WebSvcExt"); + + hr = ScaAppPoolInstall7(psapList); + MessageExitOnFailure(hr, msierrIISFailedSchedInstallAppPool, "failed to schedule install of AppPools"); + + hr = ScaWebsInstall7(pswList, psapList); + MessageExitOnFailure(hr, msierrIISFailedSchedInstallWebs, "failed to schedule install of webs"); + + hr = ScaWebDirsInstall7(pswdList, psapList); + MessageExitOnFailure(hr, msierrIISFailedSchedInstallWebDirs, "failed to schedule install of web directories"); + + hr = ScaVirtualDirsInstall7(psvdList, psapList); + MessageExitOnFailure(hr, msierrIISFailedSchedInstallVDirs, "failed to schedule install of virtual directories"); + + hr = ScaFiltersInstall7(psfList); + MessageExitOnFailure(hr, msierrIISFailedSchedInstallFilters, "failed to schedule install of filters"); + + hr = ScaPropertyInstall7(pspList); + MessageExitOnFailure(hr, msierrIISFailedSchedInstallProp, "failed to schedule install of properties"); + + hr = ScaWriteConfigurationScript(pwzScriptKey); + ExitOnFailure(hr, "failed to schedule metabase configuration"); + +LExit: + ReleaseNullStr(pwzScriptKey); + ReleaseNullStr(pwzCustomActionData); + + WcaFinishUnwrapQuery(hUserQuery); + WcaFinishUnwrapQuery(hWebBaseQuery); + WcaFinishUnwrapQuery(hWebDirPropQuery); + WcaFinishUnwrapQuery(hSslCertQuery); + WcaFinishUnwrapQuery(hWebLogQuery); + WcaFinishUnwrapQuery(hWebAppQuery); + WcaFinishUnwrapQuery(hWebAppExtQuery); + + if (psWseList) + { + ScaWebSvcExtFreeList(psWseList); + } + + if (psfList) + { + ScaFiltersFreeList(psfList); + } + + if (psvdList) + { + ScaVirtualDirsFreeList7(psvdList); + } + + if (pswdList) + { + ScaWebDirsFreeList7(pswdList); + } + + if (pswList) + { + ScaWebsFreeList7(pswList); + } + + if (psmmList) + { + ScaMimeMapFreeList(psmmList); + } + + if (pshhList) + { + ScaHttpHeaderCheckList(pshhList); + ScaHttpHeaderFreeList(pshhList); + } + + if (psweList) + { + ScaWebErrorCheckList(psweList); + ScaWebErrorFreeList(psweList); + } + + er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE; + return WcaFinalize(er); +} diff --git a/src/ext/Iis/ca/scassl.cpp b/src/ext/Iis/ca/scassl.cpp new file mode 100644 index 00000000..4a06b77e --- /dev/null +++ b/src/ext/Iis/ca/scassl.cpp @@ -0,0 +1,115 @@ +// 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" + +enum eSslCertificateQuery { scqStoreName = 1, scqHash, scqWeb }; + +static HRESULT AddSslCertificateToList( + __in SCA_WEB_SSL_CERTIFICATE** ppswscList + ); + + +HRESULT ScaSslCertificateRead( + __in LPCWSTR wzWebId, + __in WCA_WRAPQUERY_HANDLE hSslCertQuery, + __inout SCA_WEB_SSL_CERTIFICATE** ppswscList + ) +{ + HRESULT hr = S_OK; + + MSIHANDLE hRec; + SCA_WEB_SSL_CERTIFICATE* pswsc = NULL; + LPWSTR pwzData = NULL; + + WcaFetchWrappedReset(hSslCertQuery); + + // Get the certificate information. + while (S_OK == (hr = WcaFetchWrappedRecordWhereString(hSslCertQuery, scqWeb, wzWebId, &hRec))) + { + hr = AddSslCertificateToList(ppswscList); + ExitOnFailure(hr, "failed to add ssl certificate to list"); + + pswsc = *ppswscList; + + hr = WcaGetRecordString(hRec, scqStoreName, &pwzData); + ExitOnFailure(hr, "Failed to get web ssl certificate store name."); + + hr = ::StringCchCopyW(pswsc->wzStoreName, countof(pswsc->wzStoreName), pwzData); + ExitOnFailure(hr, "Failed to copy web ssl certificate store name."); + + hr = WcaGetRecordString(hRec, scqHash, &pwzData); + ExitOnFailure(hr, "Failed to get hash for web ssl certificate."); + + hr = StrHexDecode(pwzData, pswsc->rgbSHA1Hash, countof(pswsc->rgbSHA1Hash)); + ExitOnFailure(hr, "Failed to decode certificate hash for web: %ls, data: %ls", wzWebId, pwzData); + } + + if (E_NOMOREITEMS == hr) + { + hr = S_OK; + } + ExitOnFailure(hr, "Failed to read IIsWebSiteCertificates table."); + +LExit: + ReleaseStr(pwzData); + return hr; +} + + +HRESULT ScaSslCertificateWriteMetabase( + __in IMSAdminBase* piMetabase, + __in LPCWSTR wzWebBase, + __in SCA_WEB_SSL_CERTIFICATE* pswscList + ) +{ + HRESULT hr = S_OK; + BLOB blob; + + for (SCA_WEB_SSL_CERTIFICATE* pswsc = pswscList; pswsc; pswsc = pswsc->pNext) + { + // Write: /W3SVC/1:SslCertStoreName = "MY", "CA", "Root", etc. + hr = ScaWriteMetabaseValue(piMetabase, wzWebBase, L"", MD_SSL_CERT_STORE_NAME, METADATA_INHERIT, IIS_MD_UT_SERVER, STRING_METADATA, static_cast(pswsc->wzStoreName)); + ExitOnFailure(hr, "Failed to write SslCertStoreName"); + + // Write: /W3SVC/1:SslCertHash = + blob.pBlobData = pswsc->rgbSHA1Hash; + blob.cbSize = countof(pswsc->rgbSHA1Hash); + hr = ScaWriteMetabaseValue(piMetabase, wzWebBase, L"", MD_SSL_CERT_HASH, METADATA_INHERIT, IIS_MD_UT_SERVER, BINARY_METADATA, static_cast(&blob)); + ExitOnFailure(hr, "Failed to write SslCertHash"); + } + +LExit: + return hr; +} + + +void ScaSslCertificateFreeList( + __in SCA_WEB_SSL_CERTIFICATE* pswscList + ) +{ + SCA_WEB_SSL_CERTIFICATE* pswscDelete = pswscList; + while (pswscList) + { + pswscDelete = pswscList; + pswscList = pswscList->pNext; + + MemFree(pswscDelete); + } +} + + +static HRESULT AddSslCertificateToList( + __in SCA_WEB_SSL_CERTIFICATE** ppswscList + ) +{ + HRESULT hr = S_OK; + + SCA_WEB_SSL_CERTIFICATE* pswsc = static_cast(MemAlloc(sizeof(SCA_WEB_SSL_CERTIFICATE), TRUE)); + ExitOnNull(pswsc, hr, E_OUTOFMEMORY, "failed to allocate memory for new SSL certificate list element"); + + pswsc->pNext = *ppswscList; + *ppswscList = pswsc; + +LExit: + return hr; +} diff --git a/src/ext/Iis/ca/scassl.h b/src/ext/Iis/ca/scassl.h new file mode 100644 index 00000000..df7c473d --- /dev/null +++ b/src/ext/Iis/ca/scassl.h @@ -0,0 +1,36 @@ +#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 MD_SSL_CERT_HASH ( IIS_MD_SSL_BASE+6 ) +#define MD_SSL_CERT_STORE_NAME ( IIS_MD_SSL_BASE+11 ) +//#define WIDE(x) WIDE2(x) +//#define WIDE2(x) L ## x + + +// structs +struct SCA_WEB_SSL_CERTIFICATE +{ + WCHAR wzStoreName[65]; + BYTE rgbSHA1Hash[CB_CERTIFICATE_HASH]; + + SCA_WEB_SSL_CERTIFICATE* pNext; +}; + + +// prototypes +HRESULT ScaSslCertificateRead( + __in LPCWSTR wzWebId, + __in WCA_WRAPQUERY_HANDLE hSslCertQuery, + __inout SCA_WEB_SSL_CERTIFICATE** ppswscList + ); + +HRESULT ScaSslCertificateWriteMetabase( + __in IMSAdminBase* piMetabase, + __in LPCWSTR wzWebBase, + __in SCA_WEB_SSL_CERTIFICATE* pswscList + ); + +void ScaSslCertificateFreeList( + __in SCA_WEB_SSL_CERTIFICATE* pswscList + ); diff --git a/src/ext/Iis/ca/scassl7.cpp b/src/ext/Iis/ca/scassl7.cpp new file mode 100644 index 00000000..47c16a9d --- /dev/null +++ b/src/ext/Iis/ca/scassl7.cpp @@ -0,0 +1,34 @@ +// 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" + +HRESULT ScaSslCertificateWrite7( + __in_z LPCWSTR wzWebBase, + __in SCA_WEB_SSL_CERTIFICATE* pswscList + ) +{ + HRESULT hr = S_OK; + WCHAR wzEncodedCertificateHash[CB_CERTIFICATE_HASH * 2 + 1] = { 0 }; + + for (SCA_WEB_SSL_CERTIFICATE* pswsc = pswscList; pswsc; pswsc = pswsc->pNext) + { + hr = ScaWriteConfigID(IIS_SSL_BINDING); + ExitOnFailure(hr, "Failed write SSL binding ID"); + hr = ScaWriteConfigID(IIS_CREATE); // Need to determine site action + ExitOnFailure(hr, "Failed write binding action"); + + hr = ScaWriteConfigString(wzWebBase); //site name key + ExitOnFailure(hr, "Failed to write SSL website"); + hr = ScaWriteConfigString(pswsc->wzStoreName); //ssl store name + ExitOnFailure(hr, "Failed to write SSL store name"); + + hr = StrHexEncode(pswsc->rgbSHA1Hash, countof(pswsc->rgbSHA1Hash), wzEncodedCertificateHash, countof(wzEncodedCertificateHash)); + ExitOnFailure(hr, "Failed to encode SSL hash"); + + hr = ScaWriteConfigString(wzEncodedCertificateHash); //ssl hash + ExitOnFailure(hr, "Failed to write SSL hash"); + } +LExit: + + return hr; +} diff --git a/src/ext/Iis/ca/scassl7.h b/src/ext/Iis/ca/scassl7.h new file mode 100644 index 00000000..1a4b09af --- /dev/null +++ b/src/ext/Iis/ca/scassl7.h @@ -0,0 +1,8 @@ +#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 ScaSslCertificateWrite7( + __in_z LPCWSTR wzWebBase, + __in SCA_WEB_SSL_CERTIFICATE* pswscList + ); diff --git a/src/ext/Iis/ca/scauser.cpp b/src/ext/Iis/ca/scauser.cpp new file mode 100644 index 00000000..0b99edff --- /dev/null +++ b/src/ext/Iis/ca/scauser.cpp @@ -0,0 +1,91 @@ +// 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" + +LPCWSTR vcsUserQuery = L"SELECT `User`, `Component_`, `Name`, `Domain`, `Password` FROM `User` WHERE `User`=?"; +enum eUserQuery { vuqUser = 1, vuqComponent, vuqName, vuqDomain, vuqPassword }; + +LPCWSTR vcsGroupQuery = L"SELECT `Group`, `Component_`, `Name`, `Domain` FROM `Group` WHERE `Group`=?"; +enum eGroupQuery { vgqGroup = 1, vgqComponent, vgqName, vgqDomain }; + +LPCWSTR vcsUserGroupQuery = L"SELECT `User_`, `Group_` FROM `UserGroup` WHERE `User_`=?"; +enum eUserGroupQuery { vugqUser = 1, vugqGroup }; + +LPCWSTR vActionableQuery = L"SELECT `User`,`Component_`,`Name`,`Domain`,`Password`,`Attributes` FROM `User` WHERE `Component_` IS NOT NULL"; +enum eActionableQuery { vaqUser = 1, vaqComponent, vaqName, vaqDomain, vaqPassword, vaqAttributes }; + +HRESULT __stdcall ScaGetUserDeferred( + __in LPCWSTR wzUser, + __in WCA_WRAPQUERY_HANDLE hUserQuery, + __out SCA_USER* pscau + ) +{ + if (!wzUser || !pscau) + { + return E_INVALIDARG; + } + + HRESULT hr = S_OK; + MSIHANDLE hRec, hRecTest; + + LPWSTR pwzData = NULL; + + // clear struct and bail right away if no user key was passed to search for + ::ZeroMemory(pscau, sizeof(*pscau)); + if (!*wzUser) + { + ExitFunction1(hr = S_OK); + } + + // Reset back to the first record + WcaFetchWrappedReset(hUserQuery); + + hr = WcaFetchWrappedRecordWhereString(hUserQuery, vuqUser, wzUser, &hRec); + if (S_OK == hr) + { + hr = WcaFetchWrappedRecordWhereString(hUserQuery, vuqUser, wzUser, &hRecTest); + if (S_OK == hr) + { + AssertSz(FALSE, "Found multiple matching User rows"); + } + + hr = WcaGetRecordString(hRec, vuqUser, &pwzData); + ExitOnFailure(hr, "Failed to get User.User"); + hr = ::StringCchCopyW(pscau->wzKey, countof(pscau->wzKey), pwzData); + ExitOnFailure(hr, "Failed to copy key string to user object (in deferred CA)"); + + hr = WcaGetRecordString(hRec, vuqComponent, &pwzData); + ExitOnFailure(hr, "Failed to get User.Component_"); + hr = ::StringCchCopyW(pscau->wzComponent, countof(pscau->wzComponent), pwzData); + ExitOnFailure(hr, "Failed to copy component string to user object (in deferred CA)"); + + hr = WcaGetRecordString(hRec, vuqName, &pwzData); + ExitOnFailure(hr, "Failed to get User.Name"); + hr = ::StringCchCopyW(pscau->wzName, countof(pscau->wzName), pwzData); + ExitOnFailure(hr, "Failed to copy name string to user object (in deferred CA)"); + + hr = WcaGetRecordString(hRec, vuqDomain, &pwzData); + ExitOnFailure(hr, "Failed to get User.Domain"); + hr = ::StringCchCopyW(pscau->wzDomain, countof(pscau->wzDomain), pwzData); + ExitOnFailure(hr, "Failed to copy domain string to user object (in deferred CA)"); + + hr = WcaGetRecordString(hRec, vuqPassword, &pwzData); + ExitOnFailure(hr, "Failed to get User.Password"); + hr = ::StringCchCopyW(pscau->wzPassword, countof(pscau->wzPassword), pwzData); + ExitOnFailure(hr, "Failed to copy password string to user object (in deferred CA)"); + } + else if (E_NOMOREITEMS == hr) + { + WcaLog(LOGMSG_STANDARD, "Error: Cannot locate User.User='%ls'", wzUser); + hr = E_FAIL; + } + else + { + ExitOnFailure(hr, "Error fetching single User row"); + } + +LExit: + ReleaseStr(pwzData); + + return hr; +} diff --git a/src/ext/Iis/ca/scauser.h b/src/ext/Iis/ca/scauser.h new file mode 100644 index 00000000..b2b94650 --- /dev/null +++ b/src/ext/Iis/ca/scauser.h @@ -0,0 +1,39 @@ +#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. + +// structs +struct SCA_GROUP +{ + WCHAR wzKey[MAX_DARWIN_KEY + 1]; + WCHAR wzComponent[MAX_DARWIN_KEY + 1]; + + WCHAR wzDomain[MAX_DARWIN_COLUMN + 1]; + WCHAR wzName[MAX_DARWIN_COLUMN + 1]; + + SCA_GROUP *psgNext; +}; + +struct SCA_USER +{ + WCHAR wzKey[MAX_DARWIN_KEY + 1]; + WCHAR wzComponent[MAX_DARWIN_KEY + 1]; + INSTALLSTATE isInstalled; + INSTALLSTATE isAction; + + WCHAR wzDomain[MAX_DARWIN_COLUMN + 1]; + WCHAR wzName[MAX_DARWIN_COLUMN + 1]; + WCHAR wzPassword[MAX_DARWIN_COLUMN + 1]; + INT iAttributes; + + SCA_GROUP *psgGroups; + + SCA_USER *psuNext; +}; + + +// prototypes +HRESULT __stdcall ScaGetUserDeferred( + __in LPCWSTR wzUser, + __in WCA_WRAPQUERY_HANDLE hUserQuery, + __out SCA_USER* pscau + ); diff --git a/src/ext/Iis/ca/scavdir.cpp b/src/ext/Iis/ca/scavdir.cpp new file mode 100644 index 00000000..d388ce97 --- /dev/null +++ b/src/ext/Iis/ca/scavdir.cpp @@ -0,0 +1,331 @@ +// 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" + +// prototypes +static HRESULT AddVirtualDirToList( + __in SCA_VDIR** psvdList + ); + + +HRESULT __stdcall ScaVirtualDirsRead( + __in IMSAdminBase* piMetabase, + __in SCA_WEB* pswList, + __in SCA_VDIR** ppsvdList, + __in SCA_MIMEMAP** ppsmmList, + __in SCA_HTTP_HEADER** ppshhList, + __in SCA_WEB_ERROR** ppsweList, + __in WCA_WRAPQUERY_HANDLE hUserQuery, + __in WCA_WRAPQUERY_HANDLE hWebBaseQuery, + __in WCA_WRAPQUERY_HANDLE hWebDirPropQuery, + __in WCA_WRAPQUERY_HANDLE hWebAppQuery, + __in WCA_WRAPQUERY_HANDLE hWebAppExtQuery, + __inout LPWSTR *ppwzCustomActionData + ) +{ + Assert(piMetabase && ppsvdList); + + HRESULT hr = S_OK; + MSIHANDLE hRec; + INSTALLSTATE isInstalled = INSTALLSTATE_UNKNOWN; + INSTALLSTATE isAction = INSTALLSTATE_UNKNOWN; + + SCA_VDIR* pvdir = NULL; + LPWSTR pwzData = NULL; + + WCA_WRAPQUERY_HANDLE hWrapQuery = NULL; + + hr = WcaBeginUnwrapQuery(&hWrapQuery, ppwzCustomActionData); + ExitOnFailure(hr, "Failed to unwrap query for ScaAppPoolRead"); + + if (0 == WcaGetQueryRecords(hWrapQuery)) + { + WcaLog(LOGMSG_VERBOSE, "Skipping ScaVirtualDirsRead() because IIsWebVirtualDir table not present"); + ExitFunction1(hr = S_FALSE); + } + + // loop through all the vdirs + while (S_OK == (hr = WcaFetchWrappedRecord(hWrapQuery, &hRec))) + { + // Get the Component first. If there is a Component and it is not being modified during + // this transaction, skip processing this whole record. + hr = WcaGetRecordString(hRec, vdqComponent, &pwzData); + ExitOnFailure(hr, "failed to get IIsWebVirtualDir.Component"); + + hr = WcaGetRecordInteger(hRec, vdqInstalled, (int *)&isInstalled); + ExitOnFailure(hr, "Failed to get Component installed state for virtual dir"); + + hr = WcaGetRecordInteger(hRec, vdqAction, (int *)&isAction); + ExitOnFailure(hr, "Failed to get Component action state for virtual dir"); + + if (!WcaIsInstalling(isInstalled, isAction) && + !WcaIsReInstalling(isInstalled, isAction) && + !WcaIsUninstalling(isInstalled, isAction)) + { + continue; // skip this record. + } + + hr = AddVirtualDirToList(ppsvdList); + ExitOnFailure(hr, "failed to add virtual dir to list"); + + pvdir = *ppsvdList; + + hr = ::StringCchCopyW(pvdir->wzComponent, countof(pvdir->wzComponent), pwzData); + ExitOnFailure(hr, "failed to copy component name: %ls", pwzData); + + pvdir->isInstalled = isInstalled; + pvdir->isAction = isAction; + + // get the web key + hr = WcaGetRecordString(hRec, vdqWeb, &pwzData); + ExitOnFailure(hr, "Failed to get Web for VirtualDir"); + + hr = ScaWebsGetBase(piMetabase, pswList, pwzData, pvdir->wzWebBase, countof(pvdir->wzWebBase), hWebBaseQuery); + if (WcaIsUninstalling(isInstalled, isAction)) + { + // If we're uninstalling, ignore any failure to find the existing web + hr = S_OK; + } + ExitOnFailure(hr, "Failed to get base of web: %ls for VirtualDir", pwzData); + + hr = WcaGetRecordString(hRec, vdqAlias, &pwzData); + ExitOnFailure(hr, "Failed to get Alias for VirtualDir"); + + if (0 != lstrlenW(pvdir->wzWebBase)) + { + hr = ::StringCchPrintfW(pvdir->wzVDirRoot, countof(pvdir->wzVDirRoot), L"%s/Root/%s", pvdir->wzWebBase, pwzData); + ExitOnFailure(hr, "Failed to set VDirRoot for VirtualDir"); + } + + // get the vdir's directory + hr = WcaGetRecordString(hRec, vdqDirectory, &pwzData); + ExitOnFailure(hr, "Failed to get Directory for VirtualDir"); + + // get the web's directory + if (INSTALLSTATE_SOURCE == pvdir->isAction) + { + hr = WcaGetRecordString(hRec, vdqSourcePath, &pwzData); + } + else + { + hr = WcaGetRecordString(hRec, vdqTargetPath, &pwzData); + } + ExitOnFailure(hr, "Failed to get Source/TargetPath for Directory"); + + // remove trailing backslash(es) + while (lstrlenW(pwzData) > 0 && pwzData[lstrlenW(pwzData)-1] == L'\\') + { + pwzData[lstrlenW(pwzData)-1] = 0; + } + hr = ::StringCchCopyW(pvdir->wzDirectory, countof(pvdir->wzDirectory), pwzData); + ExitOnFailure(hr, "Failed to copy directory string to vdir object"); + + // get the security information for this web + hr = WcaGetRecordString(hRec, vdqProperties, &pwzData); + ExitOnFailure(hr, "Failed to get web directory identifier for VirtualDir"); + if (*pwzData) + { + hr = ScaGetWebDirProperties(pwzData, hUserQuery, hWebDirPropQuery, &pvdir->swp); + ExitOnFailure(hr, "Failed to get web directory for VirtualDir"); + + pvdir->fHasProperties = TRUE; + } + + // get the application information for this web + hr = WcaGetRecordString(hRec, vdqApplication, &pwzData); + ExitOnFailure(hr, "Failed to get application identifier for VirtualDir"); + if (*pwzData) + { + hr = ScaGetWebApplication(NULL, pwzData, hWebAppQuery, hWebAppExtQuery, &pvdir->swapp); + ExitOnFailure(hr, "Failed to get application for VirtualDir"); + + pvdir->fHasApplication = TRUE; + } + + hr = WcaGetRecordString(hRec, vdqVDir, &pwzData); + ExitOnFailure(hr, "Failed to get VDir for VirtualDir"); + + if (*pwzData && *ppsmmList) + { + hr = ScaGetMimeMap(mmptVDir, pwzData, ppsmmList, &pvdir->psmm); + ExitOnFailure(hr, "Failed to get mimemap for VirtualDir"); + } + + if (*pwzData && *ppshhList) + { + hr = ScaGetHttpHeader(hhptVDir, pwzData, ppshhList, &pvdir->pshh); + ExitOnFailure(hr, "Failed to get custom HTTP headers for VirtualDir: %ls", pwzData); + } + + if (*pwzData && *ppsweList) + { + hr = ScaGetWebError(weptVDir, pwzData, ppsweList, &pvdir->pswe); + ExitOnFailure(hr, "Failed to get custom web errors for VirtualDir: %ls", pwzData); + } + } + + if (E_NOMOREITEMS == hr) + { + hr = S_OK; + } + ExitOnFailure(hr, "Failure while processing VirtualDirs"); + +LExit: + WcaFinishUnwrapQuery(hWrapQuery); + + ReleaseStr(pwzData); + return hr; +} + + +HRESULT ScaVirtualDirsInstall( + __in IMSAdminBase* piMetabase, + __in SCA_VDIR* psvdList, + __in SCA_APPPOOL * psapList + ) +{ + Assert(piMetabase); + + HRESULT hr = S_OK; + SCA_VDIR* psvd = psvdList; + int i; + + while (psvd) + { + // On reinstall, we have to uninstall the old application, otherwise a duplicate will be created + if (WcaIsReInstalling(psvd->isInstalled, psvd->isAction)) + { + if (psvd->fHasApplication) + { + hr = ScaDeleteApp(piMetabase, psvd->wzVDirRoot); + ExitOnFailure(hr, "Failed to remove application for WebVDir as part of a reinstall"); + } + } + + if (WcaIsInstalling(psvd->isInstalled, psvd->isAction)) + { + hr = ScaCreateMetabaseKey(piMetabase, psvd->wzVDirRoot, L""); + ExitOnFailure(hr, "Failed to create key for VirtualDir"); + hr = ScaWriteMetabaseValue(piMetabase, psvd->wzVDirRoot, L"", MD_KEY_TYPE, METADATA_NO_ATTRIBUTES, IIS_MD_UT_SERVER, STRING_METADATA, (LPVOID)L"IIsWebVirtualDir"); + ExitOnFailure(hr, "Failed to write key type for for VirtualDir"); + i = 0x4000003e; // 1073741886; // default directory browsing rights + hr = ScaWriteMetabaseValue(piMetabase, psvd->wzVDirRoot, L"", MD_DIRECTORY_BROWSING, METADATA_INHERIT, IIS_MD_UT_FILE, DWORD_METADATA, (LPVOID)((DWORD_PTR)i)); + ExitOnFailure(hr, "Failed to set directory browsing for VirtualDir"); + + hr = ScaWriteMetabaseValue(piMetabase, psvd->wzVDirRoot, L"", MD_VR_PATH, METADATA_INHERIT, IIS_MD_UT_FILE, STRING_METADATA, (LPVOID)psvd->wzDirectory); + ExitOnFailure(hr, "Failed to write Directory for VirtualDir"); + + if (psvd->fHasProperties) + { + ScaWriteWebDirProperties(piMetabase, psvd->wzVDirRoot, &psvd->swp); + ExitOnFailure(hr, "Failed to write directory properties for VirtualDir"); + } + + if (psvd->fHasApplication) + { + hr = ScaWriteWebApplication(piMetabase, psvd->wzVDirRoot, &psvd->swapp, psapList); + ExitOnFailure(hr, "Failed to write application for VirtualDir"); + } + + if (psvd->psmm) + { + hr = ScaWriteMimeMap(piMetabase, psvd->wzVDirRoot, psvd->psmm); + ExitOnFailure(hr, "Failed to write mimemap for VirtualDir"); + } + + if (psvd->pshh) + { + hr = ScaWriteHttpHeader(piMetabase, psvd->wzVDirRoot, psvd->pshh); + ExitOnFailure(hr, "Failed to write custom HTTP headers for VirtualDir"); + } + + if (psvd->pswe) + { + hr = ScaWriteWebError(piMetabase, weptVDir, psvd->wzVDirRoot, psvd->pswe); + ExitOnFailure(hr, "Failed to write custom web errors for VirtualDir"); + } + } + + psvd = psvd->psvdNext; + } + +LExit: + return hr; +} + + +HRESULT ScaVirtualDirsUninstall( + __in IMSAdminBase* piMetabase, + __in SCA_VDIR* psvdList + ) +{ + Assert(piMetabase); + + HRESULT hr = S_OK; + SCA_VDIR* psvd = psvdList; + + while (psvd) + { + if (WcaIsUninstalling(psvd->isInstalled, psvd->isAction)) + { + // delete the application for this virtual directory + if (psvd->fHasApplication) + { + hr = ScaDeleteApp(piMetabase, psvd->wzVDirRoot); + ExitOnFailure(hr, "Failed to remove application for WebVDir"); + } + + if (0 != lstrlenW(psvd->wzVDirRoot)) + { + hr = ScaDeleteMetabaseKey(piMetabase, psvd->wzVDirRoot, L""); + ExitOnFailure(hr, "Failed to remove VirtualDir '%ls' from metabase", psvd->wzKey); + } + } + + psvd = psvd->psvdNext; + } + +LExit: + return hr; +} + + +void ScaVirtualDirsFreeList( + __in SCA_VDIR* psvdList + ) +{ + SCA_VDIR* psvdDelete = psvdList; + while (psvdList) + { + psvdDelete = psvdList; + psvdList = psvdList->psvdNext; + + if (psvdDelete->psmm) + { + ScaMimeMapFreeList(psvdDelete->psmm); + } + + if (psvdDelete->pswe) + { + ScaWebErrorFreeList(psvdDelete->pswe); + } + + MemFree(psvdDelete); + } +} + + +static HRESULT AddVirtualDirToList( + __in SCA_VDIR** ppsvdList + ) +{ + HRESULT hr = S_OK; + SCA_VDIR* psvd = static_cast(MemAlloc(sizeof(SCA_VDIR), TRUE)); + ExitOnNull(psvd, hr, E_OUTOFMEMORY, "failed to allocate memory for new vdir list element"); + + psvd->psvdNext= *ppsvdList; + *ppsvdList = psvd; + +LExit: + return hr; +} diff --git a/src/ext/Iis/ca/scavdir.h b/src/ext/Iis/ca/scavdir.h new file mode 100644 index 00000000..c7e9661b --- /dev/null +++ b/src/ext/Iis/ca/scavdir.h @@ -0,0 +1,71 @@ +#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. + + +#include "scawebprop.h" +#include "scawebapp.h" +#include "scamimemap.h" +#include "scaapppool.h" + +enum eVDirQuery { vdqWeb = 1, vdqVDir, vdqComponent , vdqAlias, vdqDirectory, vdqProperties, vdqApplication, vdqInstalled, vdqAction, vdqSourcePath, vdqTargetPath }; + +struct SCA_VDIR +{ + // darwin information + WCHAR wzKey[MAX_DARWIN_KEY + 1]; + WCHAR wzComponent[MAX_DARWIN_KEY + 1]; + INSTALLSTATE isInstalled; + INSTALLSTATE isAction; + + // metabase information + WCHAR wzWebKey[MAX_DARWIN_KEY + 1]; + WCHAR wzWebBase[METADATA_MAX_NAME_LEN + 1]; + WCHAR wzVDirRoot[METADATA_MAX_NAME_LEN + 1]; + + // iis configuation information + WCHAR wzDirectory[MAX_PATH]; + + BOOL fHasProperties; + SCA_WEB_PROPERTIES swp; + + BOOL fHasApplication; + SCA_WEB_APPLICATION swapp; + + SCA_MIMEMAP* psmm; // mime mappings + SCA_HTTP_HEADER* pshh; // custom web errors + SCA_WEB_ERROR* pswe; // custom web errors + + SCA_VDIR* psvdNext; +}; + + +// prototypes +HRESULT __stdcall ScaVirtualDirsRead( + __in IMSAdminBase* piMetabase, + __in SCA_WEB* pswList, + __in SCA_VDIR** ppsvdList, + __in SCA_MIMEMAP** ppsmmList, + __in SCA_HTTP_HEADER** ppshhList, + __in SCA_WEB_ERROR** ppsweList, + __in WCA_WRAPQUERY_HANDLE hUserQuery, + __in WCA_WRAPQUERY_HANDLE hWebBaseQuery, + __in WCA_WRAPQUERY_HANDLE hWebDirPropQuery, + __in WCA_WRAPQUERY_HANDLE hWebAppQuery, + __in WCA_WRAPQUERY_HANDLE hWebAppExtQuery, + __inout LPWSTR *ppwzCustomActionData + ); + +HRESULT ScaVirtualDirsInstall( + __in IMSAdminBase* piMetabase, + __in SCA_VDIR* psvdList, + __in SCA_APPPOOL * psapList + ); + +HRESULT ScaVirtualDirsUninstall( + __in IMSAdminBase* piMetabase, + __in SCA_VDIR* psvdList + ); + +void ScaVirtualDirsFreeList( + __in SCA_VDIR* psvdList + ); diff --git a/src/ext/Iis/ca/scavdir7.cpp b/src/ext/Iis/ca/scavdir7.cpp new file mode 100644 index 00000000..3f675357 --- /dev/null +++ b/src/ext/Iis/ca/scavdir7.cpp @@ -0,0 +1,380 @@ +// 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" + +// prototypes +static HRESULT AddVirtualDirToList7( + __in SCA_VDIR7** psvdList + ); + + +HRESULT __stdcall ScaVirtualDirsRead7( + __in SCA_WEB7* pswList, + __in SCA_VDIR7** ppsvdList, + __in SCA_MIMEMAP** ppsmmList, + __in SCA_HTTP_HEADER** ppshhList, + __in SCA_WEB_ERROR** ppsweList, + __in WCA_WRAPQUERY_HANDLE hUserQuery, + __in WCA_WRAPQUERY_HANDLE /*hWebBaseQuery*/, + __in WCA_WRAPQUERY_HANDLE hWebDirPropQuery, + __in WCA_WRAPQUERY_HANDLE hWebAppQuery, + __in WCA_WRAPQUERY_HANDLE hWebAppExtQuery, + __inout LPWSTR *ppwzCustomActionData + ) +{ + Assert(ppsvdList); + + HRESULT hr = S_OK; + MSIHANDLE hRec; + + SCA_VDIR7* pvdir = NULL; + LPWSTR pwzData = NULL; + WCA_WRAPQUERY_HANDLE hWrapQuery = NULL; + + hr = WcaBeginUnwrapQuery(&hWrapQuery, ppwzCustomActionData); + ExitOnFailure(hr, "Failed to unwrap query for ScaAppPoolRead"); + + if (0 == WcaGetQueryRecords(hWrapQuery)) + { + WcaLog(LOGMSG_VERBOSE, "Skipping ScaVirtualDirsRead() because IIsWebVirtualDir table not present"); + ExitFunction1(hr = S_FALSE); + } + + // loop through all the vdirs + while (S_OK == (hr = WcaFetchWrappedRecord(hWrapQuery, &hRec))) + { + // Add this record's information into the list of things to process. + hr = AddVirtualDirToList7(ppsvdList); + ExitOnFailure(hr, "failed to add vdir to vdir list"); + + pvdir = *ppsvdList; + + // get the darwin information + hr = WcaGetRecordString(hRec, vdqComponent, &pwzData); + ExitOnFailure(hr, "failed to get IIsWebVirtualDir.Component"); + + hr = WcaGetRecordInteger(hRec, vdqInstalled, (int *)&pvdir->isInstalled); + ExitOnFailure(hr, "Failed to get Component installed state for virtual dir"); + + hr = WcaGetRecordInteger(hRec, vdqAction, (int *)&pvdir->isAction); + ExitOnFailure(hr, "Failed to get Component action state for virtual dir"); + + // get vdir properties + hr = ::StringCchCopyW(pvdir->wzComponent, countof(pvdir->wzComponent), pwzData); + ExitOnFailure(hr, "failed to copy vdir component name: %ls", pwzData); + + hr = WcaGetRecordString(hRec, vdqWeb, &pwzData); + ExitOnFailure(hr, "Failed to get Web for VirtualDir"); + + hr = ScaWebsGetBase7(pswList, pwzData, pvdir->wzWebName , countof(pvdir->wzWebName)); + if (S_FALSE == hr) + { + hr = HRESULT_FROM_WIN32(ERROR_NOT_FOUND); + ExitOnFailure(hr, "Failed to get Web Base for VirtualDir"); + } + if (WcaIsUninstalling(pvdir->isInstalled, pvdir->isAction)) + { + // If we're uninstalling, ignore any failure to find the existing web + hr = S_OK; + } + + hr = WcaGetRecordString(hRec, vdqAlias, &pwzData); + ExitOnFailure(hr, "Failed to get Alias for VirtualDir"); + + hr = ::StringCchCopyW(pvdir->wzVDirRoot, countof(pvdir->wzVDirRoot), pwzData); + ExitOnFailure(hr, "Failed to set VDirRoot for VirtualDir"); + + // get the vdir's directory + hr = WcaGetRecordString(hRec, vdqDirectory, &pwzData); + ExitOnFailure(hr, "Failed to get Directory for VirtualDir"); + + // get the web's directory + if (INSTALLSTATE_SOURCE == pvdir->isAction) + { + hr = WcaGetRecordString(hRec, vdqSourcePath, &pwzData); + } + else + { + hr = WcaGetRecordString(hRec, vdqTargetPath, &pwzData); + } + ExitOnFailure(hr, "Failed to get Source/TargetPath for Directory"); + + // remove trailing backslash(es) + while (lstrlenW(pwzData) > 0 && pwzData[lstrlenW(pwzData)-1] == L'\\') + { + pwzData[lstrlenW(pwzData)-1] = 0; + } + hr = ::StringCchCopyW(pvdir->wzDirectory, countof(pvdir->wzDirectory), pwzData); + ExitOnFailure(hr, "Failed to copy directory string to vdir object"); + + // get the security information for this web + hr = WcaGetRecordString(hRec, vdqProperties, &pwzData); + ExitOnFailure(hr, "Failed to get web directory identifier for VirtualDir"); + if (*pwzData) + { + hr = ScaGetWebDirProperties(pwzData, hUserQuery, hWebDirPropQuery, &pvdir->swp); + ExitOnFailure(hr, "Failed to get web directory for VirtualDir"); + + pvdir->fHasProperties = TRUE; + } + + // get the application information for this web + hr = WcaGetRecordString(hRec, vdqApplication, &pwzData); + ExitOnFailure(hr, "Failed to get application identifier for VirtualDir"); + if (*pwzData) + { + hr = ScaGetWebApplication(NULL, pwzData, hWebAppQuery, hWebAppExtQuery, &pvdir->swapp); + ExitOnFailure(hr, "Failed to get application for VirtualDir"); + + pvdir->fHasApplication = TRUE; + } + + hr = WcaGetRecordString(hRec, vdqVDir, &pwzData); + ExitOnFailure(hr, "Failed to get VDir for VirtualDir"); + + if (*pwzData && *ppsmmList) + { + hr = ScaGetMimeMap(mmptVDir, pwzData, ppsmmList, &pvdir->psmm); + ExitOnFailure(hr, "Failed to get mimemap for VirtualDir"); + } + + if (*pwzData && *ppshhList) + { + hr = ScaGetHttpHeader(hhptVDir, pwzData, ppshhList, &pvdir->pshh); + ExitOnFailure(hr, "Failed to get custom HTTP headers for VirtualDir: %ls", pwzData); + } + + if (*pwzData && *ppsweList) + { + hr = ScaGetWebError(weptVDir, pwzData, ppsweList, &pvdir->pswe); + ExitOnFailure(hr, "Failed to get custom web errors for VirtualDir: %ls", pwzData); + } + } + + if (E_NOMOREITEMS == hr) + { + hr = S_OK; + } + ExitOnFailure(hr, "Failure while processing VirtualDirs"); + +LExit: + WcaFinishUnwrapQuery(hWrapQuery); + + ReleaseStr(pwzData); + + return hr; +} + + +HRESULT ScaVirtualDirsInstall7( + __in SCA_VDIR7* psvdList, + __in SCA_APPPOOL * psapList + ) +{ + HRESULT hr = S_OK; + SCA_VDIR7* psvd = psvdList; + LPWSTR wzPath = NULL; + WCHAR wzAppPoolName[MAX_PATH]; + while (psvd) + { + if (WcaIsInstalling(psvd->isInstalled, psvd->isAction)) + { + // First write all applications, this is necessary since vdirs must be nested under the applications. + if (psvd->fHasApplication) + { + //create the application for this vdir application + hr = ScaWriteConfigID(IIS_APPLICATION); + ExitOnFailure(hr, "Failed to write app ID"); + hr = ScaWriteConfigID(IIS_CREATE); + ExitOnFailure(hr, "Failed to write app action"); +#pragma prefast(suppress:26037, "Source string is null terminated - it is populated as target of ::StringCchCopyW") + hr = ScaWriteConfigString(psvd->wzWebName); //site name key + ExitOnFailure(hr, "Failed to write app web key"); + hr = StrAllocFormatted(&wzPath, L"/%s", psvd->wzVDirRoot); + ExitOnFailure(hr, "Failed to create app path"); + hr = ScaWriteConfigString(wzPath); // App Path + ExitOnFailure(hr, "Failed to write app path root "); + + if (!*psvd->swapp.wzAppPool) + { + //This Application goes in default appPool + hr = ScaWriteConfigString(L""); // App Pool + } + else + { + //get apppool from WebApplication +#pragma prefast(suppress:26037, "Source string is null terminated - it is populated as target of ::StringCchCopyW") + hr = ScaFindAppPool7(psvd->swapp.wzAppPool, wzAppPoolName, countof(wzAppPoolName), psapList); + ExitOnFailure(hr, "Failed to read app pool from application"); + hr = ScaWriteConfigString(wzAppPoolName); // App Pool + ExitOnFailure(hr, "Failed to write appPool for vdir"); + + } + } + } + + psvd = psvd->psvdNext; + } + + // Reset our linked list and write all the VDirs + psvd = psvdList; + while (psvd) + { + if (WcaIsInstalling(psvd->isInstalled, psvd->isAction)) + { + //create the Vdir + hr = ScaWriteConfigID(IIS_VDIR); + ExitOnFailure(hr, "Failed write VirDir ID") + hr = ScaWriteConfigID(IIS_CREATE); + ExitOnFailure(hr, "Failed write VirDir action") +#pragma prefast(suppress:26037, "Source string is null terminated - it is populated as target of ::StringCchCopyW") + hr = ScaWriteConfigString(psvd->wzWebName); //site name key + ExitOnFailure(hr, "Failed write VirDir web name"); + hr = StrAllocFormatted(&wzPath, L"/%s", psvd->wzVDirRoot); + ExitOnFailure(hr, "Failed to create vdir path"); + hr = ScaWriteConfigString(wzPath); //vdir path + ExitOnFailure(hr, "Failed write VirDir path") +#pragma prefast(suppress:26037, "Source string is null terminated - it is populated as target of ::StringCchCopyW") + hr = ScaWriteConfigString(psvd->wzDirectory); //physical dir + ExitOnFailure(hr, "Failed write VirDir dir"); + + if (psvd->fHasProperties) + { + ScaWriteWebDirProperties7(psvd->wzWebName, psvd->wzVDirRoot, &psvd->swp); + ExitOnFailure(hr, "Failed to write directory properties for VirtualDir"); + } + + if (psvd->fHasApplication) + { + hr = ScaWriteWebApplication7(psvd->wzWebName, psvd->wzVDirRoot, &psvd->swapp, psapList); + ExitOnFailure(hr, "Failed to write application for VirtualDir"); + } + + if (psvd->psmm) + { + hr = ScaWriteMimeMap7(psvd->wzWebName, psvd->wzVDirRoot, psvd->psmm); + ExitOnFailure(hr, "Failed to write mimemap for VirtualDir"); + } + + if (psvd->pshh) + { + hr = ScaWriteHttpHeader7(psvd->wzWebName, psvd->wzVDirRoot, psvd->pshh); + ExitOnFailure(hr, "Failed to write custom HTTP headers for VirtualDir"); + } + + if (psvd->pswe) + { + hr = ScaWriteWebError7(psvd->wzWebName, psvd->wzVDirRoot, psvd->pswe); + ExitOnFailure(hr, "Failed to write custom web errors for VirtualDir"); + } + } + + psvd = psvd->psvdNext; + } + +LExit: + ReleaseStr(wzPath); + return hr; +} + + +HRESULT ScaVirtualDirsUninstall7( + __in SCA_VDIR7* psvdList + ) +{ + + HRESULT hr = S_OK; + SCA_VDIR7* psvd = psvdList; + LPWSTR wzPath = NULL; + + while (psvd) + { + if (WcaIsUninstalling(psvd->isInstalled, psvd->isAction)) + { + //init path + hr = StrAllocFormatted(&wzPath, L"/%s", psvd->wzVDirRoot); + ExitOnFailure(hr, "Failed to create vdir path"); + + if (psvd->fHasApplication) + { + //delete Application + hr = ScaWriteConfigID(IIS_APPLICATION); + ExitOnFailure(hr, "Failed to write app ID "); + hr = ScaWriteConfigID(IIS_DELETE); + ExitOnFailure(hr, "Failed to write delete app ID "); +#pragma prefast(suppress:26037, "Source string is null terminated - it is populated as target of ::StringCchCopyW") + hr = ScaWriteConfigString(psvd->wzWebName); //site name key + ExitOnFailure(hr, "Failed to write App site Name"); +#pragma prefast(suppress:26037, "Source string is null terminated - it is populated as target of ::StringCchCopyW") + hr = ScaWriteConfigString(wzPath); // App Path + ExitOnFailure(hr, "Failed to write app path root "); + hr = ScaWriteConfigString(L"NOP"); // App pool + ExitOnFailure(hr, "Failed to write app path app pool "); + } + else + { + //delete VDir + hr = ScaWriteConfigID(IIS_VDIR); + ExitOnFailure(hr, "Failed to write vDir ID "); + hr = ScaWriteConfigID(IIS_DELETE); +#pragma prefast(suppress:26037, "Source string is null terminated - it is populated as target of ::StringCchCopyW") + hr = ScaWriteConfigString(psvd->wzWebName); //site name key + ExitOnFailure(hr, "Failed to write App site Name"); + hr = ScaWriteConfigString(wzPath); // Vdir Path + ExitOnFailure(hr, "Failed to write app vdir "); + hr = ScaWriteConfigString(L"NOP"); // Phy Path + ExitOnFailure(hr, "Failed to write vdir path"); + } + + ExitOnFailure(hr, "Failed to remove VirtualDir '%ls' from config", psvd->wzKey); + } + + psvd = psvd->psvdNext; + } + +LExit: + ReleaseStr(wzPath); + return hr; +} + + +void ScaVirtualDirsFreeList7( + __in SCA_VDIR7* psvdList + ) +{ + SCA_VDIR7* psvdDelete = psvdList; + while (psvdList) + { + psvdDelete = psvdList; + psvdList = psvdList->psvdNext; + + if (psvdDelete->psmm) + { + ScaMimeMapFreeList(psvdDelete->psmm); + } + + if (psvdDelete->pswe) + { + ScaWebErrorFreeList(psvdDelete->pswe); + } + + MemFree(psvdDelete); + } +} + + +static HRESULT AddVirtualDirToList7( + __in SCA_VDIR7** ppsvdList + ) +{ + HRESULT hr = S_OK; + + SCA_VDIR7* psvd = static_cast(MemAlloc(sizeof(SCA_VDIR7), TRUE)); + ExitOnNull(psvd, hr, E_OUTOFMEMORY, "failed to allocate memory for new vdir list element"); + + psvd->psvdNext= *ppsvdList; + *ppsvdList = psvd; + +LExit: + return hr; +} diff --git a/src/ext/Iis/ca/scavdir7.h b/src/ext/Iis/ca/scavdir7.h new file mode 100644 index 00000000..dba25431 --- /dev/null +++ b/src/ext/Iis/ca/scavdir7.h @@ -0,0 +1,66 @@ +#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. + + +#include "scawebprop.h" +#include "scawebapp.h" +#include "scamimemap.h" +#include "scaapppool.h" + +struct SCA_VDIR7 +{ + // darwin information + WCHAR wzKey[MAX_DARWIN_KEY + 1]; + WCHAR wzComponent[MAX_DARWIN_KEY + 1]; + INSTALLSTATE isInstalled; + INSTALLSTATE isAction; + + // metabase information + WCHAR wzWebKey[MAX_DARWIN_KEY + 1]; + WCHAR wzWebName[METADATA_MAX_NAME_LEN + 1]; + WCHAR wzVDirRoot[METADATA_MAX_NAME_LEN + 1]; + + // iis configuation information + WCHAR wzDirectory[MAX_PATH]; + + BOOL fHasProperties; + SCA_WEB_PROPERTIES swp; + + BOOL fHasApplication; + SCA_WEB_APPLICATION swapp; + + SCA_MIMEMAP* psmm; // mime mappings + SCA_HTTP_HEADER* pshh; // custom web errors + SCA_WEB_ERROR* pswe; // custom web errors + + SCA_VDIR7* psvdNext; +}; + + +// prototypes +HRESULT __stdcall ScaVirtualDirsRead7( + __in SCA_WEB7* pswList, + __in SCA_VDIR7** ppsvdList, + __in SCA_MIMEMAP** ppsmmList, + __in SCA_HTTP_HEADER** ppshhList, + __in SCA_WEB_ERROR** ppsweList, + __in WCA_WRAPQUERY_HANDLE hUserQuery, + __in WCA_WRAPQUERY_HANDLE hWebBaseQuery, + __in WCA_WRAPQUERY_HANDLE hWebDirPropQuery, + __in WCA_WRAPQUERY_HANDLE hWebAppQuery, + __in WCA_WRAPQUERY_HANDLE hWebAppExtQuery, + __inout LPWSTR *ppwzCustomActionData + ); + +HRESULT ScaVirtualDirsInstall7( + __in SCA_VDIR7* psvdList, + __in SCA_APPPOOL * psapList + ); + +HRESULT ScaVirtualDirsUninstall7( + __in SCA_VDIR7* psvdList + ); + +void ScaVirtualDirsFreeList7( + __in SCA_VDIR7* psvdList + ); diff --git a/src/ext/Iis/ca/scaweb.cpp b/src/ext/Iis/ca/scaweb.cpp new file mode 100644 index 00000000..0452fb0b --- /dev/null +++ b/src/ext/Iis/ca/scaweb.cpp @@ -0,0 +1,1187 @@ +// 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" + +//Adding this because delivery doesn't have the updated specstrings.h that windows build does +#ifndef __in_xcount +#define __in_xcount(size) +#endif + +// sql queries + +enum eWebBaseQuery { wbqWeb = 1, wbqId, wbqIP, wbqPort, wbqHeader, wbqSecure, wbqDescription }; + + +// prototypes for private helper functions +static SCA_WEB* NewWeb(); +static void FreeWeb(SCA_WEB *pswDelete); +static SCA_WEB* AddWebToList( + __in SCA_WEB* pswList, + __in SCA_WEB* psw + ); +static HRESULT ScaWebFindBase( + __in IMSAdminBase* piMetabase, + __in SCA_WEB* pswList, + __in_z LPCWSTR wzWeb, + __in int iSiteId, + __in_z LPCWSTR wzIP, + __in int iPort, + __in_z LPCWSTR wzHeader, + __in BOOL fSecure, + __in_z LPCWSTR wzDescription, + __out_ecount(cchWebBase) LPWSTR wzWebBase, + __in DWORD cchWebBase + ); +static HRESULT ScaWebFindFreeBase( + __in IMSAdminBase* piMetabase, + __in_xcount(unknown) SCA_WEB* pswList, + __in int iSiteId, + __in_z LPCWSTR wzDescription, + __out_ecount(cchWebBase) LPWSTR wzWebBase, + __in DWORD cchWebBase + ); +static HRESULT ScaWebWrite( + __in IMSAdminBase* piMetabase, + __in SCA_WEB* psw, + __in SCA_APPPOOL * psapList + ); +static HRESULT ScaWebRemove( + __in IMSAdminBase* piMetabase, + __in const SCA_WEB* psw); +static DWORD SiteIdFromDescription( + __in_z LPCWSTR wzDescription + ); +static void Sort( + __in_ecount(cArray) DWORD dwArray[], + __in int cArray + ); + + +HRESULT ScaWebsRead( + __in IMSAdminBase* piMetabase, + __in SCA_MIMEMAP** ppsmmList, + __in SCA_WEB** ppswList, + __in SCA_HTTP_HEADER** ppshhList, + __in SCA_WEB_ERROR** ppsweList, + __in WCA_WRAPQUERY_HANDLE hUserQuery, + __in WCA_WRAPQUERY_HANDLE hWebDirPropQuery, + __in WCA_WRAPQUERY_HANDLE hSslCertQuery, + __in WCA_WRAPQUERY_HANDLE hWebLogQuery, + __in WCA_WRAPQUERY_HANDLE hWebAppQuery, + __in WCA_WRAPQUERY_HANDLE hWebAppExtQuery, + __inout LPWSTR *ppwzCustomActionData + ) +{ + Assert(piMetabase && ppswList); + + HRESULT hr = S_OK; + + MSIHANDLE hRec; + MSIHANDLE hRecAddresses; + + SCA_WEB* psw = NULL; + LPWSTR pwzData = NULL; + int iSiteId; + + DWORD dwLen = 0; + WCA_WRAPQUERY_HANDLE hQueryWebSite = NULL; + WCA_WRAPQUERY_HANDLE hQueryWebAddress = NULL; + + hr = WcaBeginUnwrapQuery(&hQueryWebSite, ppwzCustomActionData); + ExitOnFailure(hr, "Failed to unwrap query for ScaWebsRead"); + + hr = WcaBeginUnwrapQuery(&hQueryWebAddress, ppwzCustomActionData); + ExitOnFailure(hr, "Failed to unwrap query for ScaWebsRead"); + + if (0 == WcaGetQueryRecords(hQueryWebSite)) + { + WcaLog(LOGMSG_VERBOSE, "Required tables not present"); + ExitFunction1(hr = S_FALSE); + } + + // loop through all the webs + while (S_OK == (hr = WcaFetchWrappedRecord(hQueryWebSite, &hRec))) + { + psw = NewWeb(); + ExitOnNull(psw, hr, E_OUTOFMEMORY, "Failed to allocate memory for web object in memory"); + + // get the darwin information + hr = WcaGetRecordString(hRec, wqWeb, &pwzData); + ExitOnFailure(hr, "Failed to get Web"); + hr = ::StringCchCopyW(psw->wzKey, countof(psw->wzKey), pwzData); + ExitOnFailure(hr, "Failed to copy key string to web object"); + + if (*pwzData && *ppsmmList) + { + hr = ScaGetMimeMap(mmptWeb, pwzData, ppsmmList, &psw->psmm); + ExitOnFailure(hr, "Failed to get mimemap for VirtualDir"); + } + + // get component install state + hr = WcaGetRecordString(hRec, wqComponent, &pwzData); + ExitOnFailure(hr, "Failed to get Component for Web"); + hr = ::StringCchCopyW(psw->wzComponent, countof(psw->wzComponent), pwzData); + ExitOnFailure(hr, "Failed to copy component string to web object"); + if (*(psw->wzComponent)) + { + psw->fHasComponent = TRUE; + + hr = WcaGetRecordInteger(hRec, wqInstalled, (int *)&psw->isInstalled); + ExitOnFailure(hr, "Failed to get web Component's installed state"); + + WcaGetRecordInteger(hRec, wqAction, (int *)&psw->isAction); + ExitOnFailure(hr, "Failed to get web Component's action state"); + + if (!WcaIsInstalling(psw->isInstalled, psw->isAction) && !WcaIsUninstalling(psw->isInstalled, psw->isAction) + && !WcaIsReInstalling(psw->isInstalled, psw->isAction)) + { + FreeWeb(psw); + psw = NULL; + + continue; // If we aren't acting on this component, skip it + } + } + + hr = WcaGetRecordInteger(hRec, wqId, &iSiteId); + ExitOnFailure(hr, "Failed to get SiteId for Web"); + + // Get the web's key address. + hr = WcaGetRecordString(hRec, wqAddress, &pwzData); + ExitOnFailure(hr, "Failed to get Address for Web"); + hr = ::StringCchCopyW(psw->swaKey.wzKey, countof(psw->swaKey.wzKey), pwzData); + ExitOnFailure(hr, "Failed to copy key string to web object"); + + hr = WcaGetRecordString(hRec, wqIP, &pwzData); + ExitOnFailure(hr, "Failed to get IP for Web"); + hr = ::StringCchCopyW(psw->swaKey.wzIP, countof(psw->swaKey.wzIP), pwzData); + ExitOnFailure(hr, "Failed to copy IP string to web object"); + + hr = WcaGetRecordString(hRec, wqPort, &pwzData); + ExitOnFailure(hr, "Failed to get Web Address port"); + psw->swaKey.iPort = wcstol(pwzData, NULL, 10); + + hr = WcaGetRecordString(hRec, wqHeader, &pwzData); + ExitOnFailure(hr, "Failed to get Header for Web"); + hr = ::StringCchCopyW(psw->swaKey.wzHeader, countof(psw->swaKey.wzHeader), pwzData); + ExitOnFailure(hr, "Failed to copy header string to web object"); + + hr = WcaGetRecordInteger(hRec, wqSecure, &psw->swaKey.fSecure); + ExitOnFailure(hr, "Failed to get if Web is secure"); + if (S_FALSE == hr) + { + psw->swaKey.fSecure = FALSE; + } + + // Get the web's description. + hr = WcaGetRecordString(hRec, wqDescription, &pwzData); + ExitOnFailure(hr, "Failed to get Description for Web"); + hr = ::StringCchCopyW(psw->wzDescription, countof(psw->wzDescription), pwzData); + ExitOnFailure(hr, "Failed to copy description string to web object"); + + // Try to find the web root in case it already exists. + dwLen = METADATA_MAX_NAME_LEN; + hr = ScaWebFindBase(piMetabase, *ppswList, + psw->wzKey, + iSiteId, + psw->swaKey.wzIP, + psw->swaKey.iPort, + psw->swaKey.wzHeader, + psw->swaKey.fSecure, + psw->wzDescription, + psw->wzWebBase, dwLen); + if (S_OK == hr) + { + psw->fBaseExists = TRUE; + } + else if (S_FALSE == hr) // didn't find the web site. + { + psw->fBaseExists = FALSE; + + // If we're actually configuring the web site. + if (psw->fHasComponent) + { + if (WcaIsInstalling(psw->isInstalled, psw->isAction)) + { + hr = ScaWebFindFreeBase(piMetabase, *ppswList, iSiteId, psw->wzDescription, psw->wzWebBase, countof(psw->wzWebBase)); + ExitOnFailure(hr, "Failed to find free web root."); + } + else if (WcaIsUninstalling(psw->isInstalled, psw->isAction)) + { + WcaLog(LOGMSG_VERBOSE, "Web site: '%ls' was already removed, skipping.", psw->wzKey); + + hr = S_OK; + continue; + } + } + } + ExitOnFailure(hr, "Failed to find web root"); + + // get any extra web addresses + WcaFetchWrappedReset(hQueryWebAddress); + while (S_OK == (hr = WcaFetchWrappedRecordWhereString(hQueryWebAddress, 2, psw->wzKey, &hRecAddresses))) + { + if (MAX_ADDRESSES_PER_WEB <= psw->cExtraAddresses) + { + hr = HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER); + ExitOnFailure(hr, "Failure to get more extra web addresses, max exceeded."); + } + + hr = WcaGetRecordString(hRecAddresses, waqAddress, &pwzData); + ExitOnFailure(hr, "Failed to get extra web Address"); + + // if this isn't the key address add it + if (0 != lstrcmpW(pwzData, psw->swaKey.wzKey)) + { + hr = ::StringCchCopyW(psw->swaExtraAddresses[psw->cExtraAddresses].wzKey, countof(psw->swaExtraAddresses[psw->cExtraAddresses].wzKey), pwzData); + ExitOnFailure(hr, "Failed to copy extra addresses key string to web object"); + + hr = WcaGetRecordString(hRecAddresses, waqIP, &pwzData); + ExitOnFailure(hr, "Failed to get extra web IP"); + hr = ::StringCchCopyW(psw->swaExtraAddresses[psw->cExtraAddresses].wzIP, countof(psw->swaExtraAddresses[psw->cExtraAddresses].wzIP), pwzData); + ExitOnFailure(hr, "Failed to copy extra addresses IP string to web object"); + + hr = WcaGetRecordString(hRecAddresses, waqPort, &pwzData); + ExitOnFailure(hr, "Failed to get port for extra web IP"); + psw->swaExtraAddresses[psw->cExtraAddresses].iPort= wcstol(pwzData, NULL, 10); + + hr = WcaGetRecordString(hRecAddresses, waqHeader, &pwzData); + ExitOnFailure(hr, "Failed to get header for extra web IP"); + hr = ::StringCchCopyW(psw->swaExtraAddresses[psw->cExtraAddresses].wzHeader, countof(psw->swaExtraAddresses[psw->cExtraAddresses].wzHeader), pwzData); + ExitOnFailure(hr, "Failed to copy extra addresses header string to web object"); + + hr = WcaGetRecordInteger(hRecAddresses, waqSecure, &psw->swaExtraAddresses[psw->cExtraAddresses].fSecure); + ExitOnFailure(hr, "Failed to get if secure extra web IP"); + if (S_FALSE == hr) + psw->swaExtraAddresses[psw->cExtraAddresses].fSecure = FALSE; + + ++psw->cExtraAddresses; + } + } + + if (E_NOMOREITEMS == hr) + hr = S_OK; + ExitOnFailure(hr, "Failure occured while getting extra web addresses"); + + hr = WcaGetRecordInteger(hRec, wqConnectionTimeout, &psw->iConnectionTimeout); + ExitOnFailure(hr, "Failed to get connection timeout for Web"); + + if (psw->fHasComponent) // If we're installing it, it needs a dir + { + // get the web's directory + if (INSTALLSTATE_SOURCE == psw->isAction) + { + hr = WcaGetRecordString(hRec, wqSourcePath, &pwzData); + } + else + { + hr = WcaGetRecordString(hRec, wqTargetPath, &pwzData); + } + ExitOnFailure(hr, "Failed to get Source/TargetPath for Directory"); + + // remove trailing backslashes + while (lstrlenW(pwzData) > 0 && pwzData[lstrlenW(pwzData)-1] == L'\\') + { + pwzData[lstrlenW(pwzData)-1] = 0; + } + hr = ::StringCchCopyW(psw->wzDirectory, countof(psw->wzDirectory), pwzData); + ExitOnFailure(hr, "Failed to copy directory string to web object"); + } + + hr = WcaGetRecordInteger(hRec, wqState, &psw->iState); + ExitOnFailure(hr, "Failed to get state for Web"); + + hr = WcaGetRecordInteger(hRec, wqAttributes, &psw->iAttributes); + ExitOnFailure(hr, "Failed to get attributes for Web"); + + // get the dir properties for this web + hr = WcaGetRecordString(hRec, wqProperties, &pwzData); + ExitOnFailure(hr, "Failed to get directory property record for Web"); + if (*pwzData) + { + hr = ScaGetWebDirProperties(pwzData, hUserQuery, hWebDirPropQuery, &psw->swp); + ExitOnFailure(hr, "Failed to get directory properties for Web"); + + psw->fHasProperties = TRUE; + } + + // get the application information for this web + hr = WcaGetRecordString(hRec, wqApplication, &pwzData); + ExitOnFailure(hr, "Failed to get application identifier for Web"); + if (*pwzData) + { + hr = ScaGetWebApplication(NULL, pwzData, hWebAppQuery, hWebAppExtQuery, &psw->swapp); + ExitOnFailure(hr, "Failed to get application for Web"); + + psw->fHasApplication = TRUE; + } + + // get the SSL certificates + hr = ScaSslCertificateRead(psw->wzKey, hSslCertQuery, &(psw->pswscList)); + ExitOnFailure(hr, "Failed to get SSL Certificates."); + + // get the custom headers + if (*ppshhList) + { + hr = ScaGetHttpHeader(hhptWeb, psw->wzKey, ppshhList, &(psw->pshhList)); + ExitOnFailure(hr, "Failed to get Custom HTTP Headers"); + } + + // get the errors + if (*ppsweList) + { + hr = ScaGetWebError(weptWeb, psw->wzKey, ppsweList, &(psw->psweList)); + ExitOnFailure(hr, "Failed to get Custom Errors"); + } + + // get the log information for this web + hr = WcaGetRecordString(hRec, wqLog, &pwzData); + ExitOnFailure(hr, "Failed to get log identifier for Web"); + if (*pwzData) + { + hr = ScaGetWebLog(piMetabase, pwzData, hWebLogQuery, &psw->swl); + ExitOnFailure(hr, "Failed to get Log for Web."); + + psw->fHasLog = TRUE; + } + + *ppswList = AddWebToList(*ppswList, psw); + psw = NULL; // set the web NULL so it doesn't accidentally get freed below + } + + if (E_NOMOREITEMS == hr) + { + hr = S_OK; + } + +LExit: + // if anything was left over after an error clean it all up + WcaFinishUnwrapQuery(hQueryWebSite); + WcaFinishUnwrapQuery(hQueryWebAddress); + + if (psw) + { + ScaWebsFreeList(psw); + } + + ReleaseStr(pwzData); + + return hr; +} + + +HRESULT ScaWebsGetBase( + __in IMSAdminBase* piMetabase, + __in SCA_WEB* pswList, + __in LPCWSTR wzWeb, + __out_ecount(cchWebBase) LPWSTR wzWebBase, + __in DWORD cchWebBase, + __in WCA_WRAPQUERY_HANDLE hWrapQuery + ) +{ + HRESULT hr = S_OK; + MSIHANDLE hRec; + + int iSiteId; + WCHAR wzIP[MAX_PATH]; + int iPort = -1; + WCHAR wzHeader[MAX_PATH]; + BOOL fSecure = FALSE; + + LPWSTR pwzData = NULL; + + // get the web information + WcaFetchWrappedReset(hWrapQuery); + + hr = WcaFetchWrappedRecordWhereString(hWrapQuery, 1, wzWeb, &hRec); + if (S_OK == hr) + { + // get the data to search for + hr = WcaGetRecordInteger(hRec, wbqId, &iSiteId); + ExitOnFailure(hr, "Failed to get SiteId for Web for VirtualDir"); + + hr = WcaGetRecordString(hRec, wbqIP, &pwzData); + ExitOnFailure(hr, "Failed to get IP for Web for VirtualDir"); + hr = ::StringCchCopyW(wzIP, countof(wzIP), pwzData); + ExitOnFailure(hr, "Failed to copy IP for Web for VirtualDir"); + + hr = WcaGetRecordString(hRec, wbqPort, &pwzData); + ExitOnFailure(hr, "Failed to get port for extra web IP"); + iPort = wcstol(pwzData, NULL, 10); + + hr = WcaGetRecordString(hRec, wbqHeader, &pwzData); + ExitOnFailure(hr, "Failed to get Header for Web for VirtualDir"); + hr = ::StringCchCopyW(wzHeader, countof(wzHeader), pwzData); + ExitOnFailure(hr, "Failed to copy Header for Web for VirtualDir"); + + hr = WcaGetRecordInteger(hRec, wbqSecure, &fSecure); + if (S_FALSE == hr) + fSecure = FALSE; + + hr = WcaGetRecordString(hRec, wbqDescription, &pwzData); + ExitOnFailure(hr, "Failed to get Description for Web"); + + // find the web or find the next free web location + hr = ScaWebFindBase(piMetabase, pswList, wzWeb, iSiteId, wzIP, iPort, wzHeader, fSecure, pwzData, wzWebBase, cchWebBase); + if (S_FALSE == hr) + hr = HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND); + ExitOnFailure(hr, "Failed to find Web base"); + } + else if (S_FALSE == hr) + hr = HRESULT_FROM_WIN32(ERROR_NOT_FOUND); + + // Let's check that there isn't more than one record found - if there is, throw an assert like WcaFetchSingleRecord() would + HRESULT hrTemp = WcaFetchWrappedRecordWhereString(hWrapQuery, 1, wzWeb, &hRec); + if (SUCCEEDED(hrTemp)) + { + AssertSz(E_NOMOREITEMS == hrTemp, "ScaWebsGetBase found more than one record"); + } + +LExit: + ReleaseStr(pwzData); + + return hr; +} + + +HRESULT ScaWebsInstall( + __in IMSAdminBase* piMetabase, + __in SCA_WEB* pswList, + __in SCA_APPPOOL * psapList + ) +{ + HRESULT hr = S_OK; + SCA_WEB* psw = pswList; + + while (psw) + { + // if we are installing the web site + if (psw->fHasComponent && WcaIsInstalling(psw->isInstalled, psw->isAction)) + { + hr = ScaWebWrite(piMetabase, psw, psapList); + ExitOnFailure(hr, "failed to write web '%ls' to metabase", psw->wzKey); + } + + psw = psw->pswNext; + } + +LExit: + return hr; +} + + +HRESULT ScaWebsUninstall( + __in IMSAdminBase* piMetabase, + __in SCA_WEB* pswList + ) +{ + HRESULT hr = S_OK; + SCA_WEB* psw = pswList; + + while (psw) + { + // if we are uninstalling the web site + if (psw->fHasComponent && WcaIsUninstalling(psw->isInstalled, psw->isAction)) + { + hr = ScaWebRemove(piMetabase, psw); + ExitOnFailure(hr, "Failed to remove web '%ls' from metabase", psw->wzKey); + } + + psw = psw->pswNext; + } + +LExit: + return hr; +} + + +void ScaWebsFreeList( + __in SCA_WEB* pswList + ) +{ + SCA_WEB* pswDelete = pswList; + while (pswList) + { + pswDelete = pswList; + pswList = pswList->pswNext; + + // Free the SSL, headers and errors list first + FreeWeb(pswDelete); + } +} + + +// private helper functions + +static void FreeWeb(SCA_WEB *pswDelete) +{ + ScaSslCertificateFreeList(pswDelete->pswscList); + ScaHttpHeaderFreeList(pswDelete->pshhList); + ScaWebErrorFreeList(pswDelete->psweList); + MemFree(pswDelete); +} + +static SCA_WEB* NewWeb() +{ + SCA_WEB* psw = static_cast(MemAlloc(sizeof(SCA_WEB), TRUE)); + Assert(psw); + return psw; +} + + +static SCA_WEB* AddWebToList( + __in SCA_WEB* pswList, + __in SCA_WEB* psw + ) +{ + if (pswList) + { + SCA_WEB* pswTemp = pswList; + while (pswTemp->pswNext) + { + pswTemp = pswTemp->pswNext; + } + + pswTemp->pswNext = psw; + } + else + { + pswList = psw; + } + + return pswList; +} + + +static HRESULT ScaWebFindBase( + __in IMSAdminBase* piMetabase, + __in SCA_WEB* pswList, + __in_z LPCWSTR wzWeb, + __in int iSiteId, + __in_z LPCWSTR wzIP, + __in int iPort, + __in_z LPCWSTR wzHeader, + __in BOOL fSecure, + __in_z LPCWSTR wzDescription, + __out_ecount(cchWebBase) LPWSTR wzWebBase, + __in DWORD cchWebBase + ) +{ + Assert(piMetabase); + + HRESULT hr = S_OK; + + WCHAR wzKey[METADATA_MAX_NAME_LEN]; + WCHAR wzSubkey[METADATA_MAX_NAME_LEN]; + LPWSTR wzFoundKey = NULL; + + METADATA_RECORD mr; + ::ZeroMemory(&mr, sizeof(mr)); + + METADATA_RECORD mrCompare; + ::ZeroMemory(&mrCompare, sizeof(mrCompare)); + + // try to find the web in memory first + for (SCA_WEB* psw = pswList; psw; psw = psw->pswNext) + { + if (0 == lstrcmpW(wzWeb, psw->wzKey)) + { + if ((0 == lstrcmpW(wzIP, psw->swaKey.wzIP) || 0 == lstrcmpW(wzIP, L"*") || 0 == lstrcmpW(psw->swaKey.wzIP, L"*")) && + iPort == psw->swaKey.iPort && + 0 == lstrcmpW(wzHeader, psw->swaKey.wzHeader) && + fSecure == psw->swaKey.fSecure) + { + if (0 == lstrlenW(psw->wzWebBase)) + { + WcaLog(LOGMSG_STANDARD, "A matching web object in memory was found, but the web object in memory has no associated base"); + ExitFunction1(hr = S_FALSE); + } + + hr = ::StringCchCopyW(wzKey, countof(wzKey), psw->wzWebBase); + ExitOnFailure(hr, "Failed to copy web base into key."); + + wzFoundKey = wzKey; + break; + } + else + { + WcaLog(LOGMSG_STANDARD, "Found web `%ls` but data did not match.", wzWeb); + hr = E_UNEXPECTED; + break; + } + } + } + ExitOnFailure(hr, "Failure occured while searching for web in memory"); + + // If we didn't find a web in memory matching look in the metabase. + if (!wzFoundKey) + { + mr.dwMDIdentifier = MD_KEY_TYPE; + mr.dwMDAttributes = METADATA_INHERIT; + mr.dwMDUserType = IIS_MD_UT_SERVER; + mr.dwMDDataType = ALL_METADATA; + mr.dwMDDataLen = 0; + mr.pbMDData = NULL; + + // If we're looking based on the old WebAddress detection (NULL) or the new + // Web description detection (-1) + if (MSI_NULL_INTEGER == iSiteId || -1 == iSiteId) + { + if (MSI_NULL_INTEGER == iSiteId) + { + mrCompare.dwMDIdentifier = (fSecure) ? MD_SECURE_BINDINGS : MD_SERVER_BINDINGS; + } + else + { + mrCompare.dwMDIdentifier = MD_SERVER_COMMENT; + } + mrCompare.dwMDAttributes = METADATA_INHERIT; + mrCompare.dwMDUserType = IIS_MD_UT_SERVER; + mrCompare.dwMDDataType = ALL_METADATA; + mrCompare.dwMDDataLen = 0; + mrCompare.pbMDData = NULL; + + // Loop through the "web keys" looking for the "IIsWebServer" key that matches our search criteria. + for (DWORD dwIndex = 0; SUCCEEDED(hr); ++dwIndex) + { + hr = piMetabase->EnumKeys(METADATA_MASTER_ROOT_HANDLE, L"/LM/W3SVC", wzSubkey, dwIndex); + if (SUCCEEDED(hr)) + { + hr = ::StringCchPrintfW(wzKey, countof(wzKey), L"/LM/W3SVC/%s", wzSubkey); + ExitOnFailure(hr, "Failed to copy web subkey."); + + hr = MetaGetValue(piMetabase, METADATA_MASTER_ROOT_HANDLE, wzKey, &mr); + if (MD_ERROR_DATA_NOT_FOUND == hr || HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND) == hr) + { + hr = S_FALSE; // didn't find anything, try next one + continue; + } + ExitOnFailure(hr, "Failed to get key from metabase while searching for web servers"); + + // If we have an IIsWebServer check to see if this is the site we are searching for. + if (0 == lstrcmpW(L"IIsWebServer", (LPCWSTR)mr.pbMDData)) + { + hr = MetaGetValue(piMetabase, METADATA_MASTER_ROOT_HANDLE, wzKey, &mrCompare); + if (MD_ERROR_DATA_NOT_FOUND == hr || NULL == mrCompare.pbMDData) + { + hr = S_FALSE; + continue; + } + ExitOnFailure(hr, "Failed to get address/description from metabase while searching for web servers"); + + LPWSTR pwzExists = (LPWSTR)mrCompare.pbMDData; + if (MSI_NULL_INTEGER == iSiteId) + { + // Break down the address into its constituent parts (IP:Port:Header). + while (S_OK == hr && *pwzExists) + { + LPCWSTR pwzIPExists = pwzExists; + pwzExists = const_cast(wcsstr(pwzIPExists, L":")); + ExitOnNull(pwzExists, hr, E_INVALIDARG, "Invalid web address. IP was not separated by a colon."); + *pwzExists = L'\0'; + + LPCWSTR pwzPortExists = pwzExists + 1; + pwzExists = const_cast(wcsstr(pwzPortExists, L":")); + ExitOnNull(pwzExists, hr, E_INVALIDARG, "Invalid web address. Port was not separated by a colon."); + *pwzExists = L'\0'; + int iPortExists = wcstol(pwzPortExists, NULL, 10); + + LPCWSTR pwzHeaderExists = pwzExists + 1; + + // compare the passed in address with the address listed for this web + if ((0 == lstrcmpW(wzIP, pwzIPExists) || 0 == lstrcmpW(wzIP, L"*")) && + iPort == iPortExists && + 0 == lstrcmpW(wzHeader, pwzHeaderExists)) + { + wzFoundKey = wzKey; + break; + } + + // move to the next block of data, this may move beyond the available + // data and exit the while loop above. + pwzExists = const_cast(pwzHeaderExists + lstrlenW(pwzHeaderExists) + 1); + } + } + else + { + if (0 == lstrcmpW(wzDescription, pwzExists)) + { + wzFoundKey = wzKey; + } + } + + // If we found the key we were looking for, no point in continuing to search. + if (wzFoundKey) + { + break; + } + } + } + } + + if (E_NOMOREITEMS == hr) + { + Assert(!wzFoundKey); + hr = S_FALSE; + } + } + else // we're looking a specific SiteId + { + hr = ::StringCchPrintfW(wzKey, countof(wzKey), L"/LM/W3SVC/%d", iSiteId); + ExitOnFailure(hr, "Failed to allocate metabase key string."); + + // if we have an IIsWebServer store the key + hr = MetaGetValue(piMetabase, METADATA_MASTER_ROOT_HANDLE, wzKey, &mr); + + if (SUCCEEDED(hr) && NULL != mr.pbMDData && 0 == lstrcmpW(L"IIsWebServer", (LPCWSTR)mr.pbMDData)) + { + wzFoundKey = wzKey; + } + else if (MD_ERROR_DATA_NOT_FOUND == hr || NULL == mrCompare.pbMDData || NULL == mr.pbMDData) + { + hr = S_FALSE; + } + } + } + + if (wzFoundKey) + { + Assert(S_OK == hr); + + hr = ::StringCchCopyW(wzWebBase, cchWebBase, wzFoundKey); + ExitOnFailure(hr, "Buffer not larger enough for found base key."); + } + +LExit: + MetaFreeValue(&mr); + MetaFreeValue(&mrCompare); + + if (!wzFoundKey && SUCCEEDED(hr)) + { + hr = S_FALSE; + } + + return hr; +} + + +static HRESULT ScaWebFindFreeBase( + __in IMSAdminBase* piMetabase, + __in_xcount(unknown) SCA_WEB* pswList, + __in int iSiteId, + __in_z LPCWSTR wzDescription, + __out_ecount(cchWebBase) LPWSTR wzWebBase, + __in DWORD cchWebBase + ) +{ + Assert(piMetabase); + + HRESULT hr = S_OK; + + WCHAR wzKey[METADATA_MAX_NAME_LEN]; + WCHAR wzSubkey[METADATA_MAX_NAME_LEN]; + DWORD* prgdwSubKeys = NULL; + DWORD cSubKeys = 128; + DWORD cSubKeysFilled = 0; + + DWORD dwKey; + + METADATA_RECORD mr; + ::ZeroMemory(&mr, sizeof(METADATA_RECORD)); + mr.dwMDIdentifier = MD_KEY_TYPE; + mr.dwMDAttributes = 0; + mr.dwMDUserType = IIS_MD_UT_SERVER; + mr.dwMDDataType = STRING_METADATA; + mr.dwMDDataLen = 0; + mr.pbMDData = NULL; + + if (MSI_NULL_INTEGER == iSiteId || -1 == iSiteId) + { + prgdwSubKeys = static_cast(MemAlloc(cSubKeys * sizeof(DWORD), TRUE)); + ExitOnNull(prgdwSubKeys, hr, E_OUTOFMEMORY, "failed to allocate space for web site keys"); + + // loop through the "web keys" looking for the "IIsWebServer" key that matches wzWeb + for (DWORD dwIndex = 0; SUCCEEDED(hr); ++dwIndex) + { + hr = piMetabase->EnumKeys(METADATA_MASTER_ROOT_HANDLE, L"/LM/W3SVC", wzSubkey, dwIndex); + if (SUCCEEDED(hr)) + { + hr = ::StringCchPrintfW(wzKey, countof(wzKey), L"/LM/W3SVC/%s", wzSubkey); + ExitOnFailure(hr, "Failed remember key."); + + hr = MetaGetValue(piMetabase, METADATA_MASTER_ROOT_HANDLE, wzKey, &mr); + if (MD_ERROR_DATA_NOT_FOUND == hr || HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND) == hr) + { + hr = S_FALSE; // didn't find anything, try next one + continue; + } + ExitOnFailure(hr, "Failed to get key from metabase while searching for free web root"); + + // if we have a IIsWebServer get the address information + if (0 == lstrcmpW(L"IIsWebServer", (LPCWSTR)mr.pbMDData)) + { + if (cSubKeysFilled >= cSubKeys) + { + cSubKeys = cSubKeys * 2; + prgdwSubKeys = static_cast(MemReAlloc(prgdwSubKeys, cSubKeys * sizeof(DWORD), FALSE)); + ExitOnNull(prgdwSubKeys, hr, E_OUTOFMEMORY, "failed to allocate space for web site keys"); + } + + prgdwSubKeys[cSubKeysFilled] = wcstol(wzSubkey, NULL, 10); + ++cSubKeysFilled; + Sort(prgdwSubKeys, cSubKeysFilled); + } + } + } + + if (E_NOMOREITEMS == hr) + { + hr = S_OK; + } + ExitOnFailure(hr, "Failed to find free web root"); + + // Add all the webs created in memory. + CONST WCHAR *pcchSlash; + for (SCA_WEB* psw = pswList; psw; psw = psw->pswNext) + { + // Don't process webs that don't have a base + if (!*psw->wzWebBase) + { + continue; + } + + // find the last slash in the web root because the root # is after it + pcchSlash = NULL; + for (CONST WCHAR *pcch = psw->wzWebBase; pcch && *pcch; ++pcch) + { + if (L'/' == *pcch) + { + pcchSlash = pcch; + } + } + // In case we don't find a slash, error out + ExitOnNull(pcchSlash, hr, E_INVALIDARG, "Failed to find a slash in the web root: %ls", psw->wzWebBase); + + prgdwSubKeys[cSubKeysFilled] = wcstol(pcchSlash + 1, NULL, 10); + ++cSubKeysFilled; + Sort(prgdwSubKeys, cSubKeysFilled); + + if (cSubKeysFilled >= cSubKeys) + { + cSubKeys = cSubKeys * 2; + prgdwSubKeys = static_cast(MemReAlloc(prgdwSubKeys, cSubKeys * sizeof(DWORD), FALSE)); + ExitOnNull(prgdwSubKeys, hr, E_OUTOFMEMORY, "failed to allocate space for web site keys"); + } + } + + // Find the lowest free web root. + dwKey = (-1 == iSiteId) ? SiteIdFromDescription(wzDescription) : 1; + for (DWORD i = 0; i < cSubKeysFilled; ++i) + { + if (dwKey == prgdwSubKeys[i]) + { + ++dwKey; + } + else if (dwKey < prgdwSubKeys[i]) + { + break; + } + } + } + else + { + dwKey = iSiteId; + } + + hr = ::StringCchPrintfW(wzWebBase, cchWebBase, L"/LM/W3SVC/%u", dwKey); + ExitOnFailure(hr, "failed to format web base with key: %u", dwKey); + +LExit: + MetaFreeValue(&mr); + + if (prgdwSubKeys) + { + MemFree(prgdwSubKeys); + } + + return hr; +} + + +static HRESULT ScaWebWrite( + __in IMSAdminBase* piMetabase, + __in SCA_WEB* psw, + __in SCA_APPPOOL * psapList) +{ + HRESULT hr = S_OK; + + UINT ui = 0; + WCHAR wzIP[64]; + WCHAR wzBindings[1024]; + WCHAR wzSecureBindings[1024]; + WCHAR* pcchNext; // used to properly create the MULTI_SZ + DWORD cchPcchNext; + WCHAR* pcchSecureNext ; // used to properly create the MULTI_SZ + DWORD cchPcchSecureNext; + + // if the web root doesn't exist create it + if (!psw->fBaseExists) + { + hr = ScaCreateWeb(piMetabase, psw->wzKey, psw->wzWebBase); + ExitOnFailure(hr, "Failed to create web"); + } + else if (psw->iAttributes & SWATTRIB_NOCONFIGUREIFEXISTS) // if we're not supposed to configure existing webs, bail + { + Assert(psw->fBaseExists); + + hr = S_FALSE; + WcaLog(LOGMSG_VERBOSE, "Skipping configuration of existing web: %ls", psw->wzKey); + ExitFunction(); + } + + // put the secure and non-secure bindings together as MULTI_SZs + ::ZeroMemory(wzBindings, sizeof(wzBindings)); + pcchNext = wzBindings; + cchPcchNext = countof(wzBindings); + ::ZeroMemory(wzSecureBindings, sizeof(wzSecureBindings)); + pcchSecureNext = wzSecureBindings; + cchPcchSecureNext = countof(wzSecureBindings); + + // set the IP address appropriately + if (0 == lstrcmpW(psw->swaKey.wzIP, L"*")) + { + ::ZeroMemory(wzIP, sizeof(wzIP)); + } + else + { + hr = ::StringCchCopyW(wzIP, countof(wzIP), psw->swaKey.wzIP); + ExitOnFailure(hr, "Failed to copy IP string"); + } + + WCHAR wzBinding[256]; + hr = ::StringCchPrintfW(wzBinding, countof(wzBinding), L"%s:%d:%s", wzIP, psw->swaKey.iPort, psw->swaKey.wzHeader); + ExitOnFailure(hr, "Failed to format IP:Port:Header binding string"); + if (psw->swaKey.fSecure) + { + hr = ::StringCchCopyW(pcchSecureNext, cchPcchSecureNext, wzBinding); + ExitOnFailure(hr, "Failed to copy binding string to securenext string"); + pcchSecureNext += lstrlenW(wzBinding) + 1; + cchPcchSecureNext -= lstrlenW(wzBinding) + 1; + } + else + { + hr = ::StringCchCopyW(pcchNext, cchPcchNext, wzBinding); + ExitOnFailure(hr, "Failed to copy binding string to next string"); + pcchNext += lstrlenW(wzBinding) + 1; + cchPcchNext -= lstrlenW(wzBinding) + 1; + } + + for (ui = 0; ui < psw->cExtraAddresses; ++ui) + { + // set the IP address appropriately + if (0 == lstrcmpW(psw->swaExtraAddresses[ui].wzIP, L"*")) + { + ::ZeroMemory(wzIP, sizeof(wzIP)); + } + else + { + hr = ::StringCchCopyW(wzIP, countof(wzIP), psw->swaExtraAddresses[ui].wzIP); + ExitOnFailure(hr, "Failed to copy extra addresses IP to IP string"); + } + + hr = ::StringCchPrintfW(wzBinding, countof(wzBinding), L"%s:%d:%s", wzIP, psw->swaExtraAddresses[ui].iPort, psw->swaExtraAddresses[ui].wzHeader); + ExitOnFailure(hr, "Failed to format IP:Port:Header binding string for extra address"); + if (psw->swaExtraAddresses[ui].fSecure) + { + hr = ::StringCchCopyW(pcchSecureNext, cchPcchSecureNext, wzBinding); + ExitOnFailure(hr, "Failed to copy binding string to securenext string for extra address"); + pcchSecureNext += lstrlenW(wzBinding) + 1; + cchPcchSecureNext -= lstrlenW(wzBinding) + 1; + } + else + { + hr = ::StringCchCopyW(pcchNext, cchPcchNext, wzBinding); + ExitOnFailure(hr, "Failed to copy binding string to next string for extra address"); + pcchNext += lstrlenW(wzBinding) + 1; + cchPcchNext -= lstrlenW(wzBinding) + 1; + } + } + + // Delete the existing secure bindings metabase value, as having one while SSLCertHash and SSLStoreName aren't both set correctly can result in + // 0x80070520 (ERROR_NO_SUCH_LOGON_SESSION) errors in some situations on IIS7. Clearing this value first and then setting it after the install has completed + // allows the two aforementioned properties to exist in an intermediate state without errors + hr = ScaDeleteMetabaseValue(piMetabase, psw->wzWebBase, L"", MD_SECURE_BINDINGS, MULTISZ_METADATA); + ExitOnFailure(hr, "Failed to temporarily delete secure bindings for Web"); + + // now write the bindings to the metabase + hr = ScaWriteMetabaseValue(piMetabase, psw->wzWebBase, L"", MD_SERVER_BINDINGS, METADATA_NO_ATTRIBUTES, IIS_MD_UT_SERVER, MULTISZ_METADATA, wzBindings); + ExitOnFailure(hr, "Failed to write server bindings for Web"); + + // write the target path for the web's directory to the metabase + hr = ScaWriteMetabaseValue(piMetabase, psw->wzWebBase, L"/Root", MD_VR_PATH, METADATA_INHERIT, IIS_MD_UT_FILE, STRING_METADATA, psw->wzDirectory); + ExitOnFailure(hr, "Failed to write virtual root path for Web"); + + // write the description for the web to the metabase + hr = ScaWriteMetabaseValue(piMetabase, psw->wzWebBase, L"", MD_SERVER_COMMENT, METADATA_INHERIT, IIS_MD_UT_SERVER, STRING_METADATA, psw->wzDescription); + ExitOnFailure(hr, "Failed to write description for Web"); + + ui = psw->iConnectionTimeout; + if (MSI_NULL_INTEGER != ui) + { + hr = ScaWriteMetabaseValue(piMetabase, psw->wzWebBase, L"", MD_CONNECTION_TIMEOUT, METADATA_NO_ATTRIBUTES, IIS_MD_UT_SERVER, DWORD_METADATA, (LPVOID)((DWORD_PTR)ui)); + ExitOnFailure(hr, "Failed to write connection timeout for Web"); + } + + ui = psw->iState; + if (MSI_NULL_INTEGER != ui) + { + if (2 == ui) + { + ui = 1; + hr = ScaWriteMetabaseValue(piMetabase, psw->wzWebBase, L"", MD_SERVER_AUTOSTART, METADATA_INHERIT, IIS_MD_UT_SERVER, DWORD_METADATA, (LPVOID)((DWORD_PTR)ui)); + ExitOnFailure(hr, "Failed to write auto start flag for Web"); + ui = 2; + } + + if (1 == ui || 2 == ui) + { + ui = 1; // start command + hr = ScaWriteMetabaseValue(piMetabase, psw->wzWebBase, L"", MD_SERVER_COMMAND, METADATA_NO_ATTRIBUTES, IIS_MD_UT_SERVER, DWORD_METADATA, (LPVOID)((DWORD_PTR)ui)); + ExitOnFailure(hr, "Failed to start Web"); + } + else if (0 == ui) + { + ui = 2; // stop command + hr = ScaWriteMetabaseValue(piMetabase, psw->wzWebBase, L"", MD_SERVER_COMMAND, METADATA_NO_ATTRIBUTES, IIS_MD_UT_SERVER, DWORD_METADATA, (LPVOID)((DWORD_PTR)ui)); + ExitOnFailure(hr, "Failed to stop Web"); + } + else + { + hr = E_UNEXPECTED; + ExitOnFailure(hr, "Unexpected value for Web State"); + } + } + + WCHAR wzRootOfWeb[METADATA_MAX_NAME_LEN]; + hr = ::StringCchPrintfW(wzRootOfWeb, countof(wzRootOfWeb), L"%s/Root", psw->wzWebBase); + ExitOnFailure(hr, "Failed to allocate WebBase/Root string for root of web"); + + // write the web dirproperties information + if (psw->fHasProperties) + { + hr = ScaWriteWebDirProperties(piMetabase, wzRootOfWeb, &psw->swp); + ExitOnFailure(hr, "Failed to write web security information to metabase"); + } + + // write the application information + if (psw->fHasApplication) + { + // On reinstall, we have to uninstall the old application, otherwise a duplicate will be created + if (WcaIsReInstalling(psw->isInstalled, psw->isAction)) + { + hr = ScaDeleteApp(piMetabase, wzRootOfWeb); + ExitOnFailure(hr, "Failed to remove application for WebDir as part of a reinstall"); + } + + hr = ScaWriteWebApplication(piMetabase, wzRootOfWeb, &psw->swapp, psapList); + ExitOnFailure(hr, "Failed to write web application information to metabase"); + } + + // write the SSL certificate information + if (psw->pswscList) + { + hr = ScaSslCertificateWriteMetabase(piMetabase, psw->wzWebBase, psw->pswscList); + ExitOnFailure(hr, "Failed to write SSL certificates for Web site: %ls", psw->wzKey); + } + + hr = ScaWriteMetabaseValue(piMetabase, psw->wzWebBase, L"", MD_SECURE_BINDINGS, METADATA_NO_ATTRIBUTES, IIS_MD_UT_SERVER, MULTISZ_METADATA, wzSecureBindings); + ExitOnFailure(hr, "Failed to write secure bindings for Web"); + + // write the headers + if (psw->pshhList) + { + hr = ScaWriteHttpHeader(piMetabase, wzRootOfWeb, psw->pshhList); + ExitOnFailure(hr, "Failed to write custom HTTP headers for Web site: %ls", psw->wzKey); + } + + // write the errors + if (psw->psweList) + { + hr = ScaWriteWebError(piMetabase, weptWeb, psw->wzWebBase, psw->psweList); + ExitOnFailure(hr, "Failed to write custom web errors for Web site: %ls", psw->wzKey); + } + + // write the mimetypes + if (psw->psmm) + { + hr = ScaWriteMimeMap(piMetabase, wzRootOfWeb, psw->psmm); + ExitOnFailure(hr, "Failed to write mimemap for Web site: %ls", psw->wzKey); + } + + // write the log information to the metabase + if (psw->fHasLog) + { + hr = ScaWriteWebLog(piMetabase, psw->wzWebBase, &psw->swl); + ExitOnFailure(hr, "Failed to write web log information to metabase"); + } + +LExit: + return hr; +} + + +static HRESULT ScaWebRemove( + __in IMSAdminBase* piMetabase, + __in const SCA_WEB* psw + ) +{ + HRESULT hr = S_OK; + + // simply remove the root key and everything else is pulled at the same time + hr = ScaDeleteMetabaseKey(piMetabase, psw->wzWebBase, L""); + ExitOnFailure(hr, "Failed to remove web '%ls' from metabase", psw->wzKey); + +LExit: + return hr; +} + + +static DWORD SiteIdFromDescription( + __in_z LPCWSTR wzDescription + ) +{ + LPCWSTR pwz = wzDescription; + DWORD dwSiteId = 0; + while (pwz && *pwz) + { + WCHAR ch = *pwz & 0xdf; + dwSiteId = (dwSiteId * 101) + ch; + ++pwz; + } + + return (dwSiteId % INT_MAX) + 1; +} + + +// insertion sort +static void Sort( + __in_ecount(cArray) DWORD dwArray[], + __in int cArray + ) +{ + int i, j; + DWORD dwData; + + for (i = 1; i < cArray; ++i) + { + dwData = dwArray[i]; + + j = i - 1; + while (0 <= j && dwArray[j] > dwData) + { + dwArray[j + 1] = dwArray[j]; + j--; + } + + dwArray[j + 1] = dwData; + } +} diff --git a/src/ext/Iis/ca/scaweb.h b/src/ext/Iis/ca/scaweb.h new file mode 100644 index 00000000..294030b4 --- /dev/null +++ b/src/ext/Iis/ca/scaweb.h @@ -0,0 +1,123 @@ +#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. + + +#include "scawebapp.h" +#include "scawebprop.h" +#include "scahttpheader.h" +#include "scaweberr.h" +#include "scassl.h" +#include "scaapppool.h" +#include "scaweblog.h" +#include "scamimemap.h" + +// globals +#define MAX_ADDRESSES_PER_WEB 10 + +enum eWebQuery { wqWeb = 1, wqComponent, wqId, wqDescription, wqConnectionTimeout, wqDirectory, + wqState, wqAttributes, wqProperties, wqApplication, wqAddress, wqIP, wqPort, wqHeader, wqSecure, wqLog, wqInstalled, wqAction, wqSourcePath, wqTargetPath}; + +enum eWebAddressQuery { waqAddress = 1, waqWeb, waqIP, waqPort, waqHeader, waqSecure }; + +enum SCA_WEB_ATTRIBUTES +{ + SWATTRIB_NOCONFIGUREIFEXISTS = 2 +}; + +// structs +struct SCA_WEB_ADDRESS +{ + WCHAR wzKey [MAX_DARWIN_KEY + 1]; + + WCHAR wzIP[MAX_DARWIN_COLUMN + 1]; + int iPort; + WCHAR wzHeader[MAX_DARWIN_COLUMN + 1]; + BOOL fSecure; +}; + +struct SCA_WEB +{ + // darwin information + WCHAR wzKey[MAX_DARWIN_KEY + 1]; + WCHAR wzComponent[MAX_DARWIN_KEY + 1]; + BOOL fHasComponent; + INSTALLSTATE isInstalled; + INSTALLSTATE isAction; + + // metabase information + WCHAR wzWebBase[METADATA_MAX_NAME_LEN + 1]; + BOOL fBaseExists; + + // iis configuation information + SCA_WEB_ADDRESS swaKey; + + SCA_WEB_ADDRESS swaExtraAddresses[MAX_ADDRESSES_PER_WEB + 1]; + DWORD cExtraAddresses; + + WCHAR wzDirectory[MAX_PATH]; + WCHAR wzDescription[MAX_DARWIN_COLUMN + 1]; + + int iState; + int iAttributes; + + BOOL fHasProperties; + SCA_WEB_PROPERTIES swp; + + BOOL fHasApplication; + SCA_WEB_APPLICATION swapp; + + BOOL fHasSecurity; + int dwAccessPermissions; + int iConnectionTimeout; + + SCA_MIMEMAP* psmm; // mime mappings + SCA_WEB_SSL_CERTIFICATE* pswscList; + SCA_HTTP_HEADER* pshhList; + SCA_WEB_ERROR* psweList; + + BOOL fHasLog; + SCA_WEB_LOG swl; + + SCA_WEB* pswNext; +}; + + +// prototypes +HRESULT ScaWebsRead( + __in IMSAdminBase* piMetabase, + __in SCA_MIMEMAP** ppsmmList, + __in SCA_WEB** ppswList, + __in SCA_HTTP_HEADER** pshhList, + __in SCA_WEB_ERROR** psweList, + __in WCA_WRAPQUERY_HANDLE hUserQuery, + __in WCA_WRAPQUERY_HANDLE hWebDirPropQuery, + __in WCA_WRAPQUERY_HANDLE hSslCertQuery, + __in WCA_WRAPQUERY_HANDLE hWebLogQuery, + __in WCA_WRAPQUERY_HANDLE hWebAppQuery, + __in WCA_WRAPQUERY_HANDLE hWebAppExtQuery, + __inout LPWSTR *ppwzCustomActionData + ); + +HRESULT ScaWebsGetBase( + __in IMSAdminBase* piMetabase, + __in SCA_WEB* pswList, + __in LPCWSTR wzWeb, + __out_ecount(cchWebBase) LPWSTR wzWebBase, + __in DWORD cchWebBase, + __in WCA_WRAPQUERY_HANDLE hWrapQuery + ); + +HRESULT ScaWebsInstall( + __in IMSAdminBase* piMetabase, + __in SCA_WEB* pswList, + __in SCA_APPPOOL * psapList + ); + +HRESULT ScaWebsUninstall( + __in IMSAdminBase* piMetabase, + __in SCA_WEB* pswList + ); + +void ScaWebsFreeList( + __in SCA_WEB* pswHead + ); diff --git a/src/ext/Iis/ca/scaweb7.cpp b/src/ext/Iis/ca/scaweb7.cpp new file mode 100644 index 00000000..3ee781f2 --- /dev/null +++ b/src/ext/Iis/ca/scaweb7.cpp @@ -0,0 +1,953 @@ +// 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" + +//Adding this because delivery doesn't have the updated specstrings.h that windows build does +#ifndef __in_xcount +#define __in_xcount(size) +#endif + +// prototypes for private helper functions +static SCA_WEB7* NewWeb7(); +static SCA_WEB7* AddWebToList7( + __in SCA_WEB7* pswList, + __in SCA_WEB7* psw + ); + +static HRESULT ScaWebFindBase7( + __in SCA_WEB7* pswList, + LPCWSTR wzDescription + ); + +static HRESULT ScaWebWrite7( + __in SCA_WEB7* psw, + __in SCA_APPPOOL * psapList + ); + +static HRESULT ScaWebRemove7(__in const SCA_WEB7* psw); + + +HRESULT ScaWebsRead7( + __in SCA_WEB7** ppswList, + __in SCA_HTTP_HEADER** ppshhList, + __in SCA_WEB_ERROR** ppsweList, + __in WCA_WRAPQUERY_HANDLE hUserQuery, + __in WCA_WRAPQUERY_HANDLE hWebDirPropQuery, + __in WCA_WRAPQUERY_HANDLE hSslCertQuery, + __in WCA_WRAPQUERY_HANDLE hWebLogQuery, + __in WCA_WRAPQUERY_HANDLE hWebAppQuery, + __in WCA_WRAPQUERY_HANDLE hWebAppExtQuery, + __inout LPWSTR *ppwzCustomActionData + ) +{ + Assert(ppswList); + WcaLog(LOGMSG_VERBOSE, "Entering ScaWebsRead7()"); + + HRESULT hr = S_OK; + + MSIHANDLE hRec; + MSIHANDLE hRecAddresses; + + WCA_WRAPQUERY_HANDLE hQueryWebSite = NULL; + WCA_WRAPQUERY_HANDLE hQueryWebAddress = NULL; + + SCA_WEB7* psw = NULL; + LPWSTR pwzData = NULL; + + DWORD dwLen = 0; + errno_t error = EINVAL; + + // check to see what tables are available + hr = WcaBeginUnwrapQuery(&hQueryWebSite, ppwzCustomActionData); + ExitOnFailure(hr, "Failed to unwrap query for ScaWebsRead"); + + hr = WcaBeginUnwrapQuery(&hQueryWebAddress, ppwzCustomActionData); + ExitOnFailure(hr, "Failed to unwrap query for ScaWebsRead"); + + + if (0 == WcaGetQueryRecords(hQueryWebSite) || 0 == WcaGetQueryRecords(hQueryWebAddress)) + { + WcaLog(LOGMSG_VERBOSE, "Required tables not present"); + ExitFunction1(hr = S_FALSE); + } + + // loop through all the webs + while (S_OK == (hr = WcaFetchWrappedRecord(hQueryWebSite, &hRec))) + { + psw = NewWeb7(); + if (!psw) + { + hr = E_OUTOFMEMORY; + break; + } + + // get the darwin information + hr = WcaGetRecordString(hRec, wqWeb, &pwzData); + ExitOnFailure(hr, "Failed to get Web"); + hr = ::StringCchCopyW(psw->wzKey, countof(psw->wzKey), pwzData); + ExitOnFailure(hr, "Failed to copy key string to web object"); + + // get component install state + hr = WcaGetRecordString(hRec, wqComponent, &pwzData); + ExitOnFailure(hr, "Failed to get Component for Web"); + hr = ::StringCchCopyW(psw->wzComponent, countof(psw->wzComponent), pwzData); + ExitOnFailure(hr, "Failed to copy component string to web object"); + if (*(psw->wzComponent)) + { + psw->fHasComponent = TRUE; + + hr = WcaGetRecordInteger(hRec, wqInstalled, (int *)&psw->isInstalled); + ExitOnFailure(hr, "Failed to get web Component's installed state"); + + WcaGetRecordInteger(hRec, wqAction, (int *)&psw->isAction); + ExitOnFailure(hr, "Failed to get web Component's action state"); + } + + // Get the web's description. + hr = WcaGetRecordString(hRec, wqDescription, &pwzData); + ExitOnFailure(hr, "Failed to get Description for Web"); + hr = ::StringCchCopyW(psw->wzDescription, countof(psw->wzDescription), pwzData); + ExitOnFailure(hr, "Failed to copy description string to web object"); + + //get web's site Id + hr = WcaGetRecordInteger(hRec, wqId, &psw->iSiteId); + ExitOnFailure(hr, "Failed to get SiteId for Web"); + + // get the web's key address (Bindings) + hr = WcaGetRecordString(hRec, wqAddress, &pwzData); + ExitOnFailure(hr, "Failed to get Address for Web"); + hr = ::StringCchCopyW(psw->swaBinding.wzKey, countof(psw->swaBinding.wzKey), pwzData); + ExitOnFailure(hr, "Failed to copy web binding key"); + + hr = WcaGetRecordString(hRec, wqIP, &pwzData); + ExitOnFailure(hr, "Failed to get IP for Web"); + hr = ::StringCchCopyW(psw->swaBinding.wzIP, countof(psw->swaBinding.wzIP), pwzData); + ExitOnFailure(hr, "Failed to copy web IP"); + + hr = WcaGetRecordString(hRec, wqPort, &pwzData); + ExitOnFailure(hr, "Failed to get Web Address port"); + psw->swaBinding.iPort = wcstol(pwzData, NULL, 10); + + hr = WcaGetRecordString(hRec, wqHeader, &pwzData); + ExitOnFailure(hr, "Failed to get Header for Web"); + hr = ::StringCchCopyW(psw->swaBinding.wzHeader, countof(psw->swaBinding.wzHeader), pwzData); + ExitOnFailure(hr, "Failed to copy web header"); + + hr = WcaGetRecordInteger(hRec, wqSecure, &psw->swaBinding.fSecure); + ExitOnFailure(hr, "Failed to get if Web is secure"); + if (S_FALSE == hr) + { + psw->swaBinding.fSecure = FALSE; + } + + // look to see if site exists + dwLen = METADATA_MAX_NAME_LEN; + hr = ScaWebFindBase7(*ppswList, psw->wzDescription); + + // If we didn't find a web in memory, ignore it - during execute CA + // if the site truly does not exist then there will be an error. + if (S_OK == hr) + { + // site exists in config + psw->fBaseExists = TRUE; + } + else if (HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND) == hr) + { + hr = S_OK; + + // site does not exists in config + psw->fBaseExists = FALSE; + } + ExitOnFailure(hr, "Failed to find web site"); + + // get any extra web addresses + WcaFetchWrappedReset(hQueryWebAddress); + + while (S_OK == (hr = WcaFetchWrappedRecordWhereString(hQueryWebAddress, 2, psw->wzKey, &hRecAddresses))) + { + if (MAX_ADDRESSES_PER_WEB <= psw->cExtraAddresses) + { + hr = HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER); + ExitOnFailure(hr, "Failure to get more extra web addresses, max exceeded."); + } + + hr = WcaGetRecordString(hRecAddresses, waqAddress, &pwzData); + ExitOnFailure(hr, "Failed to get extra web Address"); + + // if this isn't the key address add it + if (0 != lstrcmpW(pwzData, psw->swaBinding.wzKey)) + { + hr = ::StringCchCopyW(psw->swaExtraAddresses[psw->cExtraAddresses].wzKey, + countof(psw->swaExtraAddresses[psw->cExtraAddresses].wzKey), pwzData); + ExitOnFailure(hr, "Failed to copy web binding key"); + + hr = WcaGetRecordString(hRecAddresses, waqIP, &pwzData); + ExitOnFailure(hr, "Failed to get extra web IP"); + hr = ::StringCchCopyW(psw->swaExtraAddresses[psw->cExtraAddresses].wzIP, countof(psw->swaExtraAddresses[psw->cExtraAddresses].wzIP), pwzData); + ExitOnFailure(hr, "Failed to copy web binding IP"); + + hr = WcaGetRecordString(hRecAddresses, waqPort, &pwzData); + ExitOnFailure(hr, "Failed to get port for extra web IP"); + psw->swaExtraAddresses[psw->cExtraAddresses].iPort= wcstol(pwzData, NULL, 10); + + // errno is set to ERANGE if overflow or underflow occurs + _get_errno(&error); + + if (ERANGE == error) + { + hr = E_INVALIDARG; + ExitOnFailure(hr, "Failed to convert web Port address"); + } + + hr = WcaGetRecordString(hRecAddresses, waqHeader, &pwzData); + ExitOnFailure(hr, "Failed to get header for extra web IP"); + hr = ::StringCchCopyW(psw->swaExtraAddresses[psw->cExtraAddresses].wzHeader, countof(psw->swaExtraAddresses[psw->cExtraAddresses].wzHeader), pwzData); + ExitOnFailure(hr, "Failed to copy web binding header"); + + hr = WcaGetRecordInteger(hRecAddresses, waqSecure, &psw->swaExtraAddresses[psw->cExtraAddresses].fSecure); + ExitOnFailure(hr, "Failed to get if secure extra web IP"); + if (S_FALSE == hr) + { + psw->swaExtraAddresses[psw->cExtraAddresses].fSecure = FALSE; + } + + ++psw->cExtraAddresses; + } + } + + if (E_NOMOREITEMS == hr) + { + hr = S_OK; + } + ExitOnFailure(hr, "Failure occured while getting extra web addresses"); + + // + // Connection time out + // + hr = WcaGetRecordInteger(hRec, wqConnectionTimeout, &psw->iConnectionTimeout); + ExitOnFailure(hr, "Failed to get connection timeout for Web"); + + if (psw->fHasComponent) // If we're installing it, it needs a dir + { + // get the web's directory + if (INSTALLSTATE_SOURCE == psw->isAction) + { + hr = WcaGetRecordString(hRec, wqSourcePath, &pwzData); + } + else + { + hr = WcaGetRecordString(hRec, wqTargetPath, &pwzData); + } + ExitOnFailure(hr, "Failed to get Source/TargetPath for Directory"); + + dwLen = lstrlenW(pwzData); + // remove trailing backslash + if (dwLen > 0 && pwzData[dwLen-1] == L'\\') + { + pwzData[dwLen-1] = 0; + } + hr = ::StringCchCopyW(psw->wzDirectory, countof(psw->wzDirectory), pwzData); + ExitOnFailure(hr, "Failed to copy web dir: '%ls'", pwzData); + + } + + hr = WcaGetRecordInteger(hRec, wqState, &psw->iState); + ExitOnFailure(hr, "Failed to get state for Web"); + + hr = WcaGetRecordInteger(hRec, wqAttributes, &psw->iAttributes); + ExitOnFailure(hr, "Failed to get attributes for Web"); + + // get the dir properties for this web + hr = WcaGetRecordString(hRec, wqProperties, &pwzData); + ExitOnFailure(hr, "Failed to get directory properties for Web"); + if (*pwzData) + { + hr = ScaGetWebDirProperties(pwzData, hUserQuery, hWebDirPropQuery, &psw->swp); + ExitOnFailure(hr, "Failed to get directory properties for Web"); + + psw->fHasProperties = TRUE; + } + + // get the application information for this web + hr = WcaGetRecordString(hRec, wqApplication, &pwzData); + ExitOnFailure(hr, "Failed to get application identifier for Web"); + if (*pwzData) + { + hr = ScaGetWebApplication(NULL, pwzData, hWebAppQuery, hWebAppExtQuery, &psw->swapp); + ExitOnFailure(hr, "Failed to get application for Web"); + + psw->fHasApplication = TRUE; + } + + // get the SSL certificates + hr = ScaSslCertificateRead(psw->wzKey, hSslCertQuery, &(psw->pswscList)); + ExitOnFailure(hr, "Failed to get SSL Certificates."); + + // get the custom headers + if (*ppshhList) + { + hr = ScaGetHttpHeader(hhptWeb, psw->wzKey, ppshhList, &(psw->pshhList)); + ExitOnFailure(hr, "Failed to get Custom HTTP Headers"); + } + + // get the errors + if (*ppsweList) + { + hr = ScaGetWebError(weptWeb, psw->wzKey, ppsweList, &(psw->psweList)); + ExitOnFailure(hr, "Failed to get Custom Errors"); + } + + // get the log information for this web + hr = WcaGetRecordString(hRec, wqLog, &pwzData); + ExitOnFailure(hr, "Failed to get log identifier for Web"); + if (*pwzData) + { + hr = ScaGetWebLog7(pwzData, hWebLogQuery, &psw->swl); + ExitOnFailure(hr, "Failed to get Log for Web."); + psw->fHasLog = TRUE; + } + + *ppswList = AddWebToList7(*ppswList, psw); + psw = NULL; // set the web NULL so it doesn't accidentally get freed below + } + + if (E_NOMOREITEMS == hr) + { + hr = S_OK; + } + +LExit: + // if anything was left over after an error clean it all up + WcaFinishUnwrapQuery(hQueryWebSite); + WcaFinishUnwrapQuery(hQueryWebAddress); + + ScaWebsFreeList7(psw); + + ReleaseStr(pwzData); + WcaLog(LOGMSG_VERBOSE, "Exiting ScaWebsRead7()"); + + return hr; +} + +BOOL CompareBinding( + __in IAppHostElement* pBinding, + __in LPVOID pContext + ) +{ + BOOL fFound = FALSE; + HRESULT hr = S_OK; + LPWSTR pwzBindingInfo = NULL; + SCA_WEB7* psw = (SCA_WEB7*)pContext; + + hr = Iis7GetPropertyString(pBinding, IIS_CONFIG_BINDINGINFO, &pwzBindingInfo); + ExitOnFailure(hr, "Failed to get bindinginfo for binding element"); + + LPWSTR pwzExists = pwzBindingInfo; + // Break down the address into its constituent parts (IP:Port:Header). + // Taken from IIS6 CA code for compatibility + while (S_OK == hr && *pwzExists) + { + LPCWSTR pwzIPExists = pwzExists; + pwzExists = const_cast(wcsstr(pwzIPExists, L":")); + if (NULL == pwzExists) + { + ExitFunction(); + } + *pwzExists = L'\0'; + + LPCWSTR pwzPortExists = pwzExists + 1; + pwzExists = const_cast(wcsstr(pwzPortExists, L":")); + if (NULL == pwzExists) + { + ExitFunction(); + } + *pwzExists = L'\0'; + int iPortExists = wcstol(pwzPortExists, NULL, 10); + + LPCWSTR pwzHeaderExists = pwzExists + 1; + + BOOL fIpMatches = (0 == lstrcmpW(psw->swaBinding.wzIP, pwzIPExists)); // Explicit IP match + fIpMatches |= (0 == lstrcmpW(psw->swaBinding.wzIP, L"*")); // Authored * matches any IP + fIpMatches |= ('\0' != psw->swaBinding.wzIP) && // Unauthored IP + (0 == lstrcmpW(pwzIPExists, L"*")); // matches the All Unassigned IP : '*' + + // compare the passed in address with the address listed for this web + if (fIpMatches && psw->swaBinding.iPort == iPortExists && + 0 == lstrcmpW(psw->swaBinding.wzHeader, pwzHeaderExists)) + { + fFound = TRUE; + break; + } + + // move to the next block of data, this may move beyond the available + // data and exit the while loop above. + pwzExists = const_cast(pwzHeaderExists + lstrlenW(pwzHeaderExists)); + } + +LExit: + WcaLog(LOGMSG_VERBOSE, "Site with binding %ls %s a match", pwzBindingInfo, fFound ? "is" : "is not"); + ReleaseNullStr(pwzBindingInfo); + return fFound; +} + +BOOL EnumSiteCompareBinding( + __in IAppHostElement* pSite, + __in LPVOID pContext + ) +{ + BOOL fFound = FALSE; + HRESULT hr = S_OK; + SCA_WEB7* psw = (SCA_WEB7*)pContext; + IAppHostChildElementCollection *pSiteChildren = NULL; + IAppHostElement *pBindings = NULL; + IAppHostElementCollection *pBindingsCollection = NULL; + IAppHostElement *pBinding = NULL; + VARIANT vtProp; + VariantInit(&vtProp); + + hr = pSite->get_ChildElements(&pSiteChildren); + ExitOnFailure(hr, "Failed get site child elements collection"); + + vtProp.vt = VT_BSTR; + vtProp.bstrVal = ::SysAllocString(IIS_CONFIG_BINDINGS); + hr = pSiteChildren->get_Item(vtProp, &pBindings); + ExitOnFailure(hr, "Failed get bindings element"); + + hr = pBindings->get_Collection(&pBindingsCollection); + ExitOnFailure(hr, "Failed get bindings collection"); + + WcaLog(LOGMSG_VERBOSE, "Searching for site with binding %ls:%d:%ls", psw->swaBinding.wzIP, psw->swaBinding.iPort, psw->swaBinding.wzHeader); + + hr = Iis7EnumAppHostElements(pBindingsCollection, CompareBinding, psw, &pBinding, NULL); + ExitOnFailure(hr, "Failed search bindings collection"); + + fFound = NULL != pBinding; +LExit: + VariantClear(&vtProp); + ReleaseNullObject(pSiteChildren); + ReleaseNullObject(pBindings); + ReleaseNullObject(pBindingsCollection); + ReleaseNullObject(pBinding); + return fFound; +} + +HRESULT ScaWebSearch7( + __in SCA_WEB7* psw, + __deref_out_z_opt LPWSTR* pswWeb, + __out_opt BOOL* pfFound + ) +{ + HRESULT hr = S_OK; + BOOL fInitializedCom = FALSE; + BSTR bstrSites = NULL; + BSTR bstrAppHostRoot = NULL; + IAppHostAdminManager *pAdminMgr = NULL; + IAppHostElement *pSites = NULL; + IAppHostElementCollection *pCollection = NULL; + IAppHostElement *pSite = NULL; + + if (NULL != pswWeb) + { + ReleaseNullStr(*pswWeb); + } + + if (NULL != pfFound) + { + *pfFound = FALSE; + } + + hr = ::CoInitialize(NULL); + ExitOnFailure(hr, "Failed to initialize COM"); + fInitializedCom = TRUE; + + hr = CoCreateInstance(__uuidof(AppHostAdminManager), NULL, CLSCTX_INPROC_SERVER, __uuidof(IAppHostAdminManager), reinterpret_cast (&pAdminMgr)); + if (REGDB_E_CLASSNOTREG == hr) + { + WcaLog(LOGMSG_VERBOSE, "AppHostAdminManager was not registered, cannot find site."); + hr = S_OK; + ExitFunction(); + } + ExitOnFailure(hr, "Failed to CoCreate IAppHostAdminManager"); + + bstrSites = ::SysAllocString(IIS_CONFIG_SITES_SECTION); + ExitOnNull(bstrSites, hr, E_OUTOFMEMORY, "Failed to allocate sites string."); + + bstrAppHostRoot = ::SysAllocString(IIS_CONFIG_APPHOST_ROOT); + ExitOnNull(bstrAppHostRoot, hr, E_OUTOFMEMORY, "Failed to allocate host root string."); + + hr = pAdminMgr->GetAdminSection(bstrSites, bstrAppHostRoot, &pSites); + ExitOnFailure(hr, "Failed get sites section"); + ExitOnNull(pSites, hr, ERROR_FILE_NOT_FOUND, "Failed get sites section object"); + + hr = pSites->get_Collection(&pCollection); + ExitOnFailure(hr, "Failed get sites collection"); + + // not explicitly doing a Description search + if (-1 != psw->iSiteId) + { + if (MSI_NULL_INTEGER == psw->iSiteId) + { + // Enumerate sites & determine if the binding matches + hr = Iis7EnumAppHostElements(pCollection, EnumSiteCompareBinding, psw, &pSite, NULL); + ExitOnFailure(hr, "Failed locate site by ID"); + } + else + { + // Find a site with ID matches + hr = Iis7FindAppHostElementInteger(pCollection, IIS_CONFIG_SITE, IIS_CONFIG_ID, psw->iSiteId, &pSite, NULL); + ExitOnFailure(hr, "Failed locate site by ID"); + } + } + + if (NULL == pSite) + { + // Find a site with Name that matches + hr = Iis7FindAppHostElementString(pCollection, IIS_CONFIG_SITE, IIS_CONFIG_NAME, psw->wzDescription, &pSite, NULL); + ExitOnFailure(hr, "Failed locate site by ID"); + } + + if (NULL != pSite) + { + if (NULL != pfFound) + { + *pfFound = TRUE; + } + + if (NULL != pswWeb) + { + // We found a site, return its description + hr = Iis7GetPropertyString(pSite, IIS_CONFIG_NAME, pswWeb); + ExitOnFailure(hr, "Failed get site name"); + } + } +LExit: + ReleaseNullObject(pAdminMgr); + ReleaseNullObject(pSites); + ReleaseNullObject(pCollection); + ReleaseNullObject(pSite); + ReleaseBSTR(bstrAppHostRoot); + ReleaseBSTR(bstrSites); + + if (fInitializedCom) + { + ::CoUninitialize(); + } + return hr; +} + + +HRESULT ScaWebsGetBase7( + __in SCA_WEB7* pswList, + __in LPCWSTR pswWebKey, + __out_ecount(cchDest) LPWSTR pswWeb, + __in DWORD_PTR cchDest + ) +{ + HRESULT hr = S_OK; + BOOL fFound = FALSE; + SCA_WEB7* psw = pswList; + LPWSTR wzSiteName = NULL; + + *pswWeb = '/0'; + + //looking for psw->wzKey == pswWebKey + while(psw) + { + if (0 == wcscmp(pswWebKey, psw->wzKey)) + { + fFound = TRUE; + break; + } + psw = psw->pswNext; + } + + if (!fFound) + { + ExitFunction1(hr = S_FALSE); + } + + // Search if we're not installing the site + if (!psw->fHasComponent || (psw->iAttributes & SWATTRIB_NOCONFIGUREIFEXISTS)) + { + // We are not installing this website. Search for it in IIS config + hr = ScaWebSearch7(psw, &wzSiteName, NULL); + ExitOnFailure(hr, "Failed to search for Website"); + + if (NULL != wzSiteName) + { + hr = ::StringCchCopyW(pswWeb, cchDest, wzSiteName); + ExitOnFailure(hr, "Failed to set Website description for located Website"); + } + } + + if ('/0' == *pswWeb) + { + WcaLog(LOGMSG_VERBOSE, "Could not find Web: %ls, defaulting to %ls", psw->wzKey, psw->wzDescription); + // Default to the provided description, the Exec CA will locate by description + hr = ::StringCchCopyW(pswWeb, cchDest, psw->wzDescription); + ExitOnFailure(hr, "Failed to set Website description to default"); + } +LExit: + ReleaseNullStr(wzSiteName); + return hr; +} + +HRESULT ScaWebsInstall7( + __in SCA_WEB7* pswList, + __in SCA_APPPOOL * psapList + ) +{ + HRESULT hr = S_OK; + SCA_WEB7* psw = pswList; + + while (psw) + { + // if we are installing the web site + if (psw->fHasComponent && WcaIsInstalling(psw->isInstalled, psw->isAction)) + { + hr = ScaWebWrite7(psw, psapList); + ExitOnFailure(hr, "failed to write web '%ls' to metabase", psw->wzKey); + } + + psw = psw->pswNext; + } + +LExit: + return hr; +} + + +HRESULT ScaWebsUninstall7( + __in SCA_WEB7* pswList + ) +{ + HRESULT hr = S_OK; + SCA_WEB7* psw = pswList; + + while (psw) + { + if (psw->fHasComponent && WcaIsUninstalling(psw->isInstalled, psw->isAction)) + { + // If someone + hr = ScaWebRemove7(psw); + ExitOnFailure(hr, "Failed to remove web '%ls' ", psw->wzKey); + } + + psw = psw->pswNext; + } + +LExit: + return hr; +} + + +void ScaWebsFreeList7(__in SCA_WEB7* pswList) +{ + SCA_WEB7* pswDelete = pswList; + while (pswList) + { + pswDelete = pswList; + pswList = pswList->pswNext; + + // Free the SSL, headers and errors list first + ScaSslCertificateFreeList(pswDelete->pswscList); + ScaHttpHeaderFreeList(pswDelete->pshhList); + ScaWebErrorFreeList(pswDelete->psweList); + + MemFree(pswDelete); + } +} + + +// private helper functions + +static SCA_WEB7* NewWeb7() +{ + SCA_WEB7* psw = (SCA_WEB7*)MemAlloc(sizeof(SCA_WEB7), TRUE); + Assert(psw); + return psw; +} + + +static SCA_WEB7* AddWebToList7( + __in SCA_WEB7* pswList, + __in SCA_WEB7* psw + ) +{ + if (pswList) + { + SCA_WEB7* pswTemp = pswList; + while (pswTemp->pswNext) + { + pswTemp = pswTemp->pswNext; + } + + pswTemp->pswNext = psw; + } + else + { + pswList = psw; + } + + return pswList; +} + + +static HRESULT ScaWebFindBase7( + __in SCA_WEB7* pswList, + __in_z LPCWSTR wzDescription + ) +{ + HRESULT hr = S_OK; + BOOL fFound = FALSE; + + // try to find the web in memory first + for (SCA_WEB7* psw = pswList; psw; psw = psw->pswNext) + { + if (0 == wcscmp(wzDescription, psw->wzDescription)) + { + fFound = TRUE; + break; + } + } + + if (!fFound) + { + hr = HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND); + } + + return hr; +} + + +static HRESULT ScaWebWrite7( + __in SCA_WEB7* psw, + __in SCA_APPPOOL * psapList + ) +{ + HRESULT hr = S_OK; + + BOOL fExists = FALSE; + UINT ui = 0; + WCHAR wzIP[64]; + WCHAR wzBinding[1024]; + WCHAR wzAppPoolName[MAX_PATH]; + + // + // determine if site must be new + // + if (psw->iAttributes & SWATTRIB_NOCONFIGUREIFEXISTS) + { + // Check if site already exists. + hr = ScaWebSearch7(psw, NULL, &fExists); + ExitOnFailure(hr, "Failed to search for web: %ls", psw->wzKey); + + if (fExists) + { + hr = S_FALSE; + WcaLog(LOGMSG_VERBOSE, "Skipping configuration of existing web: %ls", psw->wzKey); + ExitFunction(); + } + } + + //create a site + hr = ScaWriteConfigID(IIS_SITE); + ExitOnFailure(hr, "Failed write site ID"); + + hr = ScaWriteConfigID(IIS_CREATE); + ExitOnFailure(hr, "Failed write site ID create action"); + + //Site Name + hr = ScaWriteConfigString(psw->wzDescription); //Site Name + ExitOnFailure(hr, "Failed write site desc"); + + //Site Id -- value is MSI_NULL_INTEGER if not set in WIX + hr = ScaWriteConfigInteger(psw->iSiteId); //SiteID + ExitOnFailure(hr, "Failed write site id value"); + + //Site Auto Start -- value is MSI_NULL_INTEGER if not set in WIX + hr = ScaWriteConfigInteger(psw->iState); // serverAutoStart + ExitOnFailure(hr, "Failed write site autostart"); + + hr = ScaWriteConfigInteger(psw->iConnectionTimeout); //limits/connectionTimeout + ExitOnFailure(hr, "Failed write site timeout"); + + //create default application + hr = ScaWriteConfigID(IIS_APPLICATION); + ExitOnFailure(hr, "Failed write app ID"); + hr = ScaWriteConfigID(IIS_CREATE); + ExitOnFailure(hr, "Failed write app action ID"); + hr = ScaWriteConfigString(psw->wzDescription); //site name key + ExitOnFailure(hr, "Failed write app desc"); + hr = ScaWriteConfigString(L"/"); // App Path (default) + ExitOnFailure(hr, "Failed write app def path /"); + + if (psw->fHasApplication && *psw->swapp.wzAppPool) + { + hr = ScaFindAppPool7(psw->swapp.wzAppPool, wzAppPoolName, countof(wzAppPoolName), psapList); + ExitOnFailure(hr, "Failed to read app pool from application"); + + hr = ScaWriteConfigString(wzAppPoolName); + ExitOnFailure(hr, "Failed write app appPool"); + } + else + { + hr = ScaWriteConfigString(L""); + ExitOnFailure(hr, "Failed write app appPool"); + } + + //create vdir for default application + hr = ScaWriteConfigID(IIS_VDIR); + ExitOnFailure(hr, "Failed write vdir ID"); + hr = ScaWriteConfigID(IIS_CREATE); + ExitOnFailure(hr, "Failed write vdir action"); + hr = ScaWriteConfigString(psw->wzDescription); //site name key + ExitOnFailure(hr, "Failed write vdir desc"); + hr = ScaWriteConfigString(L"/"); //vdir path (default) + ExitOnFailure(hr, "Failed write vdir app"); + hr = ScaWriteConfigString(psw->wzDirectory); //physical dir + ExitOnFailure(hr, "Failed write vdir dir"); + + //create bindings for site + hr = ScaWriteConfigID(IIS_BINDING); + ExitOnFailure(hr, "Failed write binding ID"); + hr = ScaWriteConfigID(IIS_CREATE); + ExitOnFailure(hr, "Failed write binding action ID"); + hr = ScaWriteConfigString(psw->wzDescription); //site name key + ExitOnFailure(hr, "Failed write binding site key"); + + if (psw->swaBinding.fSecure) + { + hr = ScaWriteConfigString(L"https"); // binding protocol + ExitOnFailure(hr, "Failed write binding https"); + } + else + { + hr = ScaWriteConfigString(L"http"); // binding protocol + ExitOnFailure(hr, "Failed write binding http"); + } + + // set the IP address appropriately + if (0 == wcscmp(psw->swaBinding.wzIP, L"")) + { + hr = ::StringCchCopyW(wzIP, countof(wzIP), L"*"); // if no IP specified = add * + } + else + { +#pragma prefast(suppress:26037, "Source string is null terminated - it is populated as target of ::StringCchCopyW") + hr = ::StringCchCopyW(wzIP, countof(wzIP), psw->swaBinding.wzIP); // else leave untouched + } + ExitOnFailure(hr, "Failed to copy IP string"); + + hr = ::StringCchPrintfW(wzBinding, countof(wzBinding), L"%s:%d:%s", wzIP, psw->swaBinding.iPort, psw->swaBinding.wzHeader); + ExitOnFailure(hr, "Failed to format IP:Port:Header binding string"); + + // write bindings CAData + hr = ScaWriteConfigString(wzBinding) ; //binding info + ExitOnFailure(hr, "Failed to create web bindings"); + + for (ui = 0; (ui < MAX_ADDRESSES_PER_WEB) && (ui < psw->cExtraAddresses); ++ui) + { + // set the IP address appropriately + if (0 == wcscmp(psw->swaExtraAddresses[ui].wzIP, L"")) + { + hr = ::StringCchCopyW(wzIP, countof(wzIP), L"*"); // if no IP specified = add * + } + else + { +#pragma prefast(suppress:26037, "Source string is null terminated - it is populated as target of ::StringCchCopyW") + hr = ::StringCchCopyW(wzIP, countof(wzIP), psw->swaExtraAddresses[ui].wzIP); //else leave untouched + } + ExitOnFailure(hr, "Failed to copy web IP string"); + + hr = ::StringCchPrintfW(wzBinding, countof(wzBinding), L"%s:%d:%s", wzIP, psw->swaExtraAddresses[ui].iPort, psw->swaExtraAddresses[ui].wzHeader); + ExitOnFailure(hr, "Failed to copy web IP"); + + //create bindings for site + hr = ScaWriteConfigID(IIS_BINDING); + ExitOnFailure(hr, "Failed write binding ID"); + hr = ScaWriteConfigID(IIS_CREATE); + ExitOnFailure(hr, "Failed write binding action"); + hr = ScaWriteConfigString(psw->wzDescription); //site name key + ExitOnFailure(hr, "Failed write binding web name"); + + if (psw->swaExtraAddresses[ui].fSecure) + { + hr = ScaWriteConfigString(L"https"); // binding protocol + } + else + { + hr = ScaWriteConfigString(L"http"); // binding protocol + } + ExitOnFailure(hr, "Failed write binding http(s)"); + + // write bindings CAData + hr = ScaWriteConfigString(wzBinding) ; //binding info + ExitOnFailure(hr, "Failed write binding info"); + } + + // write the web dirproperties information + if (psw->fHasProperties) + { + // dir properties are for the default application of the web + // with location '/' + hr = ScaWriteWebDirProperties7(psw->wzDescription, L"/", &psw->swp); + ExitOnFailure(hr, "Failed to write web security information to metabase"); + } + + //// write the application information + if (psw->fHasApplication) + { + hr = ScaWriteWebApplication7(psw->wzDescription, L"/", &psw->swapp, psapList); + ExitOnFailure(hr, "Failed to write web application information to metabase"); + } + + // write the SSL certificate information + if (psw->pswscList) + { + hr = ScaSslCertificateWrite7(psw->wzDescription, psw->pswscList); + ExitOnFailure(hr, "Failed to write SSL certificates for Web site: %ls", psw->wzKey); + } + + // write the headers + if (psw->pshhList) + { + hr = ScaWriteHttpHeader7(psw->wzDescription, L"/", psw->pshhList); + ExitOnFailure(hr, "Failed to write custom HTTP headers for Web site: %ls", psw->wzKey); + } + + // write the errors + if (psw->psweList) + { + hr = ScaWriteWebError7(psw->wzDescription, L"/", psw->psweList); + ExitOnFailure(hr, "Failed to write custom web errors for Web site: %ls", psw->wzKey); + } + + // write the log information to the metabase + if (psw->fHasLog) + { + hr = ScaWriteWebLog7(psw->wzDescription, &psw->swl); + ExitOnFailure(hr, "Failed to write web log information to metabase"); + } + +LExit: + return hr; +} + + +static HRESULT ScaWebRemove7( + __in const SCA_WEB7* psw + ) +{ + HRESULT hr = S_OK; + + hr = ScaWriteConfigID(IIS_SITE); + ExitOnFailure(hr, "Failed write site ID"); + hr = ScaWriteConfigID(IIS_DELETE); + ExitOnFailure(hr, "Failed write site action"); + hr = ScaWriteConfigString(psw->wzDescription); //Site Name + ExitOnFailure(hr, "Failed write site name"); + +LExit: + return hr; +} diff --git a/src/ext/Iis/ca/scaweb7.h b/src/ext/Iis/ca/scaweb7.h new file mode 100644 index 00000000..78946756 --- /dev/null +++ b/src/ext/Iis/ca/scaweb7.h @@ -0,0 +1,97 @@ +#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. + + +#include "scawebapp.h" +#include "scawebprop.h" +#include "scahttpheader.h" +#include "scaweberr.h" +#include "scassl.h" +#include "scaapppool.h" +#include "scaweblog.h" + +// globals +#define MAX_ADDRESSES_PER_WEB 10 + +// structs +struct SCA_WEB7 +{ + // darwin information + WCHAR wzKey[MAX_DARWIN_KEY + 1]; + WCHAR wzComponent[MAX_DARWIN_KEY + 1]; + BOOL fHasComponent; + INSTALLSTATE isInstalled; + INSTALLSTATE isAction; + + int iSiteId; + + // metabase information + WCHAR wzWebBase[METADATA_MAX_NAME_LEN + 1]; + BOOL fBaseExists; + + // iis configuation information + SCA_WEB_ADDRESS swaBinding; + + SCA_WEB_ADDRESS swaExtraAddresses[MAX_ADDRESSES_PER_WEB + 1]; + DWORD cExtraAddresses; + + WCHAR wzDirectory[MAX_PATH]; + WCHAR wzDescription[MAX_DARWIN_COLUMN + 1]; + + int iState; + int iAttributes; + + BOOL fHasProperties; + SCA_WEB_PROPERTIES swp; + + BOOL fHasApplication; + SCA_WEB_APPLICATION swapp; + + BOOL fHasSecurity; + int dwAccessPermissions; + int iConnectionTimeout; + + SCA_WEB_SSL_CERTIFICATE* pswscList; + SCA_HTTP_HEADER* pshhList; + SCA_WEB_ERROR* psweList; + + BOOL fHasLog; + SCA_WEB_LOG swl; + + SCA_WEB7* pswNext; +}; + + +// prototypes +HRESULT ScaWebsRead7( + __in SCA_WEB7** ppswList, + __in SCA_HTTP_HEADER** ppshhList, + __in SCA_WEB_ERROR** ppsweList, + __in WCA_WRAPQUERY_HANDLE hUserQuery, + __in WCA_WRAPQUERY_HANDLE hWebDirPropQuery, + __in WCA_WRAPQUERY_HANDLE hSslCertQuery, + __in WCA_WRAPQUERY_HANDLE hWebLogQuery, + __in WCA_WRAPQUERY_HANDLE hWebAppQuery, + __in WCA_WRAPQUERY_HANDLE hWebAppExtQuery, + __inout LPWSTR *ppwzCustomActionData + ); + +HRESULT ScaWebsGetBase7( + __in SCA_WEB7* pswList, + __in LPCWSTR pswWebKey, + __out_ecount(cchDest) LPWSTR pswWeb, + __in DWORD_PTR cchDest + ); + +HRESULT ScaWebsInstall7( + __in SCA_WEB7* pswList, + __in SCA_APPPOOL * psapList + ); + +HRESULT ScaWebsUninstall7( + __in SCA_WEB7* pswList + ); + +void ScaWebsFreeList7( + __in SCA_WEB7* pswHead + ); diff --git a/src/ext/Iis/ca/scawebapp.cpp b/src/ext/Iis/ca/scawebapp.cpp new file mode 100644 index 00000000..a7e9cf82 --- /dev/null +++ b/src/ext/Iis/ca/scawebapp.cpp @@ -0,0 +1,194 @@ +// 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 +enum eWebApplicationQuery { wappqName = 1, wappqIsolation, wappqAllowSession, + wappqSessionTimeout, wappqBuffer, wappqParentPaths, + wappqDefaultScript, wappqScriptTimeout, + wappqServerDebugging, wappqClientDebugging, wappqAppPool, wappqApplication}; + + +HRESULT ScaGetWebApplication(MSIHANDLE /*hViewApplications*/, + LPCWSTR pwzApplication, + __in WCA_WRAPQUERY_HANDLE hWebAppQuery, + __in WCA_WRAPQUERY_HANDLE hWebAppExtQuery, + SCA_WEB_APPLICATION* pswapp) +{ + HRESULT hr = S_OK; + + MSIHANDLE hRec; + LPWSTR pwzData = NULL; + + // Reset back to the first record + WcaFetchWrappedReset(hWebAppQuery); + + // get the application information + hr = WcaFetchWrappedRecordWhereString(hWebAppQuery, wappqApplication, pwzApplication, &hRec); + if (S_OK == hr) + { + // application name + hr = WcaGetRecordString(hRec, wappqName, &pwzData); + ExitOnFailure(hr, "Failed to get Name of App"); + hr = ::StringCchCopyW(pswapp->wzName, countof(pswapp->wzName), pwzData); + if (HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER) == hr) + { + // The application name is sometimes truncated to IIS's supported length, so ignore insufficient buffer errors here + WcaLog(LOGMSG_VERBOSE, "Application name \"%ls\" truncated to fit IIS's supported %d-character length", pwzData, MAX_APP_NAME); + hr = S_OK; + } + ExitOnFailure(hr, "Failed to copy name string to webapp object"); + + hr = WcaGetRecordInteger(hRec, wappqIsolation, &pswapp->iIsolation); + ExitOnFailure(hr, "Failed to get App isolation: '%ls'", pswapp->wzName); + + hr = WcaGetRecordInteger(hRec, wappqAllowSession, &pswapp->fAllowSessionState); + + hr = WcaGetRecordInteger(hRec, wappqSessionTimeout, &pswapp->iSessionTimeout); + + hr = WcaGetRecordInteger(hRec, wappqBuffer, &pswapp->fBuffer); + + hr = WcaGetRecordInteger(hRec, wappqParentPaths, &pswapp->fParentPaths); + + hr = WcaGetRecordString(hRec, wappqDefaultScript, &pwzData); + ExitOnFailure(hr, "Failed to get default scripting language for App: '%ls'", pswapp->wzName); + hr = ::StringCchCopyW(pswapp->wzDefaultScript, countof(pswapp->wzDefaultScript), pwzData); + ExitOnFailure(hr, "Failed to copy default script string to webapp object"); + + // asp script timeout + hr = WcaGetRecordInteger(hRec, wappqScriptTimeout, &pswapp->iScriptTimeout); + ExitOnFailure(hr, "Failed to get scripting timeout for App: '%ls'", pswapp->wzName); + + // asp server-side script debugging + hr = WcaGetRecordInteger(hRec, wappqServerDebugging, &pswapp->fServerDebugging); + + // asp client-side script debugging + hr = WcaGetRecordInteger(hRec, wappqClientDebugging, &pswapp->fClientDebugging); + + hr = WcaGetRecordString(hRec, wappqAppPool, &pwzData); + ExitOnFailure(hr, "Failed to get AppPool for App: '%ls'", pswapp->wzName); + hr = ::StringCchCopyW(pswapp->wzAppPool, countof(pswapp->wzAppPool), pwzData); + ExitOnFailure(hr, "failed to copy AppPool: '%ls' for App: '%ls'", pwzData, pswapp->wzName); + + // app extensions + hr = ScaWebAppExtensionsRead(pwzApplication, hWebAppExtQuery, &pswapp->pswappextList); + ExitOnFailure(hr, "Failed to read AppExtensions for App: '%ls'", pswapp->wzName); + + hr = S_OK; + } + else if (E_NOMOREITEMS == hr) + { + WcaLog(LOGMSG_STANDARD, "Error: Cannot locate IIsWebApplication.Application='%ls'", pwzApplication); + hr = E_FAIL; + } + else + ExitOnFailure(hr, "Error matching Application rows"); + + // Let's check that there isn't more than one record found - if there is, throw an assert like WcaFetchSingleRecord() would + HRESULT hrTemp = WcaFetchWrappedRecordWhereString(hWebAppQuery, wappqApplication, pwzApplication, &hRec); + if (SUCCEEDED(hrTemp)) + { + AssertSz(E_NOMOREITEMS == hrTemp, "ScaGetWebApplication found more than one record"); + } + +LExit: + ReleaseStr(pwzData); + + return hr; +} + + +HRESULT ScaWriteWebApplication(IMSAdminBase* piMetabase, LPCWSTR wzRootOfWeb, + SCA_WEB_APPLICATION* pswapp, SCA_APPPOOL * psapList) +{ + HRESULT hr = S_OK; + WCHAR wzAppPoolName[MAX_PATH]; + + hr = ScaCreateApp(piMetabase, wzRootOfWeb, pswapp->iIsolation); + ExitOnFailure(hr, "Failed to create ASP App"); + + // Medium Isolation seems to have to be set through the metabase + if (2 == pswapp->iIsolation) + { + hr = ScaWriteMetabaseValue(piMetabase, wzRootOfWeb, NULL, MD_APP_ISOLATED, METADATA_INHERIT, IIS_MD_UT_WAM, DWORD_METADATA, (LPVOID)((DWORD_PTR)pswapp->iIsolation)); + ExitOnFailure(hr, "Failed to write isolation value for App: '%ls'", pswapp->wzName); + } + + // application name + hr = ScaWriteMetabaseValue(piMetabase, wzRootOfWeb, NULL, MD_APP_FRIENDLY_NAME, METADATA_INHERIT, IIS_MD_UT_WAM, STRING_METADATA, pswapp->wzName); + ExitOnFailure(hr, "Failed to write Name of App: '%ls'", pswapp->wzName); + + // allow session state + if (MSI_NULL_INTEGER != pswapp->fAllowSessionState) + { + hr = ScaWriteMetabaseValue(piMetabase, wzRootOfWeb, NULL, MD_ASP_ALLOWSESSIONSTATE, METADATA_INHERIT, ASP_MD_UT_APP, DWORD_METADATA, (LPVOID)((DWORD_PTR)pswapp->fAllowSessionState)); + ExitOnFailure(hr, "Failed to write allow session information for App: '%ls'", pswapp->wzName); + } + + // session timeout + if (MSI_NULL_INTEGER != pswapp->iSessionTimeout) + { + hr = ScaWriteMetabaseValue(piMetabase, wzRootOfWeb, NULL, MD_ASP_SESSIONTIMEOUT, METADATA_INHERIT, ASP_MD_UT_APP, DWORD_METADATA, (LPVOID)((DWORD_PTR)pswapp->iSessionTimeout)); + ExitOnFailure(hr, "Failed to write session timeout for App: '%ls'", pswapp->wzName); + } + + // asp buffering + if (MSI_NULL_INTEGER != pswapp->fBuffer) + { + hr = ScaWriteMetabaseValue(piMetabase, wzRootOfWeb, NULL, MD_ASP_BUFFERINGON, METADATA_INHERIT, ASP_MD_UT_APP, DWORD_METADATA, (LPVOID)((DWORD_PTR)pswapp->fBuffer)); + ExitOnFailure(hr, "Failed to write buffering flag for App: '%ls'", pswapp->wzName); + } + + // asp parent paths + if (MSI_NULL_INTEGER != pswapp->fParentPaths) + { + hr = ScaWriteMetabaseValue(piMetabase, wzRootOfWeb, NULL, MD_ASP_ENABLEPARENTPATHS, METADATA_INHERIT, ASP_MD_UT_APP, DWORD_METADATA, (LPVOID)((DWORD_PTR)pswapp->fParentPaths)); + ExitOnFailure(hr, "Failed to write parent paths flag for App: '%ls'", pswapp->wzName); + } + + // default scripting language + if (*pswapp->wzDefaultScript) + { + hr = ScaWriteMetabaseValue(piMetabase, wzRootOfWeb, NULL, MD_ASP_SCRIPTLANGUAGE, METADATA_INHERIT, ASP_MD_UT_APP, STRING_METADATA, pswapp->wzDefaultScript); + ExitOnFailure(hr, "Failed to write default scripting language for App: '%ls'", pswapp->wzName); + } + + // asp script timeout + if (MSI_NULL_INTEGER != pswapp->iScriptTimeout) + { + hr = ScaWriteMetabaseValue(piMetabase, wzRootOfWeb, NULL, MD_ASP_SCRIPTTIMEOUT, METADATA_INHERIT, ASP_MD_UT_APP, DWORD_METADATA, (LPVOID)((DWORD_PTR)pswapp->iScriptTimeout)); + ExitOnFailure(hr, "Failed to write script timeout for App: '%ls'", pswapp->wzName); + } + + // asp server-side script debugging + if (MSI_NULL_INTEGER != pswapp->fServerDebugging) + { + hr = ScaWriteMetabaseValue(piMetabase, wzRootOfWeb, NULL, MD_ASP_ENABLESERVERDEBUG, METADATA_INHERIT, ASP_MD_UT_APP, DWORD_METADATA, (LPVOID)((DWORD_PTR)pswapp->fServerDebugging)); + ExitOnFailure(hr, "Failed to write ASP server-side script debugging flag for App: '%ls'", pswapp->wzName); + } + + // asp server-side script debugging + if (MSI_NULL_INTEGER != pswapp->fClientDebugging) + { + hr = ScaWriteMetabaseValue(piMetabase, wzRootOfWeb, NULL, MD_ASP_ENABLECLIENTDEBUG, METADATA_INHERIT, ASP_MD_UT_APP, DWORD_METADATA, (LPVOID)((DWORD_PTR)pswapp->fClientDebugging)); + ExitOnFailure(hr, "Failed to write ASP client-side script debugging flag for App: '%ls'", pswapp->wzName); + } + + // AppPool + if (*pswapp->wzAppPool && NULL != psapList) + { + hr = ScaFindAppPool(piMetabase, pswapp->wzAppPool, wzAppPoolName, countof(wzAppPoolName), psapList); + ExitOnFailure(hr, "failed to find app pool: %ls", pswapp->wzAppPool); + hr = ScaWriteMetabaseValue(piMetabase, wzRootOfWeb, NULL, MD_APP_APPPOOL_ID, METADATA_INHERIT, IIS_MD_UT_SERVER, STRING_METADATA, wzAppPoolName); + ExitOnFailure(hr, "Failed to write default AppPool for App: '%ls'", pswapp->wzName); + } + + if (pswapp->pswappextList) + { + hr = ScaWebAppExtensionsWrite(piMetabase, wzRootOfWeb, pswapp->pswappextList); + ExitOnFailure(hr, "Failed to write AppExtensions for App: '%ls'", pswapp->wzName); + } + +LExit: + return hr; +} diff --git a/src/ext/Iis/ca/scawebapp.h b/src/ext/Iis/ca/scawebapp.h new file mode 100644 index 00000000..a4152116 --- /dev/null +++ b/src/ext/Iis/ca/scawebapp.h @@ -0,0 +1,42 @@ +#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. + + +#include "scaapppool.h" +#include "scawebappext.h" + +// global sql queries provided for optimization +extern LPCWSTR vcsWebApplicationQuery; +const int MAX_APP_NAME = 255; + +// structs +struct SCA_WEB_APPLICATION +{ + WCHAR wzName[MAX_APP_NAME + 1]; + + int iIsolation; + BOOL fAllowSessionState; + int iSessionTimeout; + BOOL fBuffer; + BOOL fParentPaths; + + WCHAR wzDefaultScript[MAX_DARWIN_COLUMN + 1]; + int iScriptTimeout; + BOOL fServerDebugging; + BOOL fClientDebugging; + WCHAR wzAppPool[MAX_DARWIN_COLUMN + 1]; + + SCA_WEB_APPLICATION_EXTENSION* pswappextList; +}; + + +// prototypes +HRESULT ScaGetWebApplication(MSIHANDLE hViewApplications, + LPCWSTR pwzApplication, + __in WCA_WRAPQUERY_HANDLE hWebAppQuery, + __in WCA_WRAPQUERY_HANDLE hWebAppExtQuery, + SCA_WEB_APPLICATION* pswapp); + +HRESULT ScaWriteWebApplication(IMSAdminBase* piMetabase, LPCWSTR wzRootOfWeb, + SCA_WEB_APPLICATION* pswapp, SCA_APPPOOL * psapList); + diff --git a/src/ext/Iis/ca/scawebapp7.cpp b/src/ext/Iis/ca/scawebapp7.cpp new file mode 100644 index 00000000..94e6bb18 --- /dev/null +++ b/src/ext/Iis/ca/scawebapp7.cpp @@ -0,0 +1,120 @@ +// 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" +HRESULT ScaWriteWebApplication7( + __in_z LPCWSTR wzWebName, + __in_z LPCWSTR wzRootOfWeb, + SCA_WEB_APPLICATION* pswapp, + SCA_APPPOOL * /*psapList*/ + ) +{ + HRESULT hr = S_OK; + + //all go to same web/root location tag + hr = ScaWriteConfigID(IIS_ASP_BEGIN); + ExitOnFailure(hr, "Failed to write WebApp ASP begin id"); + hr = ScaWriteConfigString(wzWebName); //site name key + ExitOnFailure(hr, "Failed to write app web key"); + hr = ScaWriteConfigString(wzRootOfWeb); //app path key + ExitOnFailure(hr, "Failed to write app web root"); + + // IIS7 Not Supported: Isolation + if (MSI_NULL_INTEGER != pswapp->iIsolation) + { + WcaLog(LOGMSG_TRACEONLY, "Not supported by IIS7: Isolation Mode, ignoring"); + } + + // allow session state + if (MSI_NULL_INTEGER != pswapp->fAllowSessionState) + { + //system.webServer/asp /session | allowSessionState + hr = ScaWriteConfigID(IIS_ASP_SESSIONSTATE); + ExitOnFailure(hr, "Failed to write WebApp ASP sessionstate id"); + hr = ScaWriteConfigInteger(pswapp->fAllowSessionState); + ExitOnFailure(hr, "Failed to write allow session information for App: '%ls'", pswapp->wzName); + } + + // session timeout + if (MSI_NULL_INTEGER != pswapp->iSessionTimeout) + { + //system.webServer/asp /session | timeout + hr = ScaWriteConfigID(IIS_ASP_SESSIONTIMEOUT); + ExitOnFailure(hr, "Failed to write WebApp ASP sessiontimepot id"); + hr = ScaWriteConfigInteger(pswapp->iSessionTimeout); + ExitOnFailure(hr, "Failed to write session timeout for App: '%ls'", pswapp->wzName); + } + + // asp buffering + if (MSI_NULL_INTEGER != pswapp->fBuffer) + { + //system.webServer/asp | bufferingOn + hr = ScaWriteConfigID(IIS_ASP_BUFFER); + ExitOnFailure(hr, "Failed to write WebApp ASP buffer id"); + hr = ScaWriteConfigInteger(pswapp->fBuffer); + ExitOnFailure(hr, "Failed to write buffering flag for App: '%ls'", pswapp->wzName); + } + + // asp parent paths + if (MSI_NULL_INTEGER != pswapp->fParentPaths) + { + //system.webServer/asp | enableParentPaths + hr = ScaWriteConfigID(IIS_ASP_PARENTPATHS); + ExitOnFailure(hr, "Failed to write WebApp ASP parentpaths id"); + hr = ScaWriteConfigInteger(pswapp->fParentPaths); + ExitOnFailure(hr, "Failed to write parent paths flag for App: '%ls'", pswapp->wzName); + } + + // default scripting language + if (*pswapp->wzDefaultScript) + { + //system.webServer/asp | scriptLanguage + hr = ScaWriteConfigID(IIS_ASP_SCRIPTLANG); + ExitOnFailure(hr, "Failed to write WebApp ASP script lang id"); + hr = ScaWriteConfigString(pswapp->wzDefaultScript); + ExitOnFailure(hr, "Failed to write default scripting language for App: '%ls'", pswapp->wzName); + } + + // asp script timeout + if (MSI_NULL_INTEGER != pswapp->iScriptTimeout) + { + //system.webServer/asp /limits | scriptTimeout + hr = ScaWriteConfigID(IIS_ASP_SCRIPTTIMEOUT); + ExitOnFailure(hr, "Failed to write WebApp ASP script timeout id"); + hr = ScaWriteConfigInteger(pswapp->iScriptTimeout); + ExitOnFailure(hr, "Failed to write script timeout for App: '%ls'", pswapp->wzName); + } + + // asp server-side script debugging + if (MSI_NULL_INTEGER != pswapp->fServerDebugging) + { + //system.webServer/asp | appAllowDebugging + hr = ScaWriteConfigID(IIS_ASP_SCRIPTSERVERDEBUG); + ExitOnFailure(hr, "Failed to write WebApp ASP script debug id"); + hr = ScaWriteConfigInteger(pswapp->fServerDebugging); + ExitOnFailure(hr, "Failed to write ASP server-side script debugging flag for App: '%ls'", pswapp->wzName); + } + + // asp client-side script debugging + if (MSI_NULL_INTEGER != pswapp->fClientDebugging) + { + //system.webServer/asp | appAllowClientDebug + hr = ScaWriteConfigID(IIS_ASP_SCRIPTCLIENTDEBUG); + ExitOnFailure(hr, "Failed to write WebApp ASP script debug id"); + hr = ScaWriteConfigInteger(pswapp->fClientDebugging); + ExitOnFailure(hr, "Failed to write ASP client-side script debugging flag for App: '%ls'", pswapp->wzName); + } + + //done with ASP application properties + hr = ScaWriteConfigID(IIS_ASP_END); + ExitOnFailure(hr, "Failed to write WebApp ASP begin id"); + + //write out app estensions + if (pswapp->pswappextList) + { + hr = ScaWebAppExtensionsWrite7(wzWebName, wzRootOfWeb, pswapp->pswappextList); + ExitOnFailure(hr, "Failed to write AppExtensions for App: '%ls'", pswapp->wzName); + } + +LExit: + return hr; +} diff --git a/src/ext/Iis/ca/scawebapp7.h b/src/ext/Iis/ca/scawebapp7.h new file mode 100644 index 00000000..e1d87f53 --- /dev/null +++ b/src/ext/Iis/ca/scawebapp7.h @@ -0,0 +1,10 @@ +#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 ScaWriteWebApplication7( + __in_z LPCWSTR wzWebName, + __in_z LPCWSTR wzRootOfWeb, + SCA_WEB_APPLICATION* pswapp, + SCA_APPPOOL * psapList + ); diff --git a/src/ext/Iis/ca/scawebappext.cpp b/src/ext/Iis/ca/scawebappext.cpp new file mode 100644 index 00000000..cf3b9dd3 --- /dev/null +++ b/src/ext/Iis/ca/scawebappext.cpp @@ -0,0 +1,207 @@ +// 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" + +enum eWebAppExtensionQuery { wappextqExtension = 1, wappextqVerbs, wappextqExecutable, wappextqAttributes, wappextqApplication }; + +// prototypes for private helper functions +static HRESULT NewAppExt( + __out SCA_WEB_APPLICATION_EXTENSION** ppswappext + ); +static SCA_WEB_APPLICATION_EXTENSION* AddAppExtToList( + __in SCA_WEB_APPLICATION_EXTENSION* pswappextList, + __in SCA_WEB_APPLICATION_EXTENSION* pswappext + ); + + + +HRESULT ScaWebAppExtensionsRead( + __in LPCWSTR wzApplication, + __in WCA_WRAPQUERY_HANDLE hWebAppExtQuery, + __inout SCA_WEB_APPLICATION_EXTENSION** ppswappextList + ) +{ + HRESULT hr = S_OK; + MSIHANDLE hRec; + + SCA_WEB_APPLICATION_EXTENSION* pswappext = NULL; + LPWSTR pwzData = NULL; + + // Reset back to the first record + WcaFetchWrappedReset(hWebAppExtQuery); + + // get the application extension information + while (S_OK == (hr = WcaFetchWrappedRecordWhereString(hWebAppExtQuery, wappextqApplication, wzApplication, &hRec))) + { + hr = NewAppExt(&pswappext); + ExitOnFailure(hr, "failed to create new web app extension"); + + // get the extension + hr = WcaGetRecordString(hRec, wappextqExtension, &pwzData); + ExitOnFailure(hr, "Failed to get Web Application Extension"); + hr = ::StringCchCopyW(pswappext->wzExtension, countof(pswappext->wzExtension), pwzData); + ExitOnFailure(hr, "Failed to copy extension string to webappext object"); + + // application extension verbs + hr = WcaGetRecordString(hRec, wappextqVerbs, &pwzData); + ExitOnFailure(hr, "Failed to get Verbs for Application: '%ls'", wzApplication); + hr = ::StringCchCopyW(pswappext->wzVerbs, countof(pswappext->wzVerbs), pwzData); + ExitOnFailure(hr, "Failed to copy verbs string to webappext object"); + + // extension executeable + hr = WcaGetRecordString(hRec, wappextqExecutable, &pwzData); + ExitOnFailure(hr, "Failed to get Executable for Application: '%ls'", wzApplication); + hr = ::StringCchCopyW(pswappext->wzExecutable, countof(pswappext->wzExecutable), pwzData); + ExitOnFailure(hr, "Failed to copy executable string to webappext object"); + + hr = WcaGetRecordInteger(hRec, wappextqAttributes, &pswappext->iAttributes); + if (S_FALSE == hr) + { + pswappext->iAttributes = 0; + hr = S_OK; + } + ExitOnFailure(hr, "Failed to get App isolation"); + + *ppswappextList = AddAppExtToList(*ppswappextList, pswappext); + pswappext = NULL; // set the appext NULL so it doesn't accidentally get freed below + } + + if (E_NOMOREITEMS == hr) + { + hr = S_OK; + } + +LExit: + // if anything was left over after an error clean it all up + if (pswappext) + { + ScaWebAppExtensionsFreeList(pswappext); + } + + ReleaseStr(pwzData); + + return hr; +} + + + +HRESULT ScaWebAppExtensionsWrite( + __in IMSAdminBase* piMetabase, + __in LPCWSTR wzRootOfWeb, + __in SCA_WEB_APPLICATION_EXTENSION* pswappextList + ) +{ + HRESULT hr = S_OK; + + LPWSTR wzAppExt = NULL; + DWORD cchAppExt; + WCHAR wzAppExtension[1024]; + WCHAR wzAppExtensions[65536]; + SCA_WEB_APPLICATION_EXTENSION* pswappext = NULL; + + if (!pswappextList) + { + ExitFunction(); + } + + ::ZeroMemory(wzAppExtensions, sizeof(wzAppExtensions)); + wzAppExt = wzAppExtensions; + cchAppExt = countof(wzAppExtensions); + pswappext = pswappextList; + + while (pswappext) + { + // if all (represented by "*" or blank) + if (0 == lstrcmpW(pswappext->wzExtension, L"*") || 0 == lstrlenW(pswappext->wzExtension)) + { + hr = ::StringCchPrintfW(wzAppExtension, countof(wzAppExtension), L"*,%s,%d", pswappext->wzExecutable, pswappext->iAttributes); + ExitOnFailure(hr, "Failed to format *,executable,attributes string"); + } + else + { + hr = ::StringCchPrintfW(wzAppExtension, countof(wzAppExtension), L".%s,%s,%d", pswappext->wzExtension, pswappext->wzExecutable, pswappext->iAttributes); + ExitOnFailure(hr, "Failed to format extension,executable,attributes string"); + } + + // if verbs were specified and not the keyword "all" + if (pswappext->wzVerbs[0] && CSTR_EQUAL != CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, pswappext->wzVerbs, -1, L"all", -1)) + { + hr = ::StringCchCatW(wzAppExtension, countof(wzAppExtension), L","); + ExitOnFailure(hr, "Failed to concatenate comma to app extension string"); + hr = ::StringCchCatW(wzAppExtension, countof(wzAppExtension), pswappext->wzVerbs); + ExitOnFailure(hr, "Failed to concatenate verb to app extension string"); + } + + hr = ::StringCchCopyW(wzAppExt, cchAppExt, wzAppExtension); + ExitOnFailure(hr, "Failed to copy app extension string"); + wzAppExt += lstrlenW(wzAppExtension) + 1; + cchAppExt -= lstrlenW(wzAppExtension) + 1; + pswappext = pswappext->pswappextNext; + } + + if (*wzAppExtensions) + { + hr = ScaWriteMetabaseValue(piMetabase, wzRootOfWeb, NULL, MD_SCRIPT_MAPS, METADATA_INHERIT, IIS_MD_UT_FILE, MULTISZ_METADATA, wzAppExtensions); + ExitOnFailure(hr, "Failed to write AppExtension: '%ls'", wzAppExtension); + } + +LExit: + return hr; +} + + +void ScaWebAppExtensionsFreeList( + __in SCA_WEB_APPLICATION_EXTENSION* pswappextList + ) +{ + SCA_WEB_APPLICATION_EXTENSION* pswappextDelete = pswappextList; + while (pswappextList) + { + pswappextDelete = pswappextList; + pswappextList = pswappextList->pswappextNext; + + MemFree(pswappextDelete); + } +} + + + +// private helper functions + +static HRESULT NewAppExt( + __out SCA_WEB_APPLICATION_EXTENSION** ppswappext + ) +{ + HRESULT hr = S_OK; + SCA_WEB_APPLICATION_EXTENSION* pswappext = static_cast(MemAlloc(sizeof(SCA_WEB_APPLICATION_EXTENSION), TRUE)); + ExitOnNull(pswappext, hr, E_OUTOFMEMORY, "failed to allocate memory for new web app ext element"); + + *ppswappext = pswappext; + +LExit: + return hr; +} + + +static SCA_WEB_APPLICATION_EXTENSION* AddAppExtToList( + __in SCA_WEB_APPLICATION_EXTENSION* pswappextList, + __in SCA_WEB_APPLICATION_EXTENSION* pswappext + ) +{ + if (pswappextList) + { + SCA_WEB_APPLICATION_EXTENSION* pswappextT = pswappextList; + while (pswappextT->pswappextNext) + { + pswappextT = pswappextT->pswappextNext; + } + + pswappextT->pswappextNext = pswappext; + } + else + { + pswappextList = pswappext; + } + + return pswappextList; +} diff --git a/src/ext/Iis/ca/scawebappext.h b/src/ext/Iis/ca/scawebappext.h new file mode 100644 index 00000000..71adfd00 --- /dev/null +++ b/src/ext/Iis/ca/scawebappext.h @@ -0,0 +1,32 @@ +#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 SCA_WEB_APPLICATION_EXTENSION +{ + WCHAR wzExtension[MAX_DARWIN_COLUMN + 1]; + + WCHAR wzVerbs[MAX_DARWIN_COLUMN + 1]; + WCHAR wzExecutable[MAX_DARWIN_COLUMN + 1]; + int iAttributes; + + SCA_WEB_APPLICATION_EXTENSION* pswappextNext; +}; + + +// prototypes +HRESULT ScaWebAppExtensionsRead( + __in LPCWSTR wzApplication, + __in WCA_WRAPQUERY_HANDLE hWebAppExtQuery, + __inout SCA_WEB_APPLICATION_EXTENSION** ppswappextList + ); + +HRESULT ScaWebAppExtensionsWrite( + __in IMSAdminBase* piMetabase, + __in LPCWSTR wzRootOfWeb, + __in SCA_WEB_APPLICATION_EXTENSION* pswappextList + ); + +void ScaWebAppExtensionsFreeList( + __in SCA_WEB_APPLICATION_EXTENSION* pswappextList + ); diff --git a/src/ext/Iis/ca/scawebappext7.cpp b/src/ext/Iis/ca/scawebappext7.cpp new file mode 100644 index 00000000..50d3172f --- /dev/null +++ b/src/ext/Iis/ca/scawebappext7.cpp @@ -0,0 +1,61 @@ +// 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" + +HRESULT ScaWebAppExtensionsWrite7( + __in_z LPCWSTR wzWebName, + __in_z LPCWSTR wzRootOfWeb, + __in SCA_WEB_APPLICATION_EXTENSION* pswappextList + ) +{ + HRESULT hr = S_OK; + SCA_WEB_APPLICATION_EXTENSION* pswappext = NULL; + + if (!pswappextList) + { + ExitFunction1(hr = S_OK); + } + + //create the Extension for this vdir application + //all go to same web/root location tag + hr = ScaWriteConfigID(IIS_APPEXT_BEGIN); + ExitOnFailure(hr, "Failed to write webappext begin id"); + hr = ScaWriteConfigString(wzWebName); //site name key + ExitOnFailure(hr, "Failed to write app web key"); + hr = ScaWriteConfigString(wzRootOfWeb); //app path key + ExitOnFailure(hr, "Failed to write app web key"); + + pswappext = pswappextList; + + while (pswappext) + { + //create the Extension for this vdir application + hr = ScaWriteConfigID(IIS_APPEXT); + ExitOnFailure(hr, "Failed to write webappext begin id"); + + if (*pswappext->wzExtension) + { + hr = ScaWriteConfigString(pswappext->wzExtension); + } + else // blank means "*" (all) + { + hr = ScaWriteConfigString(L"*"); + } + ExitOnFailure(hr, "Failed to write extension"); + + hr = ScaWriteConfigString(pswappext->wzExecutable); + ExitOnFailure(hr, "Failed to write extension executable"); + + hr = ScaWriteConfigString(pswappext->wzVerbs); + ExitOnFailure(hr, "Failed to write extension verbs"); + + pswappext = pswappext->pswappextNext; + } + + hr = ScaWriteConfigID(IIS_APPEXT_END); + ExitOnFailure(hr, "Failed to write webappext begin id"); + +LExit: + return hr; +} + diff --git a/src/ext/Iis/ca/scawebappext7.h b/src/ext/Iis/ca/scawebappext7.h new file mode 100644 index 00000000..55c8b5fc --- /dev/null +++ b/src/ext/Iis/ca/scawebappext7.h @@ -0,0 +1,9 @@ +#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 ScaWebAppExtensionsWrite7( + __in_z LPCWSTR wzWebName, + __in_z LPCWSTR wzRootOfWeb, + __in SCA_WEB_APPLICATION_EXTENSION* pswappextList + ); diff --git a/src/ext/Iis/ca/scawebdir.cpp b/src/ext/Iis/ca/scawebdir.cpp new file mode 100644 index 00000000..26a7b32b --- /dev/null +++ b/src/ext/Iis/ca/scawebdir.cpp @@ -0,0 +1,241 @@ +// 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 +enum eWebDirQuery { wdqWeb = 1, wdqWebDir, wdqComponent , wdqPath, wdqProperties, wdqApplication, wdqInstalled, wdqAction }; + +// prototypes +static void AddWebDirToList(SCA_WEBDIR** ppswdList, SCA_WEBDIR *pswd); + +static SCA_WEBDIR* NewWebDir(); +static void FreeWebDir(SCA_WEBDIR *pswd); + + +UINT __stdcall ScaWebDirsRead( + __in IMSAdminBase* piMetabase, + __in SCA_WEB* pswList, + __in WCA_WRAPQUERY_HANDLE hUserQuery, + __in WCA_WRAPQUERY_HANDLE hWebBaseQuery, + __in WCA_WRAPQUERY_HANDLE hWebDirPropQuery, + __in WCA_WRAPQUERY_HANDLE hWebAppQuery, + __in WCA_WRAPQUERY_HANDLE hWebAppExtQuery, + __inout LPWSTR *ppwzCustomActionData, + __out SCA_WEBDIR** ppswdList + ) +{ + Assert(piMetabase && ppswdList); + + HRESULT hr = S_OK; + MSIHANDLE hRec; + + LPWSTR pwzData = NULL; + SCA_WEBDIR* pswd; + WCA_WRAPQUERY_HANDLE hWrapQuery = NULL; + + hr = WcaBeginUnwrapQuery(&hWrapQuery, ppwzCustomActionData); + ExitOnFailure(hr, "Failed to unwrap query for ScaWebDirsRead"); + + if (0 == WcaGetQueryRecords(hWrapQuery)) + { + WcaLog(LOGMSG_VERBOSE, "Skipping ScaInstallWebDirs() because IIsWebDir table not present"); + ExitFunction1(hr = S_FALSE); + } + + // loop through all the web directories + while (S_OK == (hr = WcaFetchWrappedRecord(hWrapQuery, &hRec))) + { + pswd = NewWebDir(); + ExitOnNull(pswd, hr, E_OUTOFMEMORY, "Failed to allocate memory for web dir object in memory"); + + // get component install state + hr = WcaGetRecordString(hRec, wdqComponent, &pwzData); + ExitOnFailure(hr, "Failed to get Component for WebDirs"); + hr = ::StringCchCopyW(pswd->wzComponent, countof(pswd->wzComponent), pwzData); + ExitOnFailure(hr, "Failed to copy component string to webdir object"); + + hr = WcaGetRecordInteger(hRec, wdqInstalled, (int *)&pswd->isInstalled); + ExitOnFailure(hr, "Failed to get Component installed state for webdir"); + + hr = WcaGetRecordInteger(hRec, wdqAction, (int *)&pswd->isAction); + ExitOnFailure(hr, "Failed to get Component action state for webdir"); + + // If this record has a component and no action is being taken for it, skip processing it entirely + if (0 < lstrlenW(pswd->wzComponent) && !WcaIsInstalling(pswd->isInstalled, pswd->isAction) + && !WcaIsUninstalling(pswd->isInstalled, pswd->isAction) && !WcaIsReInstalling(pswd->isInstalled, pswd->isAction)) + { + FreeWebDir(pswd); + pswd = NULL; + continue; + } + + hr = WcaGetRecordString(hRec, wdqWeb, &pwzData); + ExitOnFailure(hr, "Failed to get Web for WebDir"); + + hr = ScaWebsGetBase(piMetabase, pswList, pwzData, pswd->wzWebBase, countof(pswd->wzWebBase), hWebBaseQuery); + if (WcaIsUninstalling(pswd->isInstalled, pswd->isAction)) + { + // If we're uninstalling, ignore any failure to find the existing web + hr = S_OK; + } + + ExitOnFailure(hr, "Failed to get base of web for WebDir"); + + hr = WcaGetRecordString(hRec, wdqPath, &pwzData); + ExitOnFailure(hr, "Failed to get Path for WebDir"); + + hr = ::StringCchPrintfW(pswd->wzWebDirRoot, countof(pswd->wzWebDirRoot), L"%s/Root/%s", pswd->wzWebBase, pwzData); + ExitOnFailure(hr, "Failed to format webdir root string"); + + // get the directory properties for this web + hr = WcaGetRecordString(hRec, wdqProperties, &pwzData); + ExitOnFailure(hr, "Failed to get security identifier for WebDir"); + if (*pwzData) + { + hr = ScaGetWebDirProperties(pwzData, hUserQuery, hWebDirPropQuery, &pswd->swp); + ExitOnFailure(hr, "Failed to get properties for WebDir"); + + pswd->fHasProperties = TRUE; + } + + // get the application information for this web directory + hr = WcaGetRecordString(hRec, wdqApplication, &pwzData); + ExitOnFailure(hr, "Failed to get application identifier for WebDir"); + if (*pwzData) + { + hr = ScaGetWebApplication(NULL, pwzData, hWebAppQuery, hWebAppExtQuery, &pswd->swapp); + ExitOnFailure(hr, "Failed to get application for WebDir"); + + pswd->fHasApplication = TRUE; + } + + AddWebDirToList(ppswdList, pswd); + } + + if (E_NOMOREITEMS == hr) + { + hr = S_OK; + } + ExitOnFailure(hr, "Failure while processing WebDirs"); + +LExit: + WcaFinishUnwrapQuery(hWrapQuery); + + ReleaseStr(pwzData); + + return hr; +} + + +HRESULT ScaWebDirsInstall(IMSAdminBase* piMetabase, SCA_WEBDIR* pswdList, SCA_APPPOOL * psapList) +{ + HRESULT hr = S_OK; + SCA_WEBDIR* pswd = pswdList; + int i; + + while (pswd) + { + // On reinstall, we have to uninstall the old application, otherwise a duplicate will be created + if (WcaIsReInstalling(pswd->isInstalled, pswd->isAction)) + { + if (pswd->fHasApplication) + { + hr = ScaDeleteApp(piMetabase, pswd->wzWebDirRoot); + ExitOnFailure(hr, "Failed to remove application for WebDir as part of a reinstall"); + } + } + + // if we are installing the web site + if (WcaIsInstalling(pswd->isInstalled, pswd->isAction)) + { + hr = ScaCreateMetabaseKey(piMetabase, pswd->wzWebDirRoot, L""); + ExitOnFailure(hr, "Failed to create key for WebDir"); + hr = ScaWriteMetabaseValue(piMetabase, pswd->wzWebDirRoot, L"", MD_KEY_TYPE, METADATA_NO_ATTRIBUTES, IIS_MD_UT_SERVER, STRING_METADATA, (LPVOID)L"IIsWebDirectory"); + ExitOnFailure(hr, "Failed to write key type for for WebDir"); + i = 0x4000003e; // 1073741886: default directory browsing rights + hr = ScaWriteMetabaseValue(piMetabase, pswd->wzWebDirRoot, L"", MD_DIRECTORY_BROWSING, METADATA_INHERIT, IIS_MD_UT_FILE, DWORD_METADATA, (LPVOID)((DWORD_PTR)i)); + ExitOnFailure(hr, "Failed to set directory browsing for WebDir"); + + // get the security information for this web + if (pswd->fHasProperties) + { + ScaWriteWebDirProperties(piMetabase, pswd->wzWebDirRoot, &pswd->swp); + ExitOnFailure(hr, "Failed to write properties for WebDir"); + } + + // get the application information for this web directory + if (pswd->fHasApplication) + { + hr = ScaWriteWebApplication(piMetabase, pswd->wzWebDirRoot, &pswd->swapp, psapList); + ExitOnFailure(hr, "Failed to write application for WebDir"); + } + } + + pswd = pswd->pswdNext; + } + +LExit: + return hr; +} + + +HRESULT ScaWebDirsUninstall(IMSAdminBase* piMetabase, SCA_WEBDIR* pswdList) +{ + Assert(piMetabase); + + HRESULT hr = S_OK; + SCA_WEBDIR* pswd = pswdList; + + while (pswd) + { + if (WcaIsUninstalling(pswd->isInstalled, pswd->isAction)) + { + // remove the application from this web directory + if (pswd->fHasApplication) + { + hr = ScaDeleteApp(piMetabase, pswd->wzWebDirRoot); + ExitOnFailure(hr, "Failed to remove application for WebDir"); + } + + hr = ScaDeleteMetabaseKey(piMetabase, pswd->wzWebDirRoot, L""); + ExitOnFailure(hr, "Failed to remove WebDir '%ls' from metabase", pswd->wzKey); + } + + pswd = pswd->pswdNext; + } + +LExit: + return hr; +} + + +static SCA_WEBDIR* NewWebDir() +{ + SCA_WEBDIR* pswd = static_cast(MemAlloc(sizeof(SCA_WEBDIR), TRUE)); + Assert(pswd); + return pswd; +} + +static void FreeWebDir(SCA_WEBDIR *pswd) +{ + MemFree(pswd); +} + +void ScaWebDirsFreeList(SCA_WEBDIR* pswdList) +{ + SCA_WEBDIR* pswdDelete = pswdList; + while (pswdList) + { + pswdDelete = pswdList; + pswdList = pswdList->pswdNext; + + FreeWebDir(pswdDelete); + } +} + + +static void AddWebDirToList(SCA_WEBDIR** ppswdList, SCA_WEBDIR *pswd) +{ + pswd->pswdNext = *ppswdList; + *ppswdList = pswd; +} diff --git a/src/ext/Iis/ca/scawebdir.h b/src/ext/Iis/ca/scawebdir.h new file mode 100644 index 00000000..0b594532 --- /dev/null +++ b/src/ext/Iis/ca/scawebdir.h @@ -0,0 +1,57 @@ +#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 SCA_WEBDIR +{ + // darwin information + WCHAR wzKey[MAX_DARWIN_KEY + 1]; + WCHAR wzComponent[MAX_DARWIN_KEY + 1]; + INSTALLSTATE isInstalled; + INSTALLSTATE isAction; + + // metabase information + WCHAR wzWebKey[MAX_DARWIN_KEY + 1]; + WCHAR wzWebBase[METADATA_MAX_NAME_LEN + 1]; + WCHAR wzWebDirRoot[METADATA_MAX_NAME_LEN + 1]; + + // iis configuation information + WCHAR wzDirectory[MAX_PATH]; + + BOOL fHasProperties; + SCA_WEB_PROPERTIES swp; + + BOOL fHasApplication; + SCA_WEB_APPLICATION swapp; + + SCA_WEBDIR* pswdNext; +}; + + +// prototypes +UINT __stdcall ScaWebDirsRead( + __in IMSAdminBase* piMetabase, + __in SCA_WEB* pswList, + __in WCA_WRAPQUERY_HANDLE hUserQuery, + __in WCA_WRAPQUERY_HANDLE hWebBaseQuery, + __in WCA_WRAPQUERY_HANDLE hWebDirPropQuery, + __in WCA_WRAPQUERY_HANDLE hWebAppQuery, + __in WCA_WRAPQUERY_HANDLE hWebAppExtQuery, + __inout LPWSTR *ppwzCustomActionData, + __out SCA_WEBDIR** ppswdList + ); + +HRESULT ScaWebDirsInstall( + __in IMSAdminBase* piMetabase, + __in SCA_WEBDIR* pswdList, + __in SCA_APPPOOL* psapList + ); + +HRESULT ScaWebDirsUninstall( + __in IMSAdminBase* piMetabase, + __in SCA_WEBDIR* pswdList + ); + +void ScaWebDirsFreeList( + __in SCA_WEBDIR* pswdList + ); diff --git a/src/ext/Iis/ca/scawebdir7.cpp b/src/ext/Iis/ca/scawebdir7.cpp new file mode 100644 index 00000000..5ead0470 --- /dev/null +++ b/src/ext/Iis/ca/scawebdir7.cpp @@ -0,0 +1,219 @@ +// 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 +static LPCWSTR vcsWebDirQuery7 = L"SELECT `Web_`, `WebDir`, `Component_`, `Path`, `DirProperties_`, `Application_`" + L"FROM `IIsWebDir`"; + +enum eWebDirQuery { wdqWeb = 1, wdqWebDir, wdqComponent , wdqPath, wdqProperties, wdqApplication, wdqInstalled, wdqAction }; + +// prototypes +static HRESULT AddWebDirToList(SCA_WEBDIR7** ppswdList); + + +UINT __stdcall ScaWebDirsRead7( + __in SCA_WEB7* pswList, + __in WCA_WRAPQUERY_HANDLE hUserQuery, + __in WCA_WRAPQUERY_HANDLE /*hWebBaseQuery*/, + __in WCA_WRAPQUERY_HANDLE hWebDirPropQuery, + __in WCA_WRAPQUERY_HANDLE hWebAppQuery, + __in WCA_WRAPQUERY_HANDLE hWebAppExtQuery, + __inout LPWSTR *ppwzCustomActionData, + __out SCA_WEBDIR7** ppswdList + ) +{ + HRESULT hr = S_OK; + MSIHANDLE hRec; + + LPWSTR pwzData = NULL; + SCA_WEBDIR7* pswd; + WCA_WRAPQUERY_HANDLE hWrapQuery = NULL; + + hr = WcaBeginUnwrapQuery(&hWrapQuery, ppwzCustomActionData); + ExitOnFailure(hr, "Failed to unwrap query for ScaWebDirsRead7"); + + if (0 == WcaGetQueryRecords(hWrapQuery)) + { + WcaLog(LOGMSG_VERBOSE, "Skipping ScaInstallWebDirs7() because IIsWebDir table not present"); + ExitFunction1(hr = S_FALSE); + } + + // loop through all the web directories + while (S_OK == (hr = WcaFetchWrappedRecord(hWrapQuery, &hRec))) + { + hr = AddWebDirToList(ppswdList); + ExitOnFailure(hr, "failed to add web dir to list"); + + pswd = *ppswdList; + ExitOnNull(pswd, hr, E_INVALIDARG, "No web dir provided"); + + // get component install state + hr = WcaGetRecordString(hRec, wdqComponent, &pwzData); + ExitOnFailure(hr, "Failed to get Component for WebDirs"); + hr = ::StringCchCopyW(pswd->wzComponent, countof(pswd->wzComponent), pwzData); + ExitOnFailure(hr, "Failed to copy component string to webdir object"); + + hr = WcaGetRecordInteger(hRec, wdqInstalled, (int *)&pswd->isInstalled); + ExitOnFailure(hr, "Failed to get Component installed state for webdir"); + + hr = WcaGetRecordInteger(hRec, wdqAction, (int *)&pswd->isAction); + ExitOnFailure(hr, "Failed to get Component action state for webdir"); + + hr = WcaGetRecordString(hRec, wdqWeb, &pwzData); + ExitOnFailure(hr, "Failed to get Web for WebDir"); + + // get the web key + hr = ScaWebsGetBase7(pswList, pwzData, pswd->wzWebSite, countof(pswd->wzWebSite)); + if (S_FALSE == hr) + { + hr = HRESULT_FROM_WIN32(ERROR_NOT_FOUND); + ExitOnFailure(hr, "Failed to get base of web for WebDir"); + } + ExitOnFailure(hr, "Failed to format webdir root string"); + + hr = WcaGetRecordString(hRec, wdqPath, &pwzData); + ExitOnFailure(hr, "Failed to get Path for WebDir"); + + hr = ::StringCchCopyW(pswd->wzPath, countof(pswd->wzPath), pwzData); + ExitOnFailure(hr, "Failed to copy path for WebDir"); + + // get the directory properties for this web + hr = WcaGetRecordString(hRec, wdqProperties, &pwzData); + ExitOnFailure(hr, "Failed to get security identifier for WebDir"); + if (*pwzData) + { + hr = ScaGetWebDirProperties(pwzData, hUserQuery, hWebDirPropQuery, &pswd->swp); + ExitOnFailure(hr, "Failed to get properties for WebDir"); + + pswd->fHasProperties = TRUE; + } + + // get the application information for this web directory + hr = WcaGetRecordString(hRec, wdqApplication, &pwzData); + ExitOnFailure(hr, "Failed to get application identifier for WebDir"); + if (*pwzData) + { + hr = ScaGetWebApplication(NULL, pwzData, hWebAppQuery, hWebAppExtQuery, &pswd->swapp); + ExitOnFailure(hr, "Failed to get application for WebDir"); + + pswd->fHasApplication = TRUE; + } + } + + if (E_NOMOREITEMS == hr) + { + hr = S_OK; + } + ExitOnFailure(hr, "Failure while processing WebDirs"); + +LExit: + WcaFinishUnwrapQuery(hWrapQuery); + + ReleaseStr(pwzData); + + return hr; +} + + +HRESULT ScaWebDirsInstall7(SCA_WEBDIR7* pswdList, SCA_APPPOOL * psapList) +{ + HRESULT hr = S_OK; + SCA_WEBDIR7* pswd = pswdList; + + while (pswd) + { + // if we are installing the web site + if (WcaIsInstalling(pswd->isInstalled, pswd->isAction)) + { + hr = ScaWriteConfigID(IIS_WEBDIR); + ExitOnFailure(hr, "Failed to write WebDir ID"); + + hr = ScaWriteConfigID(IIS_CREATE); + ExitOnFailure(hr, "Failed to write WebDir action ID"); + + hr = ScaWriteConfigString(pswd->wzWebSite); + ExitOnFailure(hr, "Failed to write WebDir site"); + + hr = ScaWriteConfigString(pswd->wzPath); + ExitOnFailure(hr, "Failed to write WebDir path"); + + // get the security information for this web + if (pswd->fHasProperties) + { + ScaWriteWebDirProperties7(pswd->wzWebSite, pswd->wzPath, &pswd->swp); + ExitOnFailure(hr, "Failed to write properties for WebDir"); + } + + // get the application information for this web directory + if (pswd->fHasApplication) + { + hr = ScaWriteWebApplication7(pswd->wzWebSite, pswd->wzPath, &pswd->swapp, psapList); + ExitOnFailure(hr, "Failed to write application for WebDir"); + } + } + + pswd = pswd->pswdNext; + } + +LExit: + return hr; +} + + +HRESULT ScaWebDirsUninstall7(SCA_WEBDIR7* pswdList) +{ + HRESULT hr = S_OK; + SCA_WEBDIR7* pswd = pswdList; + + while (pswd) + { + if (WcaIsUninstalling(pswd->isInstalled, pswd->isAction)) + { + hr = ScaWriteConfigID(IIS_WEBDIR); + ExitOnFailure(hr, "Failed to write WebDir ID"); + + hr = ScaWriteConfigID(IIS_DELETE); + ExitOnFailure(hr, "Failed to write WebDir action ID"); + + hr = ScaWriteConfigString(pswd->wzWebSite); + ExitOnFailure(hr, "Failed to write WebDir site"); + + hr = ScaWriteConfigString(pswd->wzPath); + ExitOnFailure(hr, "Failed to write WebDir path"); + } + + pswd = pswd->pswdNext; + } + +LExit: + return hr; +} + + +void ScaWebDirsFreeList7(SCA_WEBDIR7* pswdList) +{ + SCA_WEBDIR7* pswdDelete = pswdList; + while (pswdList) + { + pswdDelete = pswdList; + pswdList = pswdList->pswdNext; + + MemFree(pswdDelete); + } +} + + +static HRESULT AddWebDirToList(SCA_WEBDIR7** ppswdList) +{ + HRESULT hr = S_OK; + + SCA_WEBDIR7* pswd = static_cast(MemAlloc(sizeof(SCA_WEBDIR7), TRUE)); + ExitOnNull(pswd, hr, E_OUTOFMEMORY, "failed to allocate element for web dir list"); + + pswd->pswdNext = *ppswdList; + *ppswdList = pswd; + +LExit: + return hr; +} diff --git a/src/ext/Iis/ca/scawebdir7.h b/src/ext/Iis/ca/scawebdir7.h new file mode 100644 index 00000000..c5c87e17 --- /dev/null +++ b/src/ext/Iis/ca/scawebdir7.h @@ -0,0 +1,51 @@ +#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 SCA_WEBDIR7 +{ + // darwin information + WCHAR wzKey[MAX_DARWIN_KEY + 1]; + WCHAR wzComponent[MAX_DARWIN_KEY + 1]; + INSTALLSTATE isInstalled; + INSTALLSTATE isAction; + + + // iis configuation information + WCHAR wzPath[MAX_PATH]; + WCHAR wzWebSite[MAX_PATH]; + + BOOL fHasProperties; + SCA_WEB_PROPERTIES swp; + + BOOL fHasApplication; + SCA_WEB_APPLICATION swapp; + + SCA_WEBDIR7* pswdNext; +}; + + +// prototypes +UINT __stdcall ScaWebDirsRead7( + __in SCA_WEB7* pswList, + __in WCA_WRAPQUERY_HANDLE hUserQuery, + __in WCA_WRAPQUERY_HANDLE hWebBaseQuery, + __in WCA_WRAPQUERY_HANDLE hWebDirPropQuery, + __in WCA_WRAPQUERY_HANDLE hWebAppQuery, + __in WCA_WRAPQUERY_HANDLE hWebAppExtQuery, + __inout LPWSTR *ppwzCustomActionData, + __out SCA_WEBDIR7** ppswdList + ); + +HRESULT ScaWebDirsInstall7( + __in SCA_WEBDIR7* pswdList, + __in SCA_APPPOOL* psapList + ); + +HRESULT ScaWebDirsUninstall7( + __in SCA_WEBDIR7* pswdList + ); + +void ScaWebDirsFreeList7( + __in SCA_WEBDIR7* pswdList + ); diff --git a/src/ext/Iis/ca/scaweberr.cpp b/src/ext/Iis/ca/scaweberr.cpp new file mode 100644 index 00000000..2441f006 --- /dev/null +++ b/src/ext/Iis/ca/scaweberr.cpp @@ -0,0 +1,371 @@ +// 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" + +enum eWebErrorQuery { weqErrorCode = 1, weqSubCode, weqParentType, weqParentValue, weqFile, weqURL }; + +static HRESULT AddWebErrorToList(SCA_WEB_ERROR** ppsweList); + +void ScaWebErrorFreeList(SCA_WEB_ERROR *psweList) +{ + SCA_WEB_ERROR *psweDelete = psweList; + while (psweList) + { + psweDelete = psweList; + psweList = psweList->psweNext; + + MemFree(psweDelete); + } +} + +HRESULT ScaWebErrorRead( + SCA_WEB_ERROR **ppsweList, + __inout LPWSTR *ppwzCustomActionData + ) +{ +// AssertSz(0, "Debug ScaWebErrorRead here"); + HRESULT hr = S_OK; + MSIHANDLE hRec; + LPWSTR pwzData = NULL; + SCA_WEB_ERROR* pswe; + WCA_WRAPQUERY_HANDLE hWrapQuery = NULL; + + ExitOnNull(ppsweList, hr, E_INVALIDARG, "Failed to read web error, because no web error was provided to read"); + + hr = WcaBeginUnwrapQuery(&hWrapQuery, ppwzCustomActionData); + ExitOnFailure(hr, "Failed to unwrap query for ScaAppPoolRead"); + + if (0 == WcaGetQueryRecords(hWrapQuery)) + { + WcaLog(LOGMSG_VERBOSE, "Skipping ScaWebErrorRead() - required tables not present."); + ExitFunction1(hr = S_FALSE); + } + + // loop through all the web errors + while (S_OK == (hr = WcaFetchWrappedRecord(hWrapQuery, &hRec))) + { + hr = AddWebErrorToList(ppsweList); + ExitOnFailure(hr, "failed to add web error to list"); + + pswe = *ppsweList; + + hr = WcaGetRecordInteger(hRec, weqErrorCode, &(pswe->iErrorCode)); + ExitOnFailure(hr, "failed to get IIsWebError.ErrorCode"); + + hr = WcaGetRecordInteger(hRec, weqSubCode, &(pswe->iSubCode)); + ExitOnFailure(hr, "failed to get IIsWebError.SubCode"); + + hr = WcaGetRecordInteger(hRec, weqParentType, &(pswe->iParentType)); + ExitOnFailure(hr, "failed to get IIsWebError.ParentType"); + + hr = WcaGetRecordString(hRec, weqParentValue, &pwzData); + ExitOnFailure(hr, "Failed to get IIsWebError.ParentValue"); + hr = ::StringCchCopyW(pswe->wzParentValue, countof(pswe->wzParentValue), pwzData); + ExitOnFailure(hr, "Failed to copy IIsWebError.ParentValue"); + + hr = WcaGetRecordString(hRec, weqFile, &pwzData); + ExitOnFailure(hr, "Failed to get IIsWebError.File"); + hr = ::StringCchCopyW(pswe->wzFile, countof(pswe->wzFile), pwzData); + ExitOnFailure(hr, "Failed to copy IIsWebError.File"); + + hr = WcaGetRecordString(hRec, weqURL, &pwzData); + ExitOnFailure(hr, "Failed to get IIsWebError.URL"); + hr = ::StringCchCopyW(pswe->wzURL, countof(pswe->wzURL), pwzData); + ExitOnFailure(hr, "Failed to copy IIsWebError.URL"); + + // If they've specified both a file and a URL, that's invalid + if (*(pswe->wzFile) && *(pswe->wzURL)) + ExitOnFailure(hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA), "Both File and URL specified for web error. File: %ls, URL: %ls", pswe->wzFile, pswe->wzURL); + } + + if (E_NOMOREITEMS == hr) + { + hr = S_OK; + } + ExitOnFailure(hr, "Failure while processing web errors"); + +LExit: + WcaFinishUnwrapQuery(hWrapQuery); + + ReleaseStr(pwzData); + + return hr; +} + +HRESULT ScaGetWebError(int iParentType, LPCWSTR wzParentValue, SCA_WEB_ERROR **ppsweList, SCA_WEB_ERROR **ppsweOut) +{ + HRESULT hr = S_OK; + SCA_WEB_ERROR* psweAdd = NULL; + SCA_WEB_ERROR* psweLast = NULL; + + *ppsweOut = NULL; + + if (!*ppsweList) + return hr; + + SCA_WEB_ERROR* pswe = *ppsweList; + while (pswe) + { + if (iParentType == pswe->iParentType && 0 == lstrcmpW(wzParentValue, pswe->wzParentValue)) + { + // Found a match, take this one out of the list and add it to the matched out list + psweAdd = pswe; + + if (psweLast) + { + // If we're not at the beginning of the list tell the last node about it's new next (since we're taking away it's current next) + psweLast->psweNext = psweAdd->psweNext; + } + else + { + // If we are at the beginning (no psweLast) update the beginning (since we're taking it) + *ppsweList = pswe->psweNext; + } + pswe = pswe->psweNext; // move on + + // Add the one we've removed to the beginning of the out list + psweAdd->psweNext = *ppsweOut; + *ppsweOut = psweAdd; + } + else + { + psweLast = pswe; // remember the last we that didn't match + pswe = pswe->psweNext; // move on + } + } + + return hr; +} + +HRESULT ScaWriteWebError(IMSAdminBase* piMetabase, int iParentType, LPCWSTR wzRoot, SCA_WEB_ERROR* psweList) +{ +// AssertSz(0, "Debug ScaWriteWebError here"); + Assert(*wzRoot && psweList); + + HRESULT hr = S_OK; + + DWORD cchData = 0; + LPWSTR pwzSearchKey = NULL; + LPWSTR pwz = NULL; + LPWSTR pwzErrors = NULL; + + LPWSTR pwzCodeSubCode = NULL; + LPWSTR pwzAcceptableCodeSubCode = NULL; + LPCWSTR wzFoundCodeSubCode = NULL; + DWORD_PTR dwFoundCodeSubCodeIndex = 0xFFFFFFFF; + BOOL fOldValueFound = FALSE; + LPWSTR pwzAcceptableErrors = NULL; + + LPWSTR pwzNewError = NULL; + + METADATA_RECORD mr; + ::ZeroMemory(&mr, sizeof(mr)); + + ExitOnNull(piMetabase, hr, E_INVALIDARG, "Failed to write web error, because no metabase was provided"); + ExitOnNull(wzRoot, hr, E_INVALIDARG, "Failed to write web error, because no root was provided"); + + // get the set of all valid custom errors from the metabase + mr.dwMDIdentifier = MD_CUSTOM_ERROR_DESC; + mr.dwMDAttributes = METADATA_INHERIT; + mr.dwMDUserType = IIS_MD_UT_SERVER; + mr.dwMDDataType = ALL_METADATA; + mr.dwMDDataLen = cchData = 0; + mr.pbMDData = NULL; + + hr = MetaGetValue(piMetabase, METADATA_MASTER_ROOT_HANDLE, L"/LM/W3SVC/Info", &mr); + ExitOnFailure(hr, "Unable to get set of acceptable error codes for this server."); + + pwzAcceptableErrors = reinterpret_cast(mr.pbMDData); + + // Check if web errors already exist here + mr.dwMDIdentifier = MD_CUSTOM_ERROR; + mr.dwMDAttributes = METADATA_INHERIT; + mr.dwMDUserType = IIS_MD_UT_SERVER; + mr.dwMDDataType = ALL_METADATA; + mr.dwMDDataLen = cchData = 0; + mr.pbMDData = NULL; + + hr = MetaGetValue(piMetabase, METADATA_MASTER_ROOT_HANDLE, wzRoot, &mr); + if (HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND) == hr || MD_ERROR_DATA_NOT_FOUND == hr) + { + // + // If we don't have one already, find an appropriate one to start with + // + + // we can walk up key by key and look for custom errors to inherit + + hr = StrAllocConcat(&pwzSearchKey, wzRoot, 0); + ExitOnFailure(hr, "Failed to copy root string: %ls", wzRoot); + + pwz = pwzSearchKey + lstrlenW(pwzSearchKey); + + while (NULL == pwzErrors) + { + // find the last slash + while (*pwz != '/' && pwz != pwzSearchKey) + pwz --; + + if (pwz == pwzSearchKey) + break; + + *pwz = L'\0'; + + // Try here. If it's not found, keep walking up the path + hr = MetaGetValue(piMetabase, METADATA_MASTER_ROOT_HANDLE, pwzSearchKey, &mr); + if (HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND) == hr || MD_ERROR_DATA_NOT_FOUND == hr) + hr = S_FALSE; + ExitOnFailure(hr, "failed to discover default error values to start with for web root: %ls while walking up the tree", wzRoot); + + if (S_OK == hr) + { + pwzErrors = reinterpret_cast(mr.pbMDData); + break; + } + + // Don't keep going if we're at the root + if (0 == lstrcmpW(pwz + 1, L"W3SVC")) + break; + } + } + else + { + pwzErrors = reinterpret_cast(mr.pbMDData); + } + ExitOnFailure(hr, "failed to discover default error values to start with for web root: %ls", wzRoot); + + // The above code should have come up with some value to start pwzErrors off with. Make sure it did. + if (NULL == pwzErrors) + { + ExitOnFailure(hr = E_UNEXPECTED, "failed to discover default error values to start with for web root: %ls", wzRoot); + } + + // Loop through the web errors + for (SCA_WEB_ERROR* pswe = psweList; pswe; pswe = pswe->psweNext) + { + // Assume that we will have to replace + fOldValueFound = TRUE; + + // If the subcode is 0, that means "*" in MD_CUSTOM_ERROR (thus the special formatting logic) + if (0 == pswe->iSubCode) + { + hr = StrAllocFormatted(&pwzCodeSubCode, L"%d,*", pswe->iErrorCode); + ExitOnFailure(hr, "failed to create error code string while installing web error"); + } + else + { + hr = StrAllocFormatted(&pwzCodeSubCode, L"%d,%d", pswe->iErrorCode, pswe->iSubCode); + ExitOnFailure(hr, "failed to create error code,subcode string while installing web error"); + } + + hr = MultiSzFindSubstring(pwzErrors, pwzCodeSubCode, &dwFoundCodeSubCodeIndex, &wzFoundCodeSubCode); + ExitOnFailure(hr, "failed to find existing error code,subcode: %ls", pwzCodeSubCode); + + // If we didn't find this error code/sub code pair in the list already, make sure it's acceptable to add + if (S_FALSE == hr) + { + // + // Make sure this error code/sub code pair is in the "acceptable" list + // + + // If the subcode is 0, that means "0" in MD_CUSTOM_ERROR_DESC (no special formatting logic needed) + hr = StrAllocFormatted(&pwzAcceptableCodeSubCode, L"%d,%d", pswe->iErrorCode, pswe->iSubCode); + ExitOnFailure(hr, "failed to create error code,subcode string while installing web error"); + + // We don't care where it is, just whether it's there or not + hr = MultiSzFindSubstring(pwzAcceptableErrors, pwzAcceptableCodeSubCode, NULL, NULL); + ExitOnFailure(hr, "failed to find whether or not error code, subcode: %ls is supported", pwzCodeSubCode); + + if (S_FALSE == hr) + { + WcaLog(LOGMSG_VERBOSE, "Skipping error code, subcode: %ls because it is not supported by the server.", pwzCodeSubCode); + continue; + } + + // If we didn't find it (and its an acceptable error) then we have nothing to replace + fOldValueFound = FALSE; + } + + // Set up the new error string if needed + if (*(pswe->wzFile)) + { + hr = StrAllocFormatted(&pwzNewError, L"%s,FILE,%s", pwzCodeSubCode, pswe->wzFile); + ExitOnFailure(hr, "failed to create new error code string with code,subcode: %ls, file: %ls", pwzCodeSubCode, pswe->wzFile); + } + else if (*(pswe->wzURL)) + { + hr = StrAllocFormatted(&pwzNewError, L"%s,URL,%s", pwzCodeSubCode, pswe->wzURL); + ExitOnFailure(hr, "failed to create new error code string with code,subcode: %ls, file: %ls", pwzCodeSubCode, pswe->wzFile); + } + else if (fOldValueFound) + { + // If no File or URL was specified, they want a default error so remove the old value from the MULTISZ and move on + hr = MultiSzRemoveString(&pwzErrors, dwFoundCodeSubCodeIndex); + ExitOnFailure(hr, "failed to remove string for error code sub code: %ls in order to make it 'default'", pwzCodeSubCode); + continue; + } + + // If we have something to replace, replace it, otherwise, put it at the beginning (order shouldn't matter) + if (fOldValueFound) + { + hr = MultiSzReplaceString(&pwzErrors, dwFoundCodeSubCodeIndex, pwzNewError); + ExitOnFailure(hr, "failed to replace old error string with new error string for error code,subcode: %ls", pwzCodeSubCode); + } + else + { + hr = MultiSzPrepend(&pwzErrors, NULL, pwzNewError); + ExitOnFailure(hr, "failed to prepend new error string for error code,subcode: %ls", pwzCodeSubCode); + } + } + + // now write the CustomErrors to the metabase + if (weptWeb == iParentType) + { + hr = ScaWriteMetabaseValue(piMetabase, wzRoot, L"/Root", MD_CUSTOM_ERROR, METADATA_INHERIT, IIS_MD_UT_FILE, MULTISZ_METADATA, pwzErrors); + ExitOnFailure(hr, "Failed to write Web Error to /Root"); + } + else + { + hr = ScaWriteMetabaseValue(piMetabase, wzRoot, NULL, MD_CUSTOM_ERROR, METADATA_INHERIT, IIS_MD_UT_FILE, MULTISZ_METADATA, pwzErrors); + ExitOnFailure(hr, "Failed to write Web Error"); + } + +LExit: + ReleaseStr(pwzErrors); + ReleaseStr(pwzSearchKey); + ReleaseStr(pwzCodeSubCode); + ReleaseStr(pwzAcceptableCodeSubCode); + ReleaseStr(pwzAcceptableErrors); + + return hr; +} + +static HRESULT AddWebErrorToList(SCA_WEB_ERROR** ppsweList) +{ + HRESULT hr = S_OK; + + SCA_WEB_ERROR* pswe = static_cast(MemAlloc(sizeof(SCA_WEB_ERROR), TRUE)); + ExitOnNull(pswe, hr, E_OUTOFMEMORY, "failed to allocate memory for new web error list element"); + + pswe->psweNext = *ppsweList; + *ppsweList = pswe; + +LExit: + return hr; +} + +HRESULT ScaWebErrorCheckList(SCA_WEB_ERROR* psweList) +{ + if (!psweList) + { + return S_OK; + } + + while (psweList) + { + WcaLog(LOGMSG_STANDARD, "WebError code: %d subcode: %d for parent: %ls not used!", psweList->iErrorCode, psweList->iSubCode, psweList->wzParentValue); + psweList = psweList->psweNext; + } + + return E_FAIL; +} + diff --git a/src/ext/Iis/ca/scaweberr.h b/src/ext/Iis/ca/scaweberr.h new file mode 100644 index 00000000..ad8ff4f5 --- /dev/null +++ b/src/ext/Iis/ca/scaweberr.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. + + +enum eWebErrorParentType { weptVDir = 1, weptWeb }; + +struct SCA_WEB_ERROR +{ + int iErrorCode; + int iSubCode; + + int iParentType; + WCHAR wzParentValue[MAX_DARWIN_KEY + 1]; + + WCHAR wzFile[MAX_PATH]; + WCHAR wzURL[MAX_PATH]; // TODO: this needs to be bigger than MAX_PATH + + SCA_WEB_ERROR *psweNext; +}; + +// prototypes +HRESULT ScaWebErrorRead( + SCA_WEB_ERROR **ppsweList, + __inout LPWSTR *ppwzCustomActionData + ); +void ScaWebErrorFreeList(SCA_WEB_ERROR *psweList); +HRESULT ScaWebErrorCheckList(SCA_WEB_ERROR* psweList); +HRESULT ScaGetWebError(int iParentType, LPCWSTR wzParentValue, SCA_WEB_ERROR **ppsweList, SCA_WEB_ERROR **ppsweOut); +HRESULT ScaWriteWebError(IMSAdminBase* piMetabase, int iParentType, LPCWSTR wzRoot, SCA_WEB_ERROR* psweList); + diff --git a/src/ext/Iis/ca/scaweberr7.cpp b/src/ext/Iis/ca/scaweberr7.cpp new file mode 100644 index 00000000..33c2f1bd --- /dev/null +++ b/src/ext/Iis/ca/scaweberr7.cpp @@ -0,0 +1,88 @@ +// 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" + +HRESULT ScaWriteWebError7( + __in_z LPCWSTR wzWebName, + __in_z LPCWSTR wzRoot, + SCA_WEB_ERROR* psweList + ) +{ + HRESULT hr = S_OK; + + hr = ScaWriteConfigID(IIS_WEBERROR_BEGIN); + ExitOnFailure(hr, "Fail to write webError begin ID"); + + hr = ScaWriteConfigString(wzWebName); + ExitOnFailure(hr, "Fail to write webError Web Key"); + + hr = ScaWriteConfigString(wzRoot); + ExitOnFailure(hr, "Fail to write webError Vdir key"); + + // Loop through the HTTP headers + for (SCA_WEB_ERROR* pswe = psweList; pswe; pswe = pswe->psweNext) + { + hr = ScaWriteConfigID(IIS_WEBERROR); + ExitOnFailure(hr, "Fail to write webError ID"); + + hr = ScaWriteConfigInteger(pswe->iErrorCode); + ExitOnFailure(hr, "Fail to write webError code"); + + hr = ScaWriteConfigInteger(pswe->iSubCode); + ExitOnFailure(hr, "Fail to write webError subcode"); + + //just write one + if (*(pswe->wzFile)) + { + hr = ScaWriteConfigString(pswe->wzFile); + ExitOnFailure(hr, "Fail to write webError file"); + hr = ScaWriteConfigInteger(0); + ExitOnFailure(hr, "Fail to write webError file code"); + } + else if (*(pswe->wzURL)) + { + hr = ScaWriteConfigString(pswe->wzURL); + ExitOnFailure(hr, "Fail to write webError URL"); + hr = ScaWriteConfigInteger(1); + ExitOnFailure(hr, "Fail to write webError URL code"); + } + } + + hr = ScaWriteConfigID(IIS_WEBERROR_END); + ExitOnFailure(hr, "Fail to write httpHeader end ID"); + +LExit: + return hr; + +} + +//static HRESULT AddWebErrorToList(SCA_WEB_ERROR** ppsweList) +//{ +// HRESULT hr = S_OK; +// +// SCA_WEB_ERROR* pswe = static_cast(MemAlloc(sizeof(SCA_WEB_ERROR), TRUE)); +// ExitOnNull(pswe, hr, E_OUTOFMEMORY, "failed to allocate memory for new web error list element"); +// +// pswe->psweNext = *ppsweList; +// *ppsweList = pswe; +// +//LExit: +// return hr; +//} + +HRESULT ScaWebErrorCheckList7(SCA_WEB_ERROR* psweList) +{ + if (!psweList) + { + return S_OK; + } + + while (psweList) + { + WcaLog(LOGMSG_STANDARD, "WebError code: %d subcode: %d for parent: %ls not used!", psweList->iErrorCode, psweList->iSubCode, psweList->wzParentValue); + psweList = psweList->psweNext; + } + + return E_FAIL; +} + diff --git a/src/ext/Iis/ca/scaweberr7.h b/src/ext/Iis/ca/scaweberr7.h new file mode 100644 index 00000000..62448211 --- /dev/null +++ b/src/ext/Iis/ca/scaweberr7.h @@ -0,0 +1,10 @@ +#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 ScaWriteWebError7( + __in_z LPCWSTR wzWebName, + __in_z LPCWSTR wzRoot, + SCA_WEB_ERROR* psweList + ); + diff --git a/src/ext/Iis/ca/scaweblog.cpp b/src/ext/Iis/ca/scaweblog.cpp new file mode 100644 index 00000000..01147848 --- /dev/null +++ b/src/ext/Iis/ca/scaweblog.cpp @@ -0,0 +1,177 @@ +// 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" + +enum eWebLogQuery { wlqLog = 1, wlqFormat }; + +/* **************************************************************** + * LookupLogFormatGUID -Looks up a given IIS Log format type in + * the metabase and returns the GUID for it. + * ****************************************************************/ +static HRESULT LookupLogFormatGUID( + __in IMSAdminBase* piMetabase, + __in LPCWSTR wzLogFormat, + __out_ecount(cchGUID) LPWSTR wzGUID, + __in int cchGUID + ) +{ + WCHAR wzKey[METADATA_MAX_NAME_LEN]; + HRESULT hr = S_OK; + + METADATA_RECORD mrKeyType; + ::ZeroMemory(&mrKeyType, sizeof(mrKeyType)); + + mrKeyType.dwMDIdentifier = MD_KEY_TYPE; + mrKeyType.dwMDAttributes = METADATA_NO_ATTRIBUTES; + mrKeyType.dwMDUserType = IIS_MD_UT_SERVER; + mrKeyType.dwMDDataType = ALL_METADATA; + mrKeyType.dwMDDataLen = 0; + mrKeyType.pbMDData = NULL; + + METADATA_RECORD mrPluginId; + ::ZeroMemory(&mrPluginId, sizeof(mrPluginId)); + + mrPluginId.dwMDIdentifier = MD_LOG_PLUGIN_MOD_ID; + mrPluginId.dwMDAttributes = METADATA_INHERIT; + mrPluginId.dwMDUserType = IIS_MD_UT_SERVER; + mrPluginId.dwMDDataType = ALL_METADATA; + mrPluginId.dwMDDataLen = 0; + mrPluginId.pbMDData = NULL; + + hr = ::StringCchPrintfW(wzKey, countof(wzKey), L"/LM/Logging/%s", wzLogFormat); + ExitOnFailure(hr, "failed to format logging metabase key name"); + + // verify that we have this log format available in IIS + hr = MetaGetValue(piMetabase, METADATA_MASTER_ROOT_HANDLE, wzKey, &mrKeyType); + ExitOnFailure(hr, "Failed to find specified Log format key in IIS for log format: %ls", wzLogFormat); + ExitOnNull(mrKeyType.pbMDData, hr, E_POINTER, "Request for Log format key in IIS for log format returned success, but value was NULL"); + + if (0 != lstrcmpW(L"IIsLogModule", (LPCWSTR)mrKeyType.pbMDData)) + { + ExitOnFailure(hr = E_UNEXPECTED, "Found invalid log format in IIS: %ls", (LPCWSTR)mrKeyType.pbMDData); + } + + // find the GUID for that log format + hr = MetaGetValue(piMetabase, METADATA_MASTER_ROOT_HANDLE, wzKey, &mrPluginId); + ExitOnFailure(hr, "Failed to retrieve IISLog format GUID. Key: %ls", wzKey); + ExitOnNull(mrPluginId.pbMDData, hr, E_POINTER, "Retrieval of IISLog format GUID returned success, but value was NULL"); + + hr = ::StringCchCopyW(wzGUID, cchGUID, (LPCWSTR)mrPluginId.pbMDData); + ExitOnFailure(hr, "failed to copy metabase value: %ls", (LPCWSTR)mrPluginId.pbMDData); + +LExit: + + if (mrKeyType.pbMDData) + { + MetaFreeValue(&mrKeyType); + } + + if (mrPluginId.pbMDData) + { + MetaFreeValue(&mrPluginId); + } + + return hr; +} + + +/* **************************************************************** + * ScaGetWebLog -Retrieves Log table data for the specified Log key + * + * ****************************************************************/ +HRESULT ScaGetWebLog( + IMSAdminBase* piMetabase, + LPCWSTR wzLog, + __in WCA_WRAPQUERY_HANDLE hWebLogQuery, + SCA_WEB_LOG* pswl + ) +{ + HRESULT hr = S_OK; + LPWSTR pwzData = NULL; + MSIHANDLE hRec; + + if (0 == WcaGetQueryRecords(hWebLogQuery)) + { + WcaLog(LOGMSG_VERBOSE, "Skipping ScaGetWebLog() - no records to process"); + ExitFunction1(hr = S_FALSE); + } + + WcaFetchWrappedReset(hWebLogQuery); + + hr = WcaFetchWrappedRecordWhereString(hWebLogQuery, wlqLog, wzLog, &hRec); + if (E_NOMOREITEMS == hr) + { + ExitOnFailure(hr, "cannot locate IIsWebLog.Log='%ls'", wzLog); + } + HRESULT hrTemp = WcaFetchWrappedRecordWhereString(hWebLogQuery, wlqLog, wzLog, &hRec); + + if (SUCCEEDED(hrTemp)) + { + ExitOnFailure(hr, "error - found multiple matching IIsWebLog rows"); + } + + ::ZeroMemory(pswl, sizeof(SCA_WEB_LOG)); + + // check that log key matches + hr = WcaGetRecordString(hRec, wlqLog, &pwzData); + ExitOnFailure(hr, "failed to get IIsWebLog.Log for Log: %ls", wzLog); + hr = ::StringCchCopyW(pswl->wzLog, countof(pswl->wzLog), pwzData); + ExitOnFailure(hr, "failed to copy log name: %ls", pwzData); + + hr = WcaGetRecordString(hRec, wlqFormat, &pwzData); + ExitOnFailure(hr, "failed to get IIsWebLog.Format for Log:", wzLog); + hr = ::StringCchCopyW(pswl->wzFormat, countof(pswl->wzFormat), pwzData); + ExitOnFailure(hr, "failed to copy log format: %ls", pwzData); + + // if they specified a log format, look up its GUID in the metabase + if (*pswl->wzFormat && 0 != lstrcmpW(pswl->wzFormat, L"none")) + { + hr = LookupLogFormatGUID(piMetabase, pswl->wzFormat, pswl->wzFormatGUID, countof(pswl->wzFormatGUID)); + ExitOnFailure(hr, "Failed to get Log Format GUID for Log: %ls", wzLog); + } + +LExit: + ReleaseStr(pwzData); + + return hr; +} + + +/* **************************************************************** + * ScaWriteWebLog -Writes the IIS log values to the metabase. + * + * ****************************************************************/ +HRESULT ScaWriteWebLog( + IMSAdminBase* piMetabase, + LPCWSTR wzWebBase, + SCA_WEB_LOG *pswl + ) +{ + HRESULT hr = S_OK; + + if (*pswl->wzFormat) + { + if (0 == lstrcmpW(pswl->wzFormat, L"none")) + { + // user wishes for Logging to be turned 'off' + hr = ScaWriteMetabaseValue(piMetabase, wzWebBase, L"", MD_LOG_TYPE, METADATA_INHERIT, IIS_MD_UT_SERVER, DWORD_METADATA, (LPVOID)((DWORD_PTR)0)); + ExitOnFailure(hr, "Failed to write Log Type for Web: %ls", wzWebBase); + } + else + { + Assert(*pswl->wzFormatGUID); + + // write the GUID for the log format for the web to the metabase + hr = ScaWriteMetabaseValue(piMetabase, wzWebBase, L"", MD_LOG_PLUGIN_ORDER, METADATA_INHERIT, IIS_MD_UT_SERVER, STRING_METADATA, pswl->wzFormatGUID); + ExitOnFailure(hr, "Failed to write Log GUID for Web: %ls", wzWebBase); + + hr = ScaWriteMetabaseValue(piMetabase, wzWebBase, L"", MD_LOG_TYPE, METADATA_INHERIT, IIS_MD_UT_SERVER, DWORD_METADATA, (LPVOID)((DWORD_PTR)1)); + ExitOnFailure(hr, "Failed to write Log Type for Web: %ls", wzWebBase); + } + } + +LExit: + return hr; +} + + diff --git a/src/ext/Iis/ca/scaweblog.h b/src/ext/Iis/ca/scaweblog.h new file mode 100644 index 00000000..1a690cce --- /dev/null +++ b/src/ext/Iis/ca/scaweblog.h @@ -0,0 +1,27 @@ +#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 SCA_WEB_LOG +{ + // iis configuation information + WCHAR wzLog[MAX_DARWIN_KEY + 1]; + + // for specifying the log format + WCHAR wzFormat[MAX_DARWIN_KEY + 1]; + WCHAR wzFormatGUID[MAX_DARWIN_KEY + 1]; +}; + + +// prototypes +HRESULT ScaGetWebLog( + IMSAdminBase* piMetabase, + LPCWSTR wzLog, + __in WCA_WRAPQUERY_HANDLE hWebLogQuery, + SCA_WEB_LOG* pswl + ); +HRESULT ScaWriteWebLog( + IMSAdminBase* piMetabase, + LPCWSTR wzRootOfWeb, + SCA_WEB_LOG *pswl + ); diff --git a/src/ext/Iis/ca/scaweblog7.cpp b/src/ext/Iis/ca/scaweblog7.cpp new file mode 100644 index 00000000..c857c46f --- /dev/null +++ b/src/ext/Iis/ca/scaweblog7.cpp @@ -0,0 +1,120 @@ +// 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 vcsWebLogQuery7 = L"SELECT `Log`, `Format` " + L"FROM `IIsWebLog` WHERE `Log`=?"; + +enum eWebLogQuery { wlqLog = 1, wlqFormat }; + +/* **************************************************************** + * ScaGetWebLog7 -Retrieves Log table data for the specified Log key + * + * ****************************************************************/ +HRESULT ScaGetWebLog7( + __in_z LPCWSTR wzLog, + __in WCA_WRAPQUERY_HANDLE hWebLogQuery, + __out SCA_WEB_LOG* pswl + ) +{ + HRESULT hr = S_OK; + LPWSTR pwzData = NULL; + MSIHANDLE hRec; + + if (0 == WcaGetQueryRecords(hWebLogQuery)) + { + WcaLog(LOGMSG_VERBOSE, "Skipping ScaGetWebLog() - no records to process"); + ExitFunction1(hr = S_FALSE); + } + + WcaFetchWrappedReset(hWebLogQuery); + + hr = WcaFetchWrappedRecordWhereString(hWebLogQuery, wlqLog, wzLog, &hRec); + if (E_NOMOREITEMS == hr) + { + ExitOnFailure(hr, "cannot locate IIsWebLog.Log='%ls'", wzLog); + } + HRESULT hrTemp = WcaFetchWrappedRecordWhereString(hWebLogQuery, wlqLog, wzLog, &hRec); + + if (SUCCEEDED(hrTemp)) + { + ExitOnFailure(hr, "error - found multiple matching IIsWebLog rows"); + } + + ::ZeroMemory(pswl, sizeof(SCA_WEB_LOG)); + + // check that log key matches + hr = WcaGetRecordString(hRec, wlqLog, &pwzData); + ExitOnFailure(hr, "failed to get IIsWebLog.Log for Log: %ls", wzLog); + hr = ::StringCchCopyW(pswl->wzLog, countof(pswl->wzLog), pwzData); + ExitOnFailure(hr, "failed to copy log name: %ls", pwzData); + + hr = WcaGetRecordString(hRec, wlqFormat, &pwzData); + ExitOnFailure(hr, "failed to get IIsWebLog.Format for Log:", wzLog); + + //translate WIX log format name strings to IIS7 + if (0 == lstrcmpW(pwzData, L"Microsoft IIS Log File Format")) + { + hr = ::StringCchCopyW(pswl->wzFormat, countof(pswl->wzFormat), L"IIS"); + ExitOnFailure(hr, "failed to copy log format: %ls", pwzData); + } + else if (0 == lstrcmpW(pwzData, L"NCSA Common Log File Format")) + { + hr = ::StringCchCopyW(pswl->wzFormat, countof(pswl->wzFormat), L"NCSA"); + ExitOnFailure(hr, "failed to copy log format: %ls", pwzData); + } + else if (0 == lstrcmpW(pwzData, L"none")) + { + hr = ::StringCchCopyW(pswl->wzFormat, countof(pswl->wzFormat), L"none"); + ExitOnFailure(hr, "failed to copy log format: %ls", pwzData); + } + else if (0 == lstrcmpW(pwzData, L"ODBC Logging")) + { + hr = ::StringCchCopyW(pswl->wzFormat, countof(pswl->wzFormat), L"W3C"); + ExitOnFailure(hr, "failed to copy log format: %ls", pwzData); + } + else if (0 == lstrcmpW(pwzData, L"W3C Extended Log File Format")) + { + hr = ::StringCchCopyW(pswl->wzFormat, countof(pswl->wzFormat), L"W3C"); + ExitOnFailure(hr, "failed to copy log format: %ls", pwzData); + } + else + { + hr = HRESULT_FROM_WIN32(ERROR_INVALID_INDEX); + ExitOnFailure(hr, "Invalid log file format: %ls", pwzData); + } + +LExit: + ReleaseStr(pwzData); + + return hr; +} + + +/* **************************************************************** + * ScaWriteWebLog -Writes the IIS log values to the metabase. + * + * ****************************************************************/ +HRESULT ScaWriteWebLog7( + LPCWSTR wzWebBase, + const SCA_WEB_LOG *pswl + ) +{ + HRESULT hr = S_OK; + + if (*pswl->wzFormat) + { + //write pswl->wzFormat + hr = ScaWriteConfigID(IIS_WEBLOG); + ExitOnFailure(hr, "Failed to write log format id"); + hr = ScaWriteConfigString(wzWebBase); + ExitOnFailure(hr, "Failed to write log web key"); + hr = ScaWriteConfigString(pswl->wzFormat); + ExitOnFailure(hr, "Failed to write log format string"); + } + +LExit: + return hr; +} + + diff --git a/src/ext/Iis/ca/scaweblog7.h b/src/ext/Iis/ca/scaweblog7.h new file mode 100644 index 00000000..f2bb60d7 --- /dev/null +++ b/src/ext/Iis/ca/scaweblog7.h @@ -0,0 +1,14 @@ +#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 ScaGetWebLog7( + __in_z LPCWSTR wzLog, + __in WCA_WRAPQUERY_HANDLE hWebLogQuery, + __out SCA_WEB_LOG* pswl + ); + +HRESULT ScaWriteWebLog7( + __in_z LPCWSTR wzRootOfWeb, + const SCA_WEB_LOG *pswl + ); diff --git a/src/ext/Iis/ca/scawebprop.cpp b/src/ext/Iis/ca/scawebprop.cpp new file mode 100644 index 00000000..b5e38467 --- /dev/null +++ b/src/ext/Iis/ca/scawebprop.cpp @@ -0,0 +1,301 @@ +// 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 +enum eWebDirPropertiesQuery { wpqProperties = 1, wpqAccess, wpqAuthorization, wpqUser, wpqControlledPassword, wpqLogVisits, wpqIndex, wpqDefaultDoc, wpqAspDetailedError, wpqHttpExp, wpqCCMaxAge, wpqCCCustom, wpqNoCustomError, wpqAccessSSLFlags, wpqAuthenticationProviders }; + +HRESULT ScaGetWebDirProperties( + __in LPCWSTR wzProperties, + __in WCA_WRAPQUERY_HANDLE hUserQuery, + __in WCA_WRAPQUERY_HANDLE hWebDirPropQuery, + __inout SCA_WEB_PROPERTIES* pswp + ) +{ + Assert(*wzProperties && pswp); + + HRESULT hr = S_OK; + MSIHANDLE hRec; + LPWSTR pwzData = NULL; + + ExitOnNull(wzProperties, hr, E_INVALIDARG, "Failed to get web directory properties because no properties were provided to get"); + + WcaFetchWrappedReset(hWebDirPropQuery); + + hr = WcaFetchWrappedRecordWhereString(hWebDirPropQuery, 1, wzProperties, &hRec); + if (S_OK == hr) + { + hr = WcaGetRecordString(hRec, wpqProperties, &pwzData); + ExitOnFailure(hr, "Failed to get IIsWebDirProperties.DirProperties"); + hr = ::StringCchCopyW(pswp->wzKey, countof(pswp->wzKey), pwzData); + ExitOnFailure(hr, "Failed to copy key string to webdirproperties object"); + + Assert(0 == lstrcmpW(pswp->wzKey, wzProperties)); + + hr = WcaGetRecordInteger(hRec, wpqAccess, &pswp->iAccess); + ExitOnFailure(hr, "Failed to get access value"); + + hr = WcaGetRecordInteger(hRec, wpqAuthorization, &pswp->iAuthorization); + ExitOnFailure(hr, "Failed to get authorization value"); + + // if allow anonymous users + if (S_OK == hr && pswp->iAuthorization & 1) + { + // if there is an anonymous user specified + hr = WcaGetRecordString(hRec, wpqUser, &pwzData); + ExitOnFailure(hr, "Failed to get AnonymousUser_"); + if (pwzData && *pwzData) + { + hr = WcaGetRecordInteger(hRec, wpqControlledPassword, &pswp->fIIsControlledPassword); + ExitOnFailure(hr, "Failed to get IIsControlledPassword"); + if (S_FALSE == hr) + { + pswp->fIIsControlledPassword = FALSE; + hr = S_OK; + } + + hr = ScaGetUserDeferred(pwzData, hUserQuery, &pswp->scau); + ExitOnFailure(hr, "Failed to get User information for Web"); + + pswp->fHasUser = TRUE; + } + else + pswp->fHasUser = FALSE; + } + + hr = WcaGetRecordInteger(hRec, wpqLogVisits, &pswp->fLogVisits); + ExitOnFailure(hr, "Failed to get IIsWebDirProperties.LogVisits"); + + hr = WcaGetRecordInteger(hRec, wpqIndex, &pswp->fIndex); + ExitOnFailure(hr, "Failed to get IIsWebDirProperties.Index"); + + hr = WcaGetRecordString(hRec, wpqDefaultDoc, &pwzData); + ExitOnFailure(hr, "Failed to get IIsWebDirProperties.DefaultDoc"); + if (pwzData && *pwzData) + { + pswp->fHasDefaultDoc = TRUE; + if (0 == lstrcmpW(L"-", pwzData)) // remove any existing default documents by setting them blank + { + pswp->wzDefaultDoc[0] = L'\0'; + } + else // set the default documents + { + hr = ::StringCchCopyW(pswp->wzDefaultDoc, countof(pswp->wzDefaultDoc), pwzData); + ExitOnFailure(hr, "Failed to copy default document string to webdirproperties object"); + } + } + else + { + pswp->fHasDefaultDoc = FALSE; + } + + hr = WcaGetRecordInteger(hRec, wpqAspDetailedError, &pswp->fAspDetailedError); + ExitOnFailure(hr, "Failed to get IIsWebDirProperties.AspDetailedError"); + + hr = WcaGetRecordString(hRec, wpqHttpExp, &pwzData); + ExitOnFailure(hr, "Failed to get IIsWebDirProperties.HttpExp"); + if (pwzData && *pwzData) + { + pswp->fHasHttpExp = TRUE; + if (0 == lstrcmpW(L"-", pwzData)) // remove any existing default expiration settings by setting them blank + { + pswp->wzHttpExp[0] = L'\0'; + } + else // set the expiration setting + { + hr = ::StringCchCopyW(pswp->wzHttpExp, countof(pswp->wzHttpExp), pwzData); + ExitOnFailure(hr, "Failed to copy http expiration string to webdirproperties object"); + } + } + else + { + pswp->fHasHttpExp = FALSE; + } + + hr = WcaGetRecordInteger(hRec, wpqCCMaxAge, &pswp->iCacheControlMaxAge); + ExitOnFailure(hr, "failed to get IIsWebDirProperties.CacheControlMaxAge"); + + hr = WcaGetRecordString(hRec, wpqCCCustom, &pwzData); + ExitOnFailure(hr, "Failed to get IIsWebDirProperties.CacheControlCustom"); + if (pwzData && *pwzData) + { + pswp->fHasCacheControlCustom = TRUE; + if (0 == lstrcmpW(L"-", pwzData)) // remove any existing default cache control custom settings by setting them blank + { + pswp->wzCacheControlCustom[0] = L'\0'; + } + else // set the custom cache control setting + { + hr = ::StringCchCopyW(pswp->wzCacheControlCustom, countof(pswp->wzCacheControlCustom), pwzData); + ExitOnFailure(hr, "Failed to copy cache control custom settings to webdirproperites object"); + } + } + else + { + pswp->fHasCacheControlCustom = FALSE; + } + + hr = WcaGetRecordInteger(hRec, wpqNoCustomError, &pswp->fNoCustomError); + ExitOnFailure(hr, "failed to get IIsWebDirProperties.NoCustomError"); + if (MSI_NULL_INTEGER == pswp->fNoCustomError) + pswp->fNoCustomError = FALSE; + + hr = WcaGetRecordInteger(hRec, wpqAccessSSLFlags, &pswp->iAccessSSLFlags); + ExitOnFailure(hr, "failed to get IIsWebDirProperties.AccessSSLFlags"); + + hr = WcaGetRecordString(hRec, wpqAuthenticationProviders, &pwzData); + ExitOnFailure(hr, "Failed to get IIsWebDirProperties.AuthenticationProviders"); + if (pwzData && *pwzData) + { + hr = ::StringCchCopyW(pswp->wzAuthenticationProviders, countof(pswp->wzAuthenticationProviders), pwzData); + ExitOnFailure(hr, "Failed to copy authentication providers string to webdirproperties object"); + } + else + { + pswp->wzAuthenticationProviders[0] = L'\0'; + } + } + else if (E_NOMOREITEMS == hr) + { + WcaLog(LOGMSG_STANDARD, "Error: Cannot locate IIsWebDirProperties.DirProperties='%ls'", wzProperties); + hr = E_FAIL; + } + else + { + ExitOnFailure(hr, "Error getting appropriate webdirproperty"); + } + + // Let's check that there isn't more than one record found - if there is, throw an assert like WcaFetchSingleRecord() would + HRESULT hrTemp = WcaFetchWrappedRecordWhereString(hWebDirPropQuery, 1, wzProperties, &hRec); + if (SUCCEEDED(hrTemp)) + { + AssertSz(E_NOMOREITEMS == hrTemp, "ScaGetWebDirProperties found more than one record"); + } + +LExit: + ReleaseStr(pwzData); + + return hr; +} + + +HRESULT ScaWriteWebDirProperties( + __in IMSAdminBase* piMetabase, + __in LPCWSTR wzRootOfWeb, + __inout SCA_WEB_PROPERTIES* pswp + ) +{ + HRESULT hr = S_OK; + DWORD dw = 0; + WCHAR wz[METADATA_MAX_NAME_LEN + 1]; + + // write the access permissions to the metabase + if (MSI_NULL_INTEGER != pswp->iAccess) + { + hr = ScaWriteMetabaseValue(piMetabase, wzRootOfWeb, NULL, MD_ACCESS_PERM, METADATA_INHERIT, IIS_MD_UT_FILE, DWORD_METADATA, (LPVOID)((DWORD_PTR)pswp->iAccess)); + ExitOnFailure(hr, "Failed to write access permissions for Web"); + } + + if (MSI_NULL_INTEGER != pswp->iAuthorization) + { + hr = ScaWriteMetabaseValue(piMetabase, wzRootOfWeb, NULL, MD_AUTHORIZATION, METADATA_INHERIT, IIS_MD_UT_FILE, DWORD_METADATA, (LPVOID)((DWORD_PTR)pswp->iAuthorization)); + ExitOnFailure(hr, "Failed to write authorization for Web"); + } + + if (pswp->fHasUser) + { + Assert(pswp->scau.wzName); + // write the user name + if (*pswp->scau.wzDomain) + { + hr = ::StringCchPrintfW(wz, countof(wz), L"%s\\%s", pswp->scau.wzDomain, pswp->scau.wzName); + ExitOnFailure(hr, "Failed to format domain\\username string"); + } + else + { + hr = ::StringCchCopyW(wz, countof(wz), pswp->scau.wzName); + ExitOnFailure(hr, "Failed to copy user name"); + } + hr = ScaWriteMetabaseValue(piMetabase, wzRootOfWeb, NULL, MD_ANONYMOUS_USER_NAME, METADATA_INHERIT, IIS_MD_UT_FILE, STRING_METADATA, (LPVOID)wz); + ExitOnFailure(hr, "Failed to write anonymous user name for Web"); + + // write the password + hr = ScaWriteMetabaseValue(piMetabase, wzRootOfWeb, NULL, MD_ANONYMOUS_PWD, METADATA_INHERIT | METADATA_SECURE, IIS_MD_UT_FILE, STRING_METADATA, (LPVOID)pswp->scau.wzPassword); + ExitOnFailure(hr, "Failed to write anonymous user password for Web"); + + // store whether IIs controls password + dw = (pswp->fIIsControlledPassword) ? TRUE : FALSE; + hr = ScaWriteMetabaseValue(piMetabase, wzRootOfWeb, NULL, MD_ANONYMOUS_USE_SUBAUTH, METADATA_INHERIT, IIS_MD_UT_FILE, DWORD_METADATA, (LPVOID)((DWORD_PTR)dw)); + ExitOnFailure(hr, "Failed to write if IIs controls user password for Web"); + } + + if (MSI_NULL_INTEGER != pswp->fLogVisits) + { + // The sense of this boolean value is reversed - it is "don't log", not "log visits." + dw = (pswp->fLogVisits) ? FALSE : TRUE; + hr = ScaWriteMetabaseValue(piMetabase, wzRootOfWeb, NULL, MD_DONT_LOG, METADATA_INHERIT, IIS_MD_UT_FILE, DWORD_METADATA, (LPVOID)((DWORD_PTR)dw)); + ExitOnFailure(hr, "Failed to write authorization for Web"); + } + + if (MSI_NULL_INTEGER != pswp->fIndex) + { + dw = (pswp->fIndex) ? TRUE : FALSE; + hr = ScaWriteMetabaseValue(piMetabase, wzRootOfWeb, NULL, MD_IS_CONTENT_INDEXED, METADATA_INHERIT, IIS_MD_UT_FILE, DWORD_METADATA, (LPVOID)((DWORD_PTR)dw)); + ExitOnFailure(hr, "Failed to write authorization for Web"); + } + + if (pswp->fHasDefaultDoc) + { + hr = ScaWriteMetabaseValue(piMetabase, wzRootOfWeb, NULL, MD_DEFAULT_LOAD_FILE, METADATA_INHERIT, IIS_MD_UT_FILE, STRING_METADATA, (LPVOID)pswp->wzDefaultDoc); + ExitOnFailure(hr, "Failed to write default documents for Web"); + } + + if (MSI_NULL_INTEGER != pswp->fAspDetailedError) + { + dw = (pswp->fAspDetailedError) ? TRUE : FALSE; + hr = ScaWriteMetabaseValue(piMetabase, wzRootOfWeb, NULL, MD_ASP_SCRIPTERRORSSENTTOBROWSER, METADATA_INHERIT, ASP_MD_UT_APP, DWORD_METADATA, (LPVOID)((DWORD_PTR)dw)); + ExitOnFailure(hr, "Failed to write ASP script error for Web"); + } + + if (pswp->fHasHttpExp) + { + hr = ScaWriteMetabaseValue(piMetabase, wzRootOfWeb, NULL, MD_HTTP_EXPIRES, METADATA_INHERIT, IIS_MD_UT_FILE, STRING_METADATA, (LPVOID)pswp->wzHttpExp); + ExitOnFailure(hr, "Failed to write HTTP Expiration for Web"); + } + + if (MSI_NULL_INTEGER != pswp->iCacheControlMaxAge) + { + hr = ScaWriteMetabaseValue(piMetabase, wzRootOfWeb, NULL, MD_CC_MAX_AGE, METADATA_INHERIT, IIS_MD_UT_FILE, DWORD_METADATA, (LPVOID)((DWORD_PTR)pswp->iCacheControlMaxAge)); + ExitOnFailure(hr, "Failed to write Cache Control Max Age for Web"); + } + + if (pswp->fHasCacheControlCustom) + { + hr = ScaWriteMetabaseValue(piMetabase, wzRootOfWeb, NULL, MD_CC_OTHER, METADATA_INHERIT, IIS_MD_UT_FILE, STRING_METADATA, (LPVOID)pswp->wzCacheControlCustom); + ExitOnFailure(hr, "Failed to write Cache Control Custom for Web"); + } + + if (pswp->fNoCustomError) + { + memset(wz, 0, sizeof(wz)); + hr = ScaWriteMetabaseValue(piMetabase, wzRootOfWeb, NULL, MD_CUSTOM_ERROR, METADATA_INHERIT, IIS_MD_UT_FILE, MULTISZ_METADATA, wz); + ExitOnFailure(hr, "Failed to write Custom Error for Web"); + } + + if (MSI_NULL_INTEGER != pswp->iAccessSSLFlags) + { + hr = ScaWriteMetabaseValue(piMetabase, wzRootOfWeb, NULL, MD_SSL_ACCESS_PERM, METADATA_INHERIT, IIS_MD_UT_FILE, DWORD_METADATA, (LPVOID)((DWORD_PTR)pswp->iAccessSSLFlags)); + ExitOnFailure(hr, "Failed to write AccessSSLFlags for Web"); + } + + if (*pswp->wzAuthenticationProviders) + { + hr = ::StringCchCopyW(wz, countof(wz), pswp->wzAuthenticationProviders); + ExitOnFailure(hr, "Failed to copy authentication providers string"); + hr = ScaWriteMetabaseValue(piMetabase, wzRootOfWeb, NULL, MD_NTAUTHENTICATION_PROVIDERS, METADATA_INHERIT, IIS_MD_UT_FILE, STRING_METADATA, (LPVOID)wz); + ExitOnFailure(hr, "Failed to write AuthenticationProviders for Web"); + } + +LExit: + return hr; +} diff --git a/src/ext/Iis/ca/scawebprop.h b/src/ext/Iis/ca/scawebprop.h new file mode 100644 index 00000000..7a3ae5c9 --- /dev/null +++ b/src/ext/Iis/ca/scawebprop.h @@ -0,0 +1,60 @@ +#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. + + +#include "scauser.h" + +// global sql queries provided for optimization +extern LPCWSTR vcsWebDirPropertiesQuery; + + +// structs +struct SCA_WEB_PROPERTIES +{ + WCHAR wzKey[MAX_DARWIN_KEY + 1]; + + int iAccess; + + int iAuthorization; + BOOL fHasUser; + SCA_USER scau; + BOOL fIIsControlledPassword; + + BOOL fLogVisits; + BOOL fIndex; + + BOOL fHasDefaultDoc; + WCHAR wzDefaultDoc[MAX_DARWIN_COLUMN + 1]; + + BOOL fHasHttpExp; + WCHAR wzHttpExp[MAX_DARWIN_COLUMN + 1]; + + BOOL fAspDetailedError; + + int iCacheControlMaxAge; + + BOOL fHasCacheControlCustom; + WCHAR wzCacheControlCustom[MAX_DARWIN_COLUMN + 1]; + + BOOL fNoCustomError; + + int iAccessSSLFlags; + + WCHAR wzAuthenticationProviders[MAX_DARWIN_COLUMN + 1]; +}; + + +// prototypes +HRESULT ScaGetWebDirProperties( + __in LPCWSTR pwzProperties, + __in WCA_WRAPQUERY_HANDLE hUserQuery, + __in WCA_WRAPQUERY_HANDLE hWebDirPropQuery, + __inout SCA_WEB_PROPERTIES* pswp + ); + +HRESULT ScaWriteWebDirProperties( + __in IMSAdminBase* piMetabase, + __in LPCWSTR wzRootOfWeb, + __inout SCA_WEB_PROPERTIES* pswp + ); + diff --git a/src/ext/Iis/ca/scawebprop7.cpp b/src/ext/Iis/ca/scawebprop7.cpp new file mode 100644 index 00000000..b8d99b0a --- /dev/null +++ b/src/ext/Iis/ca/scawebprop7.cpp @@ -0,0 +1,155 @@ +// 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" + +HRESULT ScaWriteWebDirProperties7( + __in_z LPCWSTR wzWebName, + __in_z LPCWSTR wzRootOfWeb, + __in const SCA_WEB_PROPERTIES* pswp + ) +{ + HRESULT hr = S_OK; + WCHAR wz[METADATA_MAX_NAME_LEN + 1]; + + //all go to same web/root location tag + hr = ScaWriteConfigID(IIS_DIRPROP_BEGIN); + ExitOnFailure(hr, "Failed to write DirProp begin id"); + hr = ScaWriteConfigString(wzWebName); //site name key + ExitOnFailure(hr, "Failed to write DirProp web key"); + hr = ScaWriteConfigString(wzRootOfWeb); //app path key + ExitOnFailure(hr, "Failed to write DirProp app key"); + + // write the access permissions to the metabase + if (MSI_NULL_INTEGER != pswp->iAccess) + { + hr = ScaWriteConfigID(IIS_DIRPROP_ACCESS); + ExitOnFailure(hr, "Failed to write DirProp access id"); + hr = ScaWriteConfigInteger(pswp->iAccess); + ExitOnFailure(hr, "Failed to write access permissions for Web"); + } + + if (MSI_NULL_INTEGER != pswp->iAuthorization) + { + hr = ScaWriteConfigID(IIS_DIRPROP_AUTH); + ExitOnFailure(hr, "Failed to write DirProp auth id"); + hr = ScaWriteConfigInteger(pswp->iAuthorization); + ExitOnFailure(hr, "Failed to write authorization for Web"); + } + + if (pswp->fHasUser) + { + Assert(pswp->scau.wzName); + // write the user name + if (*pswp->scau.wzDomain) + { + hr = ::StringCchPrintfW(wz, countof(wz), L"%s\\%s", pswp->scau.wzDomain, pswp->scau.wzName); + ExitOnFailure(hr, "Failed to format domain\\username string"); + } + else + { +#pragma prefast(suppress:26037, "Source string is null terminated - it is populated as target of ::StringCchCopyW") + hr = ::StringCchCopyW(wz, countof(wz), pswp->scau.wzName); + ExitOnFailure(hr, "Failed to copy user name"); + } + hr = ScaWriteConfigID(IIS_DIRPROP_USER); + ExitOnFailure(hr, "Failed to write DirProp user id"); + hr = ScaWriteConfigString(wz); + ExitOnFailure(hr, "Failed to write anonymous user name for Web"); + + // write the password + hr = ScaWriteConfigID(IIS_DIRPROP_PWD); + ExitOnFailure(hr, "Failed to write DirProp pwd id"); + hr = ScaWriteConfigString(pswp->scau.wzPassword); + ExitOnFailure(hr, "Failed to write anonymous user password for Web"); + + if (pswp->fIIsControlledPassword) + { + //Not Supported by IIS7 : pswp->fIIsControlledPassword + WcaLog(LOGMSG_VERBOSE, "Not supported by IIS7: WebDirProperties.IIsControlledPassword, ignoring"); + } + } + + if (MSI_NULL_INTEGER != pswp->fLogVisits) + { + hr = ScaWriteConfigID(IIS_DIRPROP_LOGVISITS); + ExitOnFailure(hr, "Failed to write DirProp logVisits id"); + hr = ScaWriteConfigInteger(pswp->fLogVisits ? FALSE : TRUE); // we capture "should log" but IIS7 wants "should not log" + ExitOnFailure(hr, "Failed to write DirProp logVisits"); + } + + if (MSI_NULL_INTEGER != pswp->fIndex) + { + //Not Supported by IIS7 : pswp->fIndex + WcaLog(LOGMSG_VERBOSE, "Not supported by IIS7: WebDirProperties.Index, ignoring"); + } + + if (pswp->fHasDefaultDoc) + { + hr = ScaWriteConfigID(IIS_DIRPROP_DEFDOCS); + ExitOnFailure(hr, "Failed to write DirProp defdocs id"); + hr = ScaWriteConfigString(pswp->wzDefaultDoc); + ExitOnFailure(hr, "Failed to write default documents for Web"); + } + + if (MSI_NULL_INTEGER != pswp->fAspDetailedError) + { + hr = ScaWriteConfigID(IIS_DIRPROP_ASPERROR); + ExitOnFailure(hr, "Failed to write ASP script error id"); + hr = ScaWriteConfigInteger(pswp->fAspDetailedError); + ExitOnFailure(hr, "Failed to write ASP script error for Web"); + } + + if (pswp->fHasHttpExp) + { + hr = ScaWriteConfigID(IIS_DIRPROP_HTTPEXPIRES); + ExitOnFailure(hr, "Failed to write DirProp HttpExpires id"); + hr = ScaWriteConfigString(pswp->wzHttpExp); + ExitOnFailure(hr, "Failed to write DirProp HttpExpires value"); + } + + if (MSI_NULL_INTEGER != pswp->iCacheControlMaxAge) + { + hr = ScaWriteConfigID(IIS_DIRPROP_MAXAGE); + ExitOnFailure(hr, "Failed to write DirProp MaxAge id"); + hr = ScaWriteConfigInteger(pswp->iCacheControlMaxAge); + ExitOnFailure(hr, "Failed to write DirProp MaxAge value"); + } + + if (pswp->fHasCacheControlCustom) + { + hr = ScaWriteConfigID(IIS_DIRPROP_CACHECUST); + ExitOnFailure(hr, "Failed to write DirProp Cache Control Custom id"); + hr = ScaWriteConfigString(pswp->wzCacheControlCustom); + ExitOnFailure(hr, "Failed to write Cache Control Custom for Web"); + } + + if (pswp->fNoCustomError) + { + hr = ScaWriteConfigID(IIS_DIRPROP_NOCUSTERROR); + ExitOnFailure(hr, "Failed to write DirProp clear Cust Errors id"); + } + + if (MSI_NULL_INTEGER != pswp->iAccessSSLFlags) + { + hr = ScaWriteConfigID(IIS_DIRPROP_SSLFLAGS); + ExitOnFailure(hr, "Failed to write DirProp sslFlags id"); + hr = ScaWriteConfigInteger(pswp->iAccessSSLFlags); + ExitOnFailure(hr, "Failed to write AccessSSLFlags for Web"); + } + + if (*pswp->wzAuthenticationProviders) + { + hr = ::StringCchCopyW(wz, countof(wz), pswp->wzAuthenticationProviders); + ExitOnFailure(hr, "Failed to copy authentication providers string"); + hr = ScaWriteConfigID(IIS_DIRPROP_AUTHPROVID); + ExitOnFailure(hr, "Failed to write DirProp AuthProvid id"); + hr = ScaWriteConfigString(wz); + ExitOnFailure(hr, "Failed to write AuthenticationProviders for Web"); + } + //End of Dir Properties + hr = ScaWriteConfigID(IIS_DIRPROP_END); + ExitOnFailure(hr, "Failed to write DirProp end id"); + +LExit: + return hr; +} diff --git a/src/ext/Iis/ca/scawebprop7.h b/src/ext/Iis/ca/scawebprop7.h new file mode 100644 index 00000000..a97e8679 --- /dev/null +++ b/src/ext/Iis/ca/scawebprop7.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. + + +#include "scauser.h" + +HRESULT ScaWriteWebDirProperties7( + __in_z LPCWSTR wzwWebName, + __in_z LPCWSTR wzRootOfWeb, + const SCA_WEB_PROPERTIES* pswp + ); + diff --git a/src/ext/Iis/ca/scawebsvcext.cpp b/src/ext/Iis/ca/scawebsvcext.cpp new file mode 100644 index 00000000..369e951b --- /dev/null +++ b/src/ext/Iis/ca/scawebsvcext.cpp @@ -0,0 +1,343 @@ +// 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 +enum eWebSvcExtQuery { ldqComponent=1 , ldqFile, ldqDescription, ldqGroup, ldqAttributes, ldqInstalled, ldqAction }; + + +LPCWSTR vcsWebSvcExtRoot = L"/LM/W3SVC"; + +// prototypes for private helper functions +static HRESULT AddWebSvcExtToList( + __in SCA_WEBSVCEXT** ppsWseList + ); + +//static HRESULT ScaCheckWebSvcExtValue( +// __in IMSAdminBase* piMetabase, +// __in DWORD dwMDIdentifier +// ); + +static HRESULT ScaWebSvcExtInstall( + __in LPWSTR *pwzWebSvcExtList, + __in DWORD_PTR *pcchWebSvcExtList, + __in SCA_WEBSVCEXT* psWseList + ); + +static HRESULT ScaWebSvcExtUninstall( + __in LPWSTR *pwzWebSvcExtList, + __in const DWORD *pcchWebSvcExtList, + __in SCA_WEBSVCEXT* psWseList + ); + +// functions + +HRESULT __stdcall ScaWebSvcExtRead( + __in SCA_WEBSVCEXT** ppsWseList, + __inout LPWSTR *ppwzCustomActionData + ) +{ + Assert(ppsWseList); + + HRESULT hr = S_OK; + MSIHANDLE hRec; + LPWSTR pwzData = NULL; + INSTALLSTATE isInstalled = INSTALLSTATE_UNKNOWN; + INSTALLSTATE isAction = INSTALLSTATE_UNKNOWN; + SCA_WEBSVCEXT* psWebSvcExt = NULL; + WCA_WRAPQUERY_HANDLE hWrapQuery = NULL; + + hr = WcaBeginUnwrapQuery(&hWrapQuery, ppwzCustomActionData); + ExitOnFailure(hr, "Failed to unwrap query for ScaWebSvcExtRead"); + + if (0 == WcaGetQueryRecords(hWrapQuery)) + { + WcaLog(LOGMSG_VERBOSE, "Skipping ScaWebSvcExtRead() because IIsWebServiceExtension data not present"); + ExitFunction1(hr = S_FALSE); + } + + // loop through all the web service extensions + while (S_OK == (hr = WcaFetchWrappedRecord(hWrapQuery, &hRec))) + { + // Get the Component first. If the Component is not being modified during + // this transaction, skip processing this whole record. + hr = WcaGetRecordString(hRec, ldqComponent, &pwzData); + ExitOnFailure(hr, "Failed to get Component for WebSvcExt"); + + hr = WcaGetRecordInteger(hRec, ldqInstalled, (int *)&isInstalled); + ExitOnFailure(hr, "Failed to get Component installed state for WebSvcExt"); + + hr = WcaGetRecordInteger(hRec, ldqAction, (int *)&isAction); + ExitOnFailure(hr, "Failed to get Component action state for WebSvcExt"); + + if (!WcaIsInstalling(isInstalled, isAction) && + !WcaIsReInstalling(isInstalled, isAction) && + !WcaIsUninstalling(isInstalled, isAction)) + { + continue; // skip this record. + } + + hr = AddWebSvcExtToList(ppsWseList); + ExitOnFailure(hr, "failed to add element to web svc ext list"); + + psWebSvcExt = *ppsWseList; + Assert(psWebSvcExt); + + psWebSvcExt->isInstalled = isInstalled; + psWebSvcExt->isAction = isAction; + + hr = WcaGetRecordString(hRec, ldqFile, &pwzData); + ExitOnFailure(hr, "Failed to get File for WebSvcExt"); + hr = ::StringCchCopyW(psWebSvcExt->wzFile, countof(psWebSvcExt->wzFile), pwzData); + ExitOnFailure(hr, "Failed to copy File for WebSvcExt"); + + hr = WcaGetRecordString(hRec, ldqDescription, &pwzData); + ExitOnFailure(hr, "Failed to get Description for WebSvcExt"); + hr = ::StringCchCopyW(psWebSvcExt->wzDescription, countof(psWebSvcExt->wzDescription), pwzData); + ExitOnFailure(hr, "Failed to copy Description for WebSvcExt"); + + hr = WcaGetRecordString(hRec, ldqGroup, &pwzData); + ExitOnFailure(hr, "Failed to get Group for WebSvcExt"); + hr = ::StringCchCopyW(psWebSvcExt->wzGroup, countof(psWebSvcExt->wzGroup), pwzData); + ExitOnFailure(hr, "Failed to copy Group for WebSvcExt"); + + hr = WcaGetRecordInteger(hRec, ldqAttributes, &psWebSvcExt->iAttributes); + ExitOnFailure(hr, "Failed to get Attributes for WebSvcExt"); + } + + if (E_NOMOREITEMS == hr) + hr = S_OK; + ExitOnFailure(hr, "Failure while processing WebSvcExt"); + +LExit: + WcaFinishUnwrapQuery(hWrapQuery); + + ReleaseStr(pwzData); + + return hr; +} + + +// Commit does both install and uninstall +HRESULT __stdcall ScaWebSvcExtCommit( + __in IMSAdminBase* piMetabase, + __in SCA_WEBSVCEXT* psWseList + ) +{ + Assert(piMetabase); + + HRESULT hr = S_OK; + METADATA_RECORD mr; + + LPWSTR wzWebSvcExtList = NULL; + DWORD cbWebSvcExtList = 0; + DWORD_PTR cchWebSvcExtList = 0; + + if (!psWseList) + { + WcaLog(LOGMSG_VERBOSE, "Skipping ScaWebSvcExtCommit() because there are no web service extensions in the list"); + ExitFunction(); + } + + // Get current set of web service extensions. + ::ZeroMemory(&mr, sizeof(mr)); + mr.dwMDIdentifier = MD_WEB_SVC_EXT_RESTRICTION_LIST; + mr.dwMDAttributes = 0; + mr.dwMDUserType = IIS_MD_UT_SERVER; + mr.dwMDDataType = ALL_METADATA; + mr.pbMDData = NULL; + mr.dwMDDataLen = 0; + + hr = piMetabase->GetData(METADATA_MASTER_ROOT_HANDLE, vcsWebSvcExtRoot, &mr, &cbWebSvcExtList); + if (MD_ERROR_DATA_NOT_FOUND == hr) + { + WcaLog(LOGMSG_VERBOSE, "Skipping ScaWebSvcExtCommit() because WebSvcExtRestrictionList value is not present"); + ExitFunction1(hr = S_FALSE); + } + else if (HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER) == hr) + { + // cchWebSvcExtList is returned in bytes. Convert to WCHAR size to call StrAlloc + cchWebSvcExtList = cbWebSvcExtList / sizeof(WCHAR); + hr = StrAlloc(&wzWebSvcExtList, cchWebSvcExtList); + ExitOnFailure(hr, "Failed allocating space for web service extensions"); + } + else + { + ExitOnFailure(hr, "Failed retrieving web service extensions"); + } + + mr.pbMDData = (unsigned char*)wzWebSvcExtList; + mr.dwMDDataLen = cbWebSvcExtList; + + hr = piMetabase->GetData(METADATA_MASTER_ROOT_HANDLE, vcsWebSvcExtRoot, &mr, &cbWebSvcExtList); + ExitOnFailure(hr, "Failed retrieving web service extensions"); + + // Make changes to local copy of metabase + while (psWseList) + { + if (WcaIsInstalling(psWseList->isInstalled, psWseList->isAction)) + { + hr = ScaWebSvcExtInstall(&wzWebSvcExtList, &cchWebSvcExtList, psWseList); + ExitOnFailure(hr, "Failed to install Web Service extension"); + } + else if (WcaIsUninstalling(psWseList->isInstalled, psWseList->isAction)) + { + hr = ScaWebSvcExtUninstall(&wzWebSvcExtList, (DWORD *)&cchWebSvcExtList, psWseList); + ExitOnFailure(hr, "Failed to uninstall Web Service extension"); + } + + psWseList = psWseList->psWseNext; + } + + // Write Metabase + hr = ScaWriteMetabaseValue(piMetabase, vcsWebSvcExtRoot, NULL, MD_WEB_SVC_EXT_RESTRICTION_LIST, METADATA_INHERIT, IIS_MD_UT_FILE, MULTISZ_METADATA, wzWebSvcExtList); + ExitOnFailure(hr, "Failed to write WebServiceExtensions: '%ls'", wzWebSvcExtList); + +LExit: + ReleaseStr(wzWebSvcExtList); + + return hr; +} + + +static HRESULT ScaWebSvcExtInstall( + __in LPWSTR *ppwzWebSvcExtList, + __in DWORD_PTR *pcchWebSvcExtList, + __in SCA_WEBSVCEXT* psWseList + ) +{ + Assert( ppwzWebSvcExtList && pcchWebSvcExtList && psWseList); + Assert(*ppwzWebSvcExtList); + + HRESULT hr = S_OK; + + LPWSTR pwzWebSvcExt = NULL; + int iAllow; + int iUiDeletable; + + BOOL fAlreadyExists = FALSE; + DWORD_PTR dwIndex = 0xFFFFFFFF; + LPCWSTR wzFoundString = NULL; + + // Check if it's already in there + hr = MultiSzFindSubstring(*ppwzWebSvcExtList, psWseList->wzFile, &dwIndex, &wzFoundString); + ExitOnFailure(hr, "failed to search for string:%ls in web service extension MULTISZ", psWseList->wzFile); + + if (S_FALSE != hr && NULL != wcsstr(wzFoundString, psWseList->wzGroup) && NULL != wcsstr(wzFoundString, psWseList->wzDescription)) + { + fAlreadyExists = TRUE; + } + + // Construct the single string in the format required for the WebSvc Ext list in metabase + iAllow = (psWseList->iAttributes & 1); + iUiDeletable = ((psWseList->iAttributes >> 1) & 1); + hr = StrAllocFormatted(&pwzWebSvcExt, L"%d,%s,%d,%s,%s", iAllow, psWseList->wzFile, iUiDeletable, psWseList->wzGroup, psWseList->wzDescription); + ExitOnFailure(hr, "Failure allocating space for web service extensions"); + + if (fAlreadyExists) + { + hr = MultiSzReplaceString(ppwzWebSvcExtList, dwIndex, pwzWebSvcExt); + ExitOnFailure(hr, "failed to update web service extension string: %ls", pwzWebSvcExt); + } + else + { + hr = MultiSzPrepend(ppwzWebSvcExtList, pcchWebSvcExtList, pwzWebSvcExt); + ExitOnFailure(hr, "failed to prepend web service extension string: %ls", pwzWebSvcExt); + } + +LExit: + ReleaseStr(pwzWebSvcExt); + + return hr; +} + + +static HRESULT ScaWebSvcExtUninstall( + __in LPWSTR *ppwzWebSvcExtList, + __in const DWORD* /*pcchWebSvcExtList*/, + __in SCA_WEBSVCEXT* psWseList + ) +{ + Assert(ppwzWebSvcExtList && *ppwzWebSvcExtList && psWseList); + Assert(*ppwzWebSvcExtList); + + HRESULT hr = S_OK; + DWORD_PTR dwIndex = 0xFFFFFFFF; + LPCWSTR wzFoundString = NULL; + + // Find the string to remove + hr = MultiSzFindSubstring(*ppwzWebSvcExtList, psWseList->wzFile, &dwIndex, &wzFoundString); + ExitOnFailure(hr, "failed to search for string:%ls in web service extension MULTISZ", psWseList->wzFile); + + // If we found a match (ignoring the Allow and Deletable flags) + if (S_FALSE != hr && NULL != wcsstr(wzFoundString, psWseList->wzGroup) && NULL != wcsstr(wzFoundString, psWseList->wzDescription)) + { + hr = MultiSzRemoveString(ppwzWebSvcExtList, dwIndex); + ExitOnFailure(hr, "failed to remove string: %d from web service extension MULTISZ", dwIndex); + } + +LExit: + return hr; +} + + +//static HRESULT ScaCheckWebSvcExtValue( +// __in IMSAdminBase* piMetabase, +// __in DWORD dwMDIdentifier +// ) +//{ +// if (!piMetabase) +// { +// return E_INVALIDARG; +// } +// +// HRESULT hr = S_OK; +// METADATA_RECORD mr = { 0 }; +// DWORD cch = 0; +// +// mr.dwMDIdentifier = dwMDIdentifier; +// mr.dwMDUserType = IIS_MD_UT_SERVER; +// +// hr = piMetabase->GetData(METADATA_MASTER_ROOT_HANDLE, vcsWebSvcExtRoot, &mr, &cch); +// if (HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER) == hr) +// { +// hr = S_OK; +// } +// else if (MD_ERROR_DATA_NOT_FOUND == hr) +// { +// hr = S_FALSE; +// } +// +// return hr; +//} + + +void ScaWebSvcExtFreeList( + __in SCA_WEBSVCEXT* psWseList + ) +{ + SCA_WEBSVCEXT* psWseDelete = psWseList; + while (psWseList) + { + psWseDelete = psWseList; + psWseList = psWseList->psWseNext; + MemFree(psWseDelete); + } +} + + +static HRESULT AddWebSvcExtToList( + __in SCA_WEBSVCEXT** ppsWseList + ) +{ + HRESULT hr = S_OK; + + SCA_WEBSVCEXT* psWse = static_cast(MemAlloc(sizeof(SCA_WEBSVCEXT), TRUE)); + ExitOnNull(psWse, hr, E_OUTOFMEMORY, "failed to allocate element for web svc ext list"); + + psWse->psWseNext = *ppsWseList; + *ppsWseList = psWse; + +LExit: + return hr; +} diff --git a/src/ext/Iis/ca/scawebsvcext.h b/src/ext/Iis/ca/scawebsvcext.h new file mode 100644 index 00000000..4d225b09 --- /dev/null +++ b/src/ext/Iis/ca/scawebsvcext.h @@ -0,0 +1,35 @@ +#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 SCA_WEBSVCEXT_ATTRIBUTES { SWSEATTRIB_ALLOW = 1, SWSEATTRIB_UIDELETABLE = 2 }; + +struct SCA_WEBSVCEXT +{ + // darwin information + INSTALLSTATE isInstalled; + INSTALLSTATE isAction; + + // iis configuation information + WCHAR wzFile[MAX_PATH + 1]; + WCHAR wzDescription[MAX_DARWIN_COLUMN + 1]; + WCHAR wzGroup[MAX_DARWIN_COLUMN + 1]; + + int iAttributes; + + SCA_WEBSVCEXT* psWseNext; +}; + +HRESULT __stdcall ScaWebSvcExtRead( + __in SCA_WEBSVCEXT** ppsWseList, + __inout LPWSTR *ppwzCustomActionData + ); + +HRESULT ScaWebSvcExtCommit( + __in IMSAdminBase* piMetabase, + __in SCA_WEBSVCEXT* psWseList + ); + +void ScaWebSvcExtFreeList( + __in SCA_WEBSVCEXT* psWseList + ); diff --git a/src/ext/Iis/ca/scawebsvcext7.cpp b/src/ext/Iis/ca/scawebsvcext7.cpp new file mode 100644 index 00000000..dcf2f7f8 --- /dev/null +++ b/src/ext/Iis/ca/scawebsvcext7.cpp @@ -0,0 +1,106 @@ +// 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" + +static HRESULT ScaWebSvcExtInstall( + const SCA_WEBSVCEXT* psWseList + ); + +static HRESULT ScaWebSvcExtUninstall( + const SCA_WEBSVCEXT* psWseList + ); + +// functions +// Commit does both install and uninstall +HRESULT __stdcall ScaWebSvcExtCommit7( + __in SCA_WEBSVCEXT* psWseList + ) +{ + HRESULT hr = S_OK; + + if (!psWseList) + { + WcaLog(LOGMSG_VERBOSE, "Skipping ScaWebSvcExtCommit() because there are no web service extensions in the list"); + ExitFunction(); + } + + // Make changes to local copy of metabase + while (psWseList) + { + if (WcaIsInstalling(psWseList->isInstalled, psWseList->isAction)) + { + hr = ScaWebSvcExtInstall(psWseList); + ExitOnFailure(hr, "Failed to install Web Service extension"); + } + else if (WcaIsUninstalling(psWseList->isInstalled, psWseList->isAction)) + { + hr = ScaWebSvcExtUninstall(psWseList); + ExitOnFailure(hr, "Failed to uninstall Web Service extension"); + } + + psWseList = psWseList->psWseNext; + } + + +LExit: + + return hr; +} + + +static HRESULT ScaWebSvcExtInstall( + const SCA_WEBSVCEXT* psWseList + ) +{ + HRESULT hr = S_OK; + int iAllow; + + //Write CAData actions + hr = ScaWriteConfigID(IIS_WEB_SVC_EXT); + ExitOnFailure(hr, "failed add web svc ext ID"); + hr = ScaWriteConfigID(IIS_CREATE); + ExitOnFailure(hr, "failed add web svc ext action"); + + // write File path + hr = ScaWriteConfigString(psWseList->wzFile); + ExitOnFailure(hr, "failed add web svc ext file path"); + + // write allowed + // unDeleatable n/a in IIS7 + iAllow = (psWseList->iAttributes & 1); + hr = ScaWriteConfigInteger(iAllow); + ExitOnFailure(hr, "failed add web svc ext Allowed"); + + //write group + hr = ScaWriteConfigString(psWseList->wzGroup); + ExitOnFailure(hr, "failed add web svc ext group"); + + //write description + hr = ScaWriteConfigString(psWseList->wzDescription); + ExitOnFailure(hr, "failed add web svc ext description"); + +LExit: + + return hr; +} + + +static HRESULT ScaWebSvcExtUninstall( + const SCA_WEBSVCEXT* psWseList + ) +{ + HRESULT hr = S_OK; + + //Write CAData actions + hr = ScaWriteConfigID(IIS_WEB_SVC_EXT); + ExitOnFailure(hr, "failed add web svc ext ID"); + hr = ScaWriteConfigID(IIS_DELETE); + ExitOnFailure(hr, "failed add web svc ext action"); + + // write File path (Key) + hr = ScaWriteConfigString(psWseList->wzFile); + ExitOnFailure(hr, "failed add web svc ext file path"); + +LExit: + return hr; +} diff --git a/src/ext/Iis/ca/scawebsvcext7.h b/src/ext/Iis/ca/scawebsvcext7.h new file mode 100644 index 00000000..1a686aed --- /dev/null +++ b/src/ext/Iis/ca/scawebsvcext7.h @@ -0,0 +1,8 @@ +#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 ScaWebSvcExtCommit7( + __in SCA_WEBSVCEXT* psWseList + ); + diff --git a/src/ext/Iis/nuget.config b/src/ext/Iis/nuget.config new file mode 100644 index 00000000..db7aba29 --- /dev/null +++ b/src/ext/Iis/nuget.config @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/ext/Iis/test/WixToolsetTest.Iis/IisExtensionFixture.cs b/src/ext/Iis/test/WixToolsetTest.Iis/IisExtensionFixture.cs new file mode 100644 index 00000000..685e7126 --- /dev/null +++ b/src/ext/Iis/test/WixToolsetTest.Iis/IisExtensionFixture.cs @@ -0,0 +1,32 @@ +// 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. + +namespace WixToolsetTest.Iis +{ + using System.Linq; + using WixBuildTools.TestSupport; + using WixToolset.Core.TestPackage; + using WixToolset.Iis; + using Xunit; + + public class IisExtensionFixture + { + [Fact] + public void CanBuildUsingIIsWebAddress() + { + var folder = TestData.Get(@"TestData\UsingIis"); + var build = new Builder(folder, typeof(IisExtensionFactory), new[] { folder }); + + var results = build.BuildAndQuery(Build, "IIsWebAddress"); + Assert.Equal(new[] + { + "IIsWebAddress:TestAddress\tTest\t\t[PORT]\t\t0", + }, results); + } + + private static void Build(string[] args) + { + var result = WixRunner.Execute(args) + .AssertSuccess(); + } + } +} diff --git a/src/ext/Iis/test/WixToolsetTest.Iis/TestData/UsingIis/Package.en-us.wxl b/src/ext/Iis/test/WixToolsetTest.Iis/TestData/UsingIis/Package.en-us.wxl new file mode 100644 index 00000000..38c12ac1 --- /dev/null +++ b/src/ext/Iis/test/WixToolsetTest.Iis/TestData/UsingIis/Package.en-us.wxl @@ -0,0 +1,11 @@ + + + + + + A newer version of [ProductName] is already installed. + MsiPackage + + diff --git a/src/ext/Iis/test/WixToolsetTest.Iis/TestData/UsingIis/Package.wxs b/src/ext/Iis/test/WixToolsetTest.Iis/TestData/UsingIis/Package.wxs new file mode 100644 index 00000000..f36aafef --- /dev/null +++ b/src/ext/Iis/test/WixToolsetTest.Iis/TestData/UsingIis/Package.wxs @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + + diff --git a/src/ext/Iis/test/WixToolsetTest.Iis/TestData/UsingIis/PackageComponents.wxs b/src/ext/Iis/test/WixToolsetTest.Iis/TestData/UsingIis/PackageComponents.wxs new file mode 100644 index 00000000..03203b50 --- /dev/null +++ b/src/ext/Iis/test/WixToolsetTest.Iis/TestData/UsingIis/PackageComponents.wxs @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + diff --git a/src/ext/Iis/test/WixToolsetTest.Iis/TestData/UsingIis/example.txt b/src/ext/Iis/test/WixToolsetTest.Iis/TestData/UsingIis/example.txt new file mode 100644 index 00000000..1b4ffe8a --- /dev/null +++ b/src/ext/Iis/test/WixToolsetTest.Iis/TestData/UsingIis/example.txt @@ -0,0 +1 @@ +This is example.txt. \ No newline at end of file diff --git a/src/ext/Iis/test/WixToolsetTest.Iis/WixToolsetTest.Iis.csproj b/src/ext/Iis/test/WixToolsetTest.Iis/WixToolsetTest.Iis.csproj new file mode 100644 index 00000000..49ed89bc --- /dev/null +++ b/src/ext/Iis/test/WixToolsetTest.Iis/WixToolsetTest.Iis.csproj @@ -0,0 +1,41 @@ + + + + + + netcoreapp3.1 + false + + + + NU1701 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/ext/Iis/test/WixToolsetTest.Iis/WixToolsetTest.Iis.v3.ncrunchproject b/src/ext/Iis/test/WixToolsetTest.Iis/WixToolsetTest.Iis.v3.ncrunchproject new file mode 100644 index 00000000..7b5b2139 --- /dev/null +++ b/src/ext/Iis/test/WixToolsetTest.Iis/WixToolsetTest.Iis.v3.ncrunchproject @@ -0,0 +1,5 @@ + + + True + + \ No newline at end of file diff --git a/src/ext/Iis/wix.snk b/src/ext/Iis/wix.snk new file mode 100644 index 00000000..3908a66a Binary files /dev/null and b/src/ext/Iis/wix.snk differ diff --git a/src/ext/Iis/wixext/IIsCompiler.cs b/src/ext/Iis/wixext/IIsCompiler.cs new file mode 100644 index 00000000..cb573ad1 --- /dev/null +++ b/src/ext/Iis/wixext/IIsCompiler.cs @@ -0,0 +1,2620 @@ +// 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. + +namespace WixToolset.Iis +{ + using System; + using System.Collections.Generic; + using System.Globalization; + using System.Xml.Linq; + using WixToolset.Data; + using WixToolset.Extensibility; + using WixToolset.Extensibility.Data; + using WixToolset.Iis.Symbols; + + /// + /// The compiler for the WiX Toolset Internet Information Services Extension. + /// + public sealed class IIsCompiler : BaseCompilerExtension + { + public override XNamespace Namespace => "http://wixtoolset.org/schemas/v4/wxs/iis"; + + /// + /// Types of objects that custom HTTP Headers can be applied to. + /// + /// Note that this must be kept in sync with the eHttpHeaderParentType in scahttpheader.h. + private enum HttpHeaderParentType + { + /// Custom HTTP Header is to be applied to a Web Virtual Directory. + WebVirtualDir = 1, + /// Custom HTTP Header is to be applied to a Web Site. + WebSite = 2, + } + + /// + /// Types of objects that MimeMaps can be applied to. + /// + /// Note that this must be kept in sync with the eMimeMapParentType in scamimemap.h. + private enum MimeMapParentType + { + /// MimeMap is to be applied to a Web Virtual Directory. + WebVirtualDir = 1, + WebSite = 2, + } + + /// + /// Types of objects that custom WebErrors can be applied to. + /// + /// Note that this must be kept in sync with the eWebErrorParentType in scaweberror.h. + private enum WebErrorParentType + { + /// Custom WebError is to be applied to a Web Virtual Directory. + WebVirtualDir = 1, + + /// Custom WebError is to be applied to a Web Site. + WebSite = 2, + } + + /// + /// Processes an element for the Compiler. + /// + /// Source line number for the parent element. + /// Parent element of element to process. + /// Element to process. + /// Extra information about the context in which this element is being parsed. + public override void ParseElement(Intermediate intermediate, IntermediateSection section, XElement parentElement, XElement element, IDictionary context) + { + switch (parentElement.Name.LocalName) + { + case "Component": + var componentId = context["ComponentId"]; + var directoryId = context["DirectoryId"]; + + switch (element.Name.LocalName) + { + case "Certificate": + this.ParseCertificateElement(intermediate, section, element, componentId); + break; + case "WebAppPool": + this.ParseWebAppPoolElement(intermediate, section, element, componentId); + break; + case "WebDir": + this.ParseWebDirElement(intermediate, section, element, componentId, null); + break; + case "WebFilter": + this.ParseWebFilterElement(intermediate, section, element, componentId, null); + break; + case "WebProperty": + this.ParseWebPropertyElement(intermediate, section, element, componentId); + break; + case "WebServiceExtension": + this.ParseWebServiceExtensionElement(intermediate, section, element, componentId); + break; + case "WebSite": + this.ParseWebSiteElement(intermediate, section, element, componentId); + break; + case "WebVirtualDir": + this.ParseWebVirtualDirElement(intermediate, section, element, componentId, null, null); + break; + default: + this.ParseHelper.UnexpectedElement(parentElement, element); + break; + } + break; + case "Fragment": + case "Module": + case "Package": + switch (element.Name.LocalName) + { + case "WebApplication": + this.ParseWebApplicationElement(intermediate, section, element); + break; + case "WebAppPool": + this.ParseWebAppPoolElement(intermediate, section, element, null); + break; + case "WebDirProperties": + this.ParseWebDirPropertiesElement(intermediate, section, element, null); + break; + case "WebLog": + this.ParseWebLogElement(intermediate, section, element); + break; + case "WebSite": + this.ParseWebSiteElement(intermediate, section, element, null); + break; + default: + this.ParseHelper.UnexpectedElement(parentElement, element); + break; + } + break; + default: + this.ParseHelper.UnexpectedElement(parentElement, element); + break; + } + } + + /// + /// Parses a certificate element. + /// + /// Element to parse. + /// Identifier for parent component. + private void ParseCertificateElement(Intermediate intermediate, IntermediateSection section, XElement element, string componentId) + { + var sourceLineNumbers = this.ParseHelper.GetSourceLineNumbers(element); + Identifier id = null; + int attributes = 8; // SCA_CERT_ATTRIBUTE_VITAL + string binaryRef = null; + string certificatePath = null; + string name = null; + string pfxPassword = null; + int storeLocation = 0; + string storeName = null; + + foreach (var attrib in element.Attributes()) + { + if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || this.Namespace == attrib.Name.Namespace) + { + switch (attrib.Name.LocalName) + { + case "Id": + id = this.ParseHelper.GetAttributeIdentifier(sourceLineNumbers, attrib); + break; + case "BinaryRef": + attributes |= 2; // SCA_CERT_ATTRIBUTE_BINARYDATA + binaryRef = this.ParseHelper.GetAttributeIdentifierValue(sourceLineNumbers, attrib); + this.ParseHelper.CreateSimpleReference(section, sourceLineNumbers, SymbolDefinitions.Binary, binaryRef); + break; + case "CertificatePath": + certificatePath = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib); + break; + case "Name": + name = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib); + break; + case "Overwrite": + if (YesNoType.Yes == this.ParseHelper.GetAttributeYesNoValue(sourceLineNumbers, attrib)) + { + attributes |= 4; // SCA_CERT_ATTRIBUTE_OVERWRITE + } + else + { + attributes &= ~4; // SCA_CERT_ATTRIBUTE_OVERWRITE + } + break; + case "PFXPassword": + pfxPassword = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib); + break; + case "Request": + if (YesNoType.Yes == this.ParseHelper.GetAttributeYesNoValue(sourceLineNumbers, attrib)) + { + attributes |= 1; // SCA_CERT_ATTRIBUTE_REQUEST + } + else + { + attributes &= ~1; // SCA_CERT_ATTRIBUTE_REQUEST + } + break; + case "StoreLocation": + var storeLocationValue = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib); + if (0 < storeLocationValue.Length) + { + switch (storeLocationValue) + { + case "currentUser": + storeLocation = 1; // SCA_CERTSYSTEMSTORE_CURRENTUSER + break; + case "localMachine": + storeLocation = 2; // SCA_CERTSYSTEMSTORE_LOCALMACHINE + break; + default: + storeLocation = -1; + this.Messaging.Write(ErrorMessages.IllegalAttributeValue(sourceLineNumbers, element.Name.LocalName, "StoreLocation", storeLocationValue, "currentUser", "localMachine")); + break; + } + } + break; + case "StoreName": + var storeNameValue = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib); + if (0 < storeNameValue.Length) + { + switch (storeNameValue) + { + case "ca": + storeName = "CA"; + break; + case "my": + case "personal": + storeName = "MY"; + break; + case "request": + storeName = "REQUEST"; + break; + case "root": + storeName = "Root"; + break; + case "otherPeople": + storeName = "AddressBook"; + break; + case "trustedPeople": + storeName = "TrustedPeople"; + break; + case "trustedPublisher": + storeName = "TrustedPublisher"; + break; + default: + this.Messaging.Write(ErrorMessages.IllegalAttributeValue(sourceLineNumbers, element.Name.LocalName, "StoreName", storeNameValue, "ca", "my", "request", "root", "otherPeople", "trustedPeople", "trustedPublisher")); + break; + } + } + break; + case "Vital": + if (YesNoType.Yes == this.ParseHelper.GetAttributeYesNoValue(sourceLineNumbers, attrib)) + { + attributes |= 8; // SCA_CERT_ATTRIBUTE_VITAL + } + else + { + attributes &= ~8; // SCA_CERT_ATTRIBUTE_VITAL + } + break; + default: + this.ParseHelper.UnexpectedAttribute(element, attrib); + break; + } + } + else + { + this.ParseHelper.ParseExtensionAttribute(this.Context.Extensions, intermediate, section, element, attrib); + } + } + + + if (null == id) + { + id = this.ParseHelper.CreateIdentifier("crt", componentId, binaryRef, certificatePath); + } + + if (null == name) + { + this.Messaging.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, element.Name.LocalName, "Name")); + } + + if (0 == storeLocation) + { + this.Messaging.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, element.Name.LocalName, "StoreLocation")); + } + + if (null == storeName) + { + this.Messaging.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, element.Name.LocalName, "StoreName")); + } + + if (null != binaryRef && null != certificatePath) + { + this.Messaging.Write(ErrorMessages.IllegalAttributeWithOtherAttribute(sourceLineNumbers, element.Name.LocalName, "BinaryRef", "CertificatePath", certificatePath)); + } + else if (null == binaryRef && null == certificatePath) + { + this.Messaging.Write(ErrorMessages.ExpectedAttributes(sourceLineNumbers, element.Name.LocalName, "BinaryRef", "CertificatePath")); + } + + this.ParseHelper.ParseForExtensionElements(this.Context.Extensions, intermediate, section, element); + + // Reference InstallCertificates and UninstallCertificates since nothing will happen without them + this.ParseHelper.CreateCustomActionReference(sourceLineNumbers, section, "Wix4InstallCertificates", this.Context.Platform, CustomActionPlatforms.X86 | CustomActionPlatforms.X64 | CustomActionPlatforms.ARM64); + this.ParseHelper.CreateCustomActionReference(sourceLineNumbers, section, "Wix4UninstallCertificates", this.Context.Platform, CustomActionPlatforms.X86 | CustomActionPlatforms.X64 | CustomActionPlatforms.ARM64); + this.ParseHelper.EnsureTable(section, sourceLineNumbers, IisTableDefinitions.CertificateHash); // Certificate CustomActions require the CertificateHash table + + if (!this.Messaging.EncounteredError) + { + section.AddSymbol(new CertificateSymbol(sourceLineNumbers, id) + { + ComponentRef = componentId, + Name = name, + StoreLocation = storeLocation, + StoreName = storeName, + Attributes = attributes, + BinaryRef = binaryRef, + CertificatePath = certificatePath, + PFXPassword = pfxPassword, + }); + } + } + + /// + /// Parses a CertificateRef extension element. + /// + /// Element to parse. + /// Identifier for parent web site. + private void ParseCertificateRefElement(Intermediate intermediate, IntermediateSection section, XElement element, string webId) + { + var sourceLineNumbers = this.ParseHelper.GetSourceLineNumbers(element); + Identifier id = null; + + foreach (var attrib in element.Attributes()) + { + if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || this.Namespace == attrib.Name.Namespace) + { + switch (attrib.Name.LocalName) + { + case "Id": + id = this.ParseHelper.GetAttributeIdentifier(sourceLineNumbers, attrib); + this.ParseHelper.CreateSimpleReference(section, sourceLineNumbers, IisSymbolDefinitions.Certificate, id.Id); + break; + default: + this.ParseHelper.UnexpectedAttribute(element, attrib); + break; + } + } + else + { + this.ParseHelper.ParseExtensionAttribute(this.Context.Extensions, intermediate, section, element, attrib); + } + } + + if (null == id) + { + id = this.ParseHelper.CreateIdentifier("wsc", webId); + } + + this.ParseHelper.ParseForExtensionElements(this.Context.Extensions, intermediate, section, element); + + if (!this.Messaging.EncounteredError) + { + this.ParseHelper.CreateSimpleReference(section, sourceLineNumbers, IisSymbolDefinitions.Certificate, id.Id); + + section.AddSymbol(new IIsWebSiteCertificatesSymbol(sourceLineNumbers) + { + WebRef = webId, + CertificateRef = id.Id, + }); + } + } + + /// + /// Parses a mime map element. + /// + /// Element to parse. + /// Identifier for parent symbol. + /// Type that parentId refers to. + private void ParseMimeMapElement(Intermediate intermediate, IntermediateSection section, XElement element, string parentId, MimeMapParentType parentType) + { + var sourceLineNumbers = this.ParseHelper.GetSourceLineNumbers(element); + Identifier id = null; + string extension = null; + string type = null; + + foreach (var attrib in element.Attributes()) + { + if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || this.Namespace == attrib.Name.Namespace) + { + switch (attrib.Name.LocalName) + { + case "Id": + id = this.ParseHelper.GetAttributeIdentifier(sourceLineNumbers, attrib); + break; + case "Extension": + extension = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib); + break; + case "Type": + type = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib); + break; + default: + this.ParseHelper.UnexpectedAttribute(element, attrib); + break; + } + } + else + { + this.ParseHelper.ParseExtensionAttribute(this.Context.Extensions, intermediate, section, element, attrib); + } + } + + if (null == id) + { + id = this.ParseHelper.CreateIdentifier("imm", parentId, type, extension); + } + + if (null == extension) + { + this.Messaging.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, element.Name.LocalName, "Extension")); + } + else if (0 < extension.Length) + { + if (!extension.StartsWith(".", StringComparison.Ordinal)) + { + this.Messaging.Write(IIsErrors.MimeMapExtensionMissingPeriod(sourceLineNumbers, element.Name.LocalName, "Extension", extension)); + } + } + + if (null == type) + { + this.Messaging.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, element.Name.LocalName, "Type")); + } + + this.ParseHelper.ParseForExtensionElements(this.Context.Extensions, intermediate, section, element); + + if (!this.Messaging.EncounteredError) + { + section.AddSymbol(new IIsMimeMapSymbol(sourceLineNumbers, id) + { + ParentType = (int)parentType, + ParentValue = parentId, + MimeType = type, + Extension = extension, + }); + } + } + + /// + /// Parses a recycle time element. + /// + /// Element to parse. + /// Recycle time value. + private string ParseRecycleTimeElement(Intermediate intermediate, IntermediateSection section, XElement element) + { + var sourceLineNumbers = this.ParseHelper.GetSourceLineNumbers(element); + string value = null; + + foreach (var attrib in element.Attributes()) + { + if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || this.Namespace == attrib.Name.Namespace) + { + switch (attrib.Name.LocalName) + { + case "Value": + value = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib); + break; + default: + this.ParseHelper.UnexpectedAttribute(element, attrib); + break; + } + } + else + { + this.ParseHelper.ParseExtensionAttribute(this.Context.Extensions, intermediate, section, element, attrib); + } + } + + if (null == value) + { + this.Messaging.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, element.Name.LocalName, "Value")); + } + + this.ParseHelper.ParseForExtensionElements(this.Context.Extensions, intermediate, section, element); + + return value; + } + + /// + /// Parses a web address element. + /// + /// Element to parse. + /// Identifier of parent web site. + /// Identifier for web address. + private string ParseWebAddressElement(Intermediate intermediate, IntermediateSection section, XElement element, string parentWeb) + { + var sourceLineNumbers = this.ParseHelper.GetSourceLineNumbers(element); + Identifier id = null; + string header = null; + string ip = null; + string port = null; + var secure = false; + + foreach (var attrib in element.Attributes()) + { + if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || this.Namespace == attrib.Name.Namespace) + { + switch (attrib.Name.LocalName) + { + case "Id": + id = this.ParseHelper.GetAttributeIdentifier(sourceLineNumbers, attrib); + break; + case "Header": + header = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib); + break; + case "IP": + ip = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib); + break; + case "Port": + port = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib); + break; + case "Secure": + secure = YesNoType.Yes == this.ParseHelper.GetAttributeYesNoValue(sourceLineNumbers, attrib); + break; + default: + this.ParseHelper.UnexpectedAttribute(element, attrib); + break; + } + } + else + { + this.ParseHelper.ParseExtensionAttribute(this.Context.Extensions, intermediate, section, element, attrib); + } + } + + if (null == id) + { + id = this.ParseHelper.CreateIdentifier("iwa", parentWeb, ip, port); + } + + if (null == port) + { + this.Messaging.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, element.Name.LocalName, "Port")); + } + + this.ParseHelper.ParseForExtensionElements(this.Context.Extensions, intermediate, section, element); + + if (!this.Messaging.EncounteredError) + { + section.AddSymbol(new IIsWebAddressSymbol(sourceLineNumbers, id) + { + WebRef = parentWeb, + IP = ip, + Port = port, + Header = header, + Secure = secure ? 1 : 0, + }); + } + + return id?.Id; + } + + /// + /// Parses a web application element. + /// + /// Element to parse. + /// Identifier for web application. + private string ParseWebApplicationElement(Intermediate intermediate, IntermediateSection section, XElement element) + { + var sourceLineNumbers = this.ParseHelper.GetSourceLineNumbers(element); + Identifier id = null; + var allowSessions = YesNoDefaultType.Default; + string appPool = null; + var buffer = YesNoDefaultType.Default; + var clientDebugging = YesNoDefaultType.Default; + string defaultScript = null; + int isolation = 0; + string name = null; + var parentPaths = YesNoDefaultType.Default; + var scriptTimeout = CompilerConstants.IntegerNotSet; + var sessionTimeout = CompilerConstants.IntegerNotSet; + var serverDebugging = YesNoDefaultType.Default; + + foreach (var attrib in element.Attributes()) + { + if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || this.Namespace == attrib.Name.Namespace) + { + switch (attrib.Name.LocalName) + { + case "Id": + id = this.ParseHelper.GetAttributeIdentifier(sourceLineNumbers, attrib); + break; + case "AllowSessions": + allowSessions = this.ParseHelper.GetAttributeYesNoDefaultValue(sourceLineNumbers, attrib); + break; + case "Buffer": + buffer = this.ParseHelper.GetAttributeYesNoDefaultValue(sourceLineNumbers, attrib); + break; + case "ClientDebugging": + clientDebugging = this.ParseHelper.GetAttributeYesNoDefaultValue(sourceLineNumbers, attrib); + break; + case "DefaultScript": + defaultScript = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib); + if (0 < defaultScript.Length) + { + switch (defaultScript) + { + case "JScript": + case "VBScript": + // these are valid values + break; + default: + this.Messaging.Write(ErrorMessages.IllegalAttributeValue(sourceLineNumbers, element.Name.LocalName, attrib.Name.LocalName, defaultScript, "JScript", "VBScript")); + break; + } + } + break; + case "Isolation": + string isolationValue = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib); + if (0 < isolationValue.Length) + { + switch (isolationValue) + { + case "low": + isolation = 0; + break; + case "medium": + isolation = 2; + break; + case "high": + isolation = 1; + break; + default: + this.Messaging.Write(ErrorMessages.IllegalAttributeValue(sourceLineNumbers, element.Name.LocalName, attrib.Name.LocalName, isolationValue, "low", "medium", "high")); + break; + } + } + break; + case "Name": + name = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib); + break; + case "ParentPaths": + parentPaths = this.ParseHelper.GetAttributeYesNoDefaultValue(sourceLineNumbers, attrib); + break; + case "ScriptTimeout": + scriptTimeout = this.ParseHelper.GetAttributeIntegerValue(sourceLineNumbers, attrib, 0, short.MaxValue); + break; + case "ServerDebugging": + serverDebugging = this.ParseHelper.GetAttributeYesNoDefaultValue(sourceLineNumbers, attrib); + break; + case "SessionTimeout": + sessionTimeout = this.ParseHelper.GetAttributeIntegerValue(sourceLineNumbers, attrib, 0, short.MaxValue); + break; + case "WebAppPool": + appPool = this.ParseHelper.GetAttributeIdentifierValue(sourceLineNumbers, attrib); + this.ParseHelper.CreateSimpleReference(section, sourceLineNumbers, IisSymbolDefinitions.IIsAppPool, appPool); + break; + default: + this.ParseHelper.UnexpectedAttribute(element, attrib); + break; + } + } + else + { + this.ParseHelper.ParseExtensionAttribute(this.Context.Extensions, intermediate, section, element, attrib); + } + } + + if (null == id) + { + id = this.ParseHelper.CreateIdentifier("wap", name, appPool); + } + + if (null == name) + { + this.Messaging.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, element.Name.LocalName, "Name")); + } + else if (-1 != name.IndexOf("\\", StringComparison.Ordinal)) + { + this.Messaging.Write(IIsErrors.IllegalCharacterInAttributeValue(sourceLineNumbers, element.Name.LocalName, "Name", name, '\\')); + } + + foreach (var child in element.Elements()) + { + if (this.Namespace == child.Name.Namespace) + { + switch (child.Name.LocalName) + { + case "WebApplicationExtension": + this.ParseWebApplicationExtensionElement(intermediate, section, child, id?.Id); + break; + default: + this.ParseHelper.UnexpectedElement(element, child); + break; + } + } + else + { + this.ParseHelper.ParseExtensionElement(this.Context.Extensions, intermediate, section, element, child); + } + } + + if (!this.Messaging.EncounteredError) + { + var symbol = section.AddSymbol(new IIsWebApplicationSymbol(sourceLineNumbers, id) + { + Name = name, + Isolation = isolation, + DefaultScript = defaultScript, + AppPoolRef = appPool, + }); + + if (YesNoDefaultType.Default != allowSessions) + { + symbol.AllowSessions = YesNoDefaultType.Yes == allowSessions ? 1 : 0; + } + + if (CompilerConstants.IntegerNotSet != sessionTimeout) + { + symbol.SessionTimeout = sessionTimeout; + } + + if (YesNoDefaultType.Default != buffer) + { + symbol.Buffer = YesNoDefaultType.Yes == buffer ? 1 : 0; + } + + if (YesNoDefaultType.Default != parentPaths) + { + symbol.ParentPaths = YesNoDefaultType.Yes == parentPaths ? 1 : 0; + } + + if (CompilerConstants.IntegerNotSet != scriptTimeout) + { + symbol.ScriptTimeout = scriptTimeout; + } + + if (YesNoDefaultType.Default != serverDebugging) + { + symbol.ServerDebugging = YesNoDefaultType.Yes == serverDebugging ? 1 : 0; + } + + if (YesNoDefaultType.Default != clientDebugging) + { + symbol.ClientDebugging = YesNoDefaultType.Yes == clientDebugging ? 1 : 0; + } + } + + return id?.Id; + } + + /// + /// Parses a web application extension element. + /// + /// Element to parse. + /// Identifier for parent web application. + private void ParseWebApplicationExtensionElement(Intermediate intermediate, IntermediateSection section, XElement element, string application) + { + var sourceLineNumbers = this.ParseHelper.GetSourceLineNumbers(element); + int attributes = 0; + string executable = null; + string extension = null; + string verbs = null; + + foreach (var attrib in element.Attributes()) + { + if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || this.Namespace == attrib.Name.Namespace) + { + switch (attrib.Name.LocalName) + { + case "CheckPath": + if (YesNoType.Yes == this.ParseHelper.GetAttributeYesNoValue(sourceLineNumbers, attrib)) + { + attributes |= 4; + } + else + { + attributes &= ~4; + } + break; + case "Executable": + executable = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib); + break; + case "Extension": + extension = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib); + break; + case "Script": + if (YesNoType.Yes == this.ParseHelper.GetAttributeYesNoValue(sourceLineNumbers, attrib)) + { + attributes |= 1; + } + else + { + attributes &= ~1; + } + break; + case "Verbs": + verbs = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib); + break; + default: + this.ParseHelper.UnexpectedAttribute(element, attrib); + break; + } + } + else + { + this.ParseHelper.ParseExtensionAttribute(this.Context.Extensions, intermediate, section, element, attrib); + } + } + + this.ParseHelper.ParseForExtensionElements(this.Context.Extensions, intermediate, section, element); + + if (!this.Messaging.EncounteredError) + { + var symbol = section.AddSymbol(new IIsWebApplicationExtensionSymbol(sourceLineNumbers) + { + ApplicationRef = application, + Extension = extension, + Verbs = verbs, + Executable = executable, + }); + + if (0 < attributes) + { + symbol.Attributes = attributes; + } + } + } + + /// + /// Parses web application pool element. + /// + /// Element to parse. + /// Optional identifier of parent component. + private void ParseWebAppPoolElement(Intermediate intermediate, IntermediateSection section, XElement element, string componentId) + { + var sourceLineNumbers = this.ParseHelper.GetSourceLineNumbers(element); + Identifier id = null; + int attributes = 0; + var cpuAction = CompilerConstants.IntegerNotSet; + string cpuMon = null; + var idleTimeout = CompilerConstants.IntegerNotSet; + int maxCpuUsage = 0; + var maxWorkerProcs = CompilerConstants.IntegerNotSet; + string managedRuntimeVersion = null; + string managedPipelineMode = null; + string name = null; + var privateMemory = CompilerConstants.IntegerNotSet; + var queueLimit = CompilerConstants.IntegerNotSet; + var recycleMinutes = CompilerConstants.IntegerNotSet; + var recycleRequests = CompilerConstants.IntegerNotSet; + string recycleTimes = null; + var refreshCpu = CompilerConstants.IntegerNotSet; + string user = null; + var virtualMemory = CompilerConstants.IntegerNotSet; + + foreach (var attrib in element.Attributes()) + { + if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || this.Namespace == attrib.Name.Namespace) + { + switch (attrib.Name.LocalName) + { + case "Id": + id = this.ParseHelper.GetAttributeIdentifier(sourceLineNumbers, attrib); + break; + case "CpuAction": + if (null == componentId) + { + this.Messaging.Write(IIsErrors.IllegalAttributeWithoutComponent(sourceLineNumbers, element.Name.LocalName, attrib.Name.LocalName)); + } + + var cpuActionValue = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib); + if (0 < cpuActionValue.Length) + { + switch (cpuActionValue) + { + case "shutdown": + cpuAction = 1; + break; + case "none": + cpuAction = 0; + break; + default: + this.Messaging.Write(ErrorMessages.IllegalAttributeValue(sourceLineNumbers, element.Name.LocalName, attrib.Name.LocalName, cpuActionValue, "shutdown", "none")); + break; + } + } + break; + case "Identity": + if (null == componentId) + { + this.Messaging.Write(IIsErrors.IllegalAttributeWithoutComponent(sourceLineNumbers, element.Name.LocalName, attrib.Name.LocalName)); + } + + var identityValue = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib); + if (0 < identityValue.Length) + { + switch (identityValue) + { + case "networkService": + attributes |= 1; + break; + case "localService": + attributes |= 2; + break; + case "localSystem": + attributes |= 4; + break; + case "other": + attributes |= 8; + break; + case "applicationPoolIdentity": + attributes |= 0x10; + break; + default: + this.Messaging.Write(ErrorMessages.IllegalAttributeValue(sourceLineNumbers, element.Name.LocalName, attrib.Name.LocalName, identityValue, "networkService", "localService", "localSystem", "other", "applicationPoolIdentity")); + break; + } + } + break; + case "IdleTimeout": + if (null == componentId) + { + this.Messaging.Write(IIsErrors.IllegalAttributeWithoutComponent(sourceLineNumbers, element.Name.LocalName, attrib.Name.LocalName)); + } + + idleTimeout = this.ParseHelper.GetAttributeIntegerValue(sourceLineNumbers, attrib, 0, short.MaxValue); + break; + case "ManagedPipelineMode": + if (null == componentId) + { + this.Messaging.Write(IIsErrors.IllegalAttributeWithoutComponent(sourceLineNumbers, element.Name.LocalName, attrib.Name.LocalName)); + } + + managedPipelineMode = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib); + + + if (!String.IsNullOrEmpty(managedPipelineMode)) + { + switch (managedPipelineMode) + { + // In 3.5 we allowed lower case values (per camel case enum style), we now use formatted fields, + // so the value needs to match exactly what we pass in to IIS which uses pascal case. + case "classic": + managedPipelineMode = "Classic"; + break; + case "integrated": + managedPipelineMode = "Integrated"; + break; + case "Classic": + break; + case "Integrated": + break; + default: + if (!this.ParseHelper.ContainsProperty(managedPipelineMode)) + { + this.Messaging.Write(ErrorMessages.IllegalAttributeValue(sourceLineNumbers, element.Name.LocalName, attrib.Name.LocalName, managedPipelineMode, "Classic", "Integrated")); + } + break; + } + } + + break; + case "ManagedRuntimeVersion": + if (null == componentId) + { + this.Messaging.Write(IIsErrors.IllegalAttributeWithoutComponent(sourceLineNumbers, element.Name.LocalName, attrib.Name.LocalName)); + } + + managedRuntimeVersion = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib); + break; + case "MaxCpuUsage": + if (null == componentId) + { + this.Messaging.Write(IIsErrors.IllegalAttributeWithoutComponent(sourceLineNumbers, element.Name.LocalName, attrib.Name.LocalName)); + } + + maxCpuUsage = this.ParseHelper.GetAttributeIntegerValue(sourceLineNumbers, attrib, 0, 100); + break; + case "MaxWorkerProcesses": + if (null == componentId) + { + this.Messaging.Write(IIsErrors.IllegalAttributeWithoutComponent(sourceLineNumbers, element.Name.LocalName, attrib.Name.LocalName)); + } + + maxWorkerProcs = this.ParseHelper.GetAttributeIntegerValue(sourceLineNumbers, attrib, 0, short.MaxValue); + break; + case "Name": + name = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib); + break; + case "PrivateMemory": + if (null == componentId) + { + this.Messaging.Write(IIsErrors.IllegalAttributeWithoutComponent(sourceLineNumbers, element.Name.LocalName, attrib.Name.LocalName)); + } + + privateMemory = this.ParseHelper.GetAttributeIntegerValue(sourceLineNumbers, attrib, 0, 4294967); + break; + case "QueueLimit": + if (null == componentId) + { + this.Messaging.Write(IIsErrors.IllegalAttributeWithoutComponent(sourceLineNumbers, element.Name.LocalName, attrib.Name.LocalName)); + } + + queueLimit = this.ParseHelper.GetAttributeIntegerValue(sourceLineNumbers, attrib, 0, short.MaxValue); + break; + case "RecycleMinutes": + if (null == componentId) + { + this.Messaging.Write(IIsErrors.IllegalAttributeWithoutComponent(sourceLineNumbers, element.Name.LocalName, attrib.Name.LocalName)); + } + + recycleMinutes = this.ParseHelper.GetAttributeIntegerValue(sourceLineNumbers, attrib, 0, short.MaxValue); + break; + case "RecycleRequests": + if (null == componentId) + { + this.Messaging.Write(IIsErrors.IllegalAttributeWithoutComponent(sourceLineNumbers, element.Name.LocalName, attrib.Name.LocalName)); + } + + recycleRequests = this.ParseHelper.GetAttributeIntegerValue(sourceLineNumbers, attrib, 0, short.MaxValue); + break; + case "RefreshCpu": + if (null == componentId) + { + this.Messaging.Write(IIsErrors.IllegalAttributeWithoutComponent(sourceLineNumbers, element.Name.LocalName, attrib.Name.LocalName)); + } + + refreshCpu = this.ParseHelper.GetAttributeIntegerValue(sourceLineNumbers, attrib, 0, int.MaxValue); + break; + case "User": + if (null == componentId) + { + this.Messaging.Write(IIsErrors.IllegalAttributeWithoutComponent(sourceLineNumbers, element.Name.LocalName, attrib.Name.LocalName)); + } + + user = this.ParseHelper.GetAttributeIdentifierValue(sourceLineNumbers, attrib); + this.ParseHelper.CreateSimpleReference(section, sourceLineNumbers, "User", user); + break; + case "VirtualMemory": + if (null == componentId) + { + this.Messaging.Write(IIsErrors.IllegalAttributeWithoutComponent(sourceLineNumbers, element.Name.LocalName, attrib.Name.LocalName)); + } + + virtualMemory = this.ParseHelper.GetAttributeIntegerValue(sourceLineNumbers, attrib, 0, 4294967); + break; + default: + this.ParseHelper.UnexpectedAttribute(element, attrib); + break; + } + } + else + { + this.ParseHelper.ParseExtensionAttribute(this.Context.Extensions, intermediate, section, element, attrib); + } + } + + if (null == id) + { + id = this.ParseHelper.CreateIdentifier("iap", name, componentId, user); + } + + if (null == name) + { + this.Messaging.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, element.Name.LocalName, "Name")); + } + + if (null == user && 8 == (attributes & 0x1F)) + { + this.Messaging.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, element.Name.LocalName, "User", "Identity", "other")); + } + + if (null != user && 8 != (attributes & 0x1F)) + { + this.Messaging.Write(ErrorMessages.IllegalAttributeValueWithoutOtherAttribute(sourceLineNumbers, element.Name.LocalName, "User", user, "Identity", "other")); + } + + cpuMon = maxCpuUsage.ToString(CultureInfo.InvariantCulture.NumberFormat); + if (CompilerConstants.IntegerNotSet != refreshCpu) + { + cpuMon = String.Concat(cpuMon, ",", refreshCpu.ToString(CultureInfo.InvariantCulture.NumberFormat)); + if (CompilerConstants.IntegerNotSet != cpuAction) + { + cpuMon = String.Concat(cpuMon, ",", cpuAction.ToString(CultureInfo.InvariantCulture.NumberFormat)); + } + } + + foreach (var child in element.Elements()) + { + if (this.Namespace == child.Name.Namespace) + { + switch (child.Name.LocalName) + { + case "RecycleTime": + if (null == componentId) + { + var childSourceLineNumbers = this.ParseHelper.GetSourceLineNumbers(child); + this.Messaging.Write(IIsErrors.IllegalElementWithoutComponent(childSourceLineNumbers, element.Name.LocalName)); + } + + if (null == recycleTimes) + { + recycleTimes = this.ParseRecycleTimeElement(intermediate, section, child); + } + else + { + recycleTimes = String.Concat(recycleTimes, ",", this.ParseRecycleTimeElement(intermediate, section, child)); + } + break; + default: + this.ParseHelper.UnexpectedElement(element, child); + break; + } + } + else + { + this.ParseHelper.ParseExtensionElement(this.Context.Extensions, intermediate, section, element, child); + } + } + + if (null != componentId) + { + // Reference ConfigureIIs since nothing will happen without it + this.AddReferenceToConfigureIIs(section, sourceLineNumbers); + } + + if (!this.Messaging.EncounteredError) + { + var symbol = section.AddSymbol(new IIsAppPoolSymbol(sourceLineNumbers, id) + { + Name = name, + ComponentRef = componentId, + Attributes = attributes, + UserRef = user, + RecycleTimes = recycleTimes, + CPUMon = cpuMon, + ManagedRuntimeVersion = managedRuntimeVersion, + ManagedPipelineMode = managedPipelineMode, + }); + + if (CompilerConstants.IntegerNotSet != recycleMinutes) + { + symbol.RecycleMinutes = recycleMinutes; + } + + if (CompilerConstants.IntegerNotSet != recycleRequests) + { + symbol.RecycleRequests = recycleRequests; + } + + if (CompilerConstants.IntegerNotSet != idleTimeout) + { + symbol.IdleTimeout = idleTimeout; + } + + if (CompilerConstants.IntegerNotSet != queueLimit) + { + symbol.QueueLimit = queueLimit; + } + + if (CompilerConstants.IntegerNotSet != maxWorkerProcs) + { + symbol.MaxProc = maxWorkerProcs; + } + + if (CompilerConstants.IntegerNotSet != virtualMemory) + { + symbol.VirtualMemory = virtualMemory; + } + + if (CompilerConstants.IntegerNotSet != privateMemory) + { + symbol.PrivateMemory = privateMemory; + } + } + } + + /// + /// Parses a web directory element. + /// + /// Element to parse. + /// Identifier for parent component. + /// Optional identifier for parent web site. + private void ParseWebDirElement(Intermediate intermediate, IntermediateSection section, XElement element, string componentId, string parentWeb) + { + var sourceLineNumbers = this.ParseHelper.GetSourceLineNumbers(element); + Identifier id = null; + string dirProperties = null; + string path = null; + string application = null; + + foreach (var attrib in element.Attributes()) + { + if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || this.Namespace == attrib.Name.Namespace) + { + switch (attrib.Name.LocalName) + { + case "Id": + id = this.ParseHelper.GetAttributeIdentifier(sourceLineNumbers, attrib); + break; + case "DirProperties": + dirProperties = this.ParseHelper.GetAttributeIdentifierValue(sourceLineNumbers, attrib); + break; + case "Path": + path = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib); + break; + case "WebApplication": + application = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib); + break; + case "WebSite": + if (null != parentWeb) + { + this.Messaging.Write(IIsErrors.WebSiteAttributeUnderWebSite(sourceLineNumbers, element.Name.LocalName)); + } + + parentWeb = this.ParseHelper.GetAttributeIdentifierValue(sourceLineNumbers, attrib); + this.ParseHelper.CreateSimpleReference(section, sourceLineNumbers, IisSymbolDefinitions.IIsWebSite, parentWeb); + break; + default: + this.ParseHelper.UnexpectedAttribute(element, attrib); + break; + } + } + else + { + this.ParseHelper.ParseExtensionAttribute(this.Context.Extensions, intermediate, section, element, attrib); + } + } + + if (null == id) + { + id = this.ParseHelper.CreateIdentifier("iwd", componentId, parentWeb, dirProperties, application); + } + + if (null == path) + { + this.Messaging.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, element.Name.LocalName, "Path")); + } + + if (null == parentWeb) + { + this.Messaging.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, element.Name.LocalName, "WebSite")); + } + + foreach (var child in element.Elements()) + { + if (this.Namespace == child.Name.Namespace) + { + var childSourceLineNumbers = this.ParseHelper.GetSourceLineNumbers(child); + switch (child.Name.LocalName) + { + case "WebApplication": + if (null != application) + { + this.Messaging.Write(IIsErrors.WebApplicationAlreadySpecified(childSourceLineNumbers, element.Name.LocalName)); + } + + application = this.ParseWebApplicationElement(intermediate, section, child); + break; + case "WebDirProperties": + if (null == componentId) + { + this.Messaging.Write(IIsErrors.IllegalElementWithoutComponent(childSourceLineNumbers, child.Name.LocalName)); + } + + string childWebDirProperties = this.ParseWebDirPropertiesElement(intermediate, section, child, componentId); + if (null == dirProperties) + { + dirProperties = childWebDirProperties; + } + else + { + this.Messaging.Write(ErrorMessages.IllegalAttributeWhenNested(sourceLineNumbers, child.Name.LocalName, "DirProperties", child.Name.LocalName)); + } + break; + default: + this.ParseHelper.UnexpectedElement(element, child); + break; + } + } + else + { + this.ParseHelper.ParseExtensionElement(this.Context.Extensions, intermediate, section, element, child); + } + } + + if (null == dirProperties) + { + this.Messaging.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, element.Name.LocalName, "DirProperties")); + } + + if (null != application) + { + this.ParseHelper.CreateSimpleReference(section, sourceLineNumbers, IisSymbolDefinitions.IIsWebApplication, application); + } + + this.ParseHelper.CreateSimpleReference(section, sourceLineNumbers, IisSymbolDefinitions.IIsWebDirProperties, dirProperties); + + // Reference ConfigureIIs since nothing will happen without it + this.AddReferenceToConfigureIIs(section, sourceLineNumbers); + + if (!this.Messaging.EncounteredError) + { + section.AddSymbol(new IIsWebDirSymbol(sourceLineNumbers, id) + { + ComponentRef = componentId, + WebRef = parentWeb, + Path = path, + DirPropertiesRef = dirProperties, + ApplicationRef = application, + }); + } + } + + /// + /// Parses a web directory properties element. + /// + /// Element to parse. + /// The identifier for this WebDirProperties. + private string ParseWebDirPropertiesElement(Intermediate intermediate, IntermediateSection section, XElement element, string componentId) + { + var sourceLineNumbers = this.ParseHelper.GetSourceLineNumbers(element); + Identifier id = null; + int access = 0; + var accessSet = false; + int accessSSLFlags = 0; + var accessSSLFlagsSet = false; + string anonymousUser = null; + var aspDetailedError = YesNoType.NotSet; + string authenticationProviders = null; + int authorization = 0; + var authorizationSet = false; + string cacheControlCustom = null; + var cacheControlMaxAge = CompilerConstants.LongNotSet; + string defaultDocuments = null; + string httpExpires = null; + var iisControlledPassword = false; + var index = YesNoType.NotSet; + var logVisits = YesNoType.NotSet; + var notCustomError = YesNoType.NotSet; + + foreach (var attrib in element.Attributes()) + { + if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || this.Namespace == attrib.Name.Namespace) + { + switch (attrib.Name.LocalName) + { + case "Id": + id = this.ParseHelper.GetAttributeIdentifier(sourceLineNumbers, attrib); + break; + case "AnonymousUser": + anonymousUser = this.ParseHelper.GetAttributeIdentifierValue(sourceLineNumbers, attrib); + this.ParseHelper.CreateSimpleReference(section, sourceLineNumbers, "User", anonymousUser); + break; + case "AspDetailedError": + aspDetailedError = this.ParseHelper.GetAttributeYesNoValue(sourceLineNumbers, attrib); + break; + case "AuthenticationProviders": + authenticationProviders = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib); + break; + case "CacheControlCustom": + cacheControlCustom = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib); + break; + case "CacheControlMaxAge": + cacheControlMaxAge = this.ParseHelper.GetAttributeLongValue(sourceLineNumbers, attrib, 0, uint.MaxValue); // 4294967295 (uint.MaxValue) represents unlimited + break; + case "ClearCustomError": + notCustomError = this.ParseHelper.GetAttributeYesNoValue(sourceLineNumbers, attrib); + break; + case "DefaultDocuments": + defaultDocuments = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib); + break; + case "HttpExpires": + httpExpires = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib); + break; + case "IIsControlledPassword": + iisControlledPassword = YesNoType.Yes == this.ParseHelper.GetAttributeYesNoValue(sourceLineNumbers, attrib); + break; + case "Index": + index = this.ParseHelper.GetAttributeYesNoValue(sourceLineNumbers, attrib); + break; + case "LogVisits": + logVisits = this.ParseHelper.GetAttributeYesNoValue(sourceLineNumbers, attrib); + break; + + // Access attributes + case "Execute": + if (YesNoType.Yes == this.ParseHelper.GetAttributeYesNoValue(sourceLineNumbers, attrib)) + { + access |= 4; + } + else + { + access &= ~4; + } + accessSet = true; + break; + case "Read": + if (YesNoType.Yes == this.ParseHelper.GetAttributeYesNoValue(sourceLineNumbers, attrib)) + { + access |= 1; + } + else + { + access &= ~1; + } + accessSet = true; + break; + case "Script": + if (YesNoType.Yes == this.ParseHelper.GetAttributeYesNoValue(sourceLineNumbers, attrib)) + { + access |= 512; + } + else + { + access &= ~512; + } + accessSet = true; + break; + case "Write": + if (YesNoType.Yes == this.ParseHelper.GetAttributeYesNoValue(sourceLineNumbers, attrib)) + { + access |= 2; + } + else + { + access &= ~2; + } + accessSet = true; + break; + + // AccessSSL Attributes + case "AccessSSL": + if (YesNoType.Yes == this.ParseHelper.GetAttributeYesNoValue(sourceLineNumbers, attrib)) + { + accessSSLFlags |= 8; + } + else + { + accessSSLFlags &= ~8; + } + accessSSLFlagsSet = true; + break; + case "AccessSSL128": + if (YesNoType.Yes == this.ParseHelper.GetAttributeYesNoValue(sourceLineNumbers, attrib)) + { + accessSSLFlags |= 256; + } + else + { + accessSSLFlags &= ~256; + } + accessSSLFlagsSet = true; + break; + case "AccessSSLMapCert": + if (YesNoType.Yes == this.ParseHelper.GetAttributeYesNoValue(sourceLineNumbers, attrib)) + { + accessSSLFlags |= 128; + } + else + { + accessSSLFlags &= ~128; + } + accessSSLFlagsSet = true; + break; + case "AccessSSLNegotiateCert": + if (YesNoType.Yes == this.ParseHelper.GetAttributeYesNoValue(sourceLineNumbers, attrib)) + { + accessSSLFlags |= 32; + } + else + { + accessSSLFlags &= ~32; + } + accessSSLFlagsSet = true; + break; + case "AccessSSLRequireCert": + if (YesNoType.Yes == this.ParseHelper.GetAttributeYesNoValue(sourceLineNumbers, attrib)) + { + accessSSLFlags |= 64; + } + else + { + accessSSLFlags &= ~64; + } + accessSSLFlagsSet = true; + break; + + // Authorization attributes + case "AnonymousAccess": + if (YesNoType.Yes == this.ParseHelper.GetAttributeYesNoValue(sourceLineNumbers, attrib)) + { + authorization |= 1; + } + else + { + authorization &= ~1; + } + authorizationSet = true; + break; + case "BasicAuthentication": + if (YesNoType.Yes == this.ParseHelper.GetAttributeYesNoValue(sourceLineNumbers, attrib)) + { + authorization |= 2; + } + else + { + authorization &= ~2; + } + authorizationSet = true; + break; + case "DigestAuthentication": + if (YesNoType.Yes == this.ParseHelper.GetAttributeYesNoValue(sourceLineNumbers, attrib)) + { + authorization |= 16; + } + else + { + authorization &= ~16; + } + authorizationSet = true; + break; + case "PassportAuthentication": + if (YesNoType.Yes == this.ParseHelper.GetAttributeYesNoValue(sourceLineNumbers, attrib)) + { + authorization |= 64; + } + else + { + authorization &= ~64; + } + authorizationSet = true; + break; + case "WindowsAuthentication": + if (YesNoType.Yes == this.ParseHelper.GetAttributeYesNoValue(sourceLineNumbers, attrib)) + { + authorization |= 4; + } + else + { + authorization &= ~4; + } + authorizationSet = true; + break; + default: + this.ParseHelper.UnexpectedAttribute(element, attrib); + break; + } + } + else + { + this.ParseHelper.ParseExtensionAttribute(this.Context.Extensions, intermediate, section, element, attrib); + } + } + + if (null == id) + { + if (null == componentId) + { + this.Messaging.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, element.Name.LocalName, "Id")); + id = Identifier.Invalid; + } + else + { + id = this.ParseHelper.CreateIdentifier("wdp", componentId); + } + } + + this.ParseHelper.ParseForExtensionElements(this.Context.Extensions, intermediate, section, element); + + if (!this.Messaging.EncounteredError) + { + var symbol = section.AddSymbol(new IIsWebDirPropertiesSymbol(sourceLineNumbers, id) + { + AnonymousUserRef = anonymousUser, + IIsControlledPassword = iisControlledPassword ? 1 : 0, + DefaultDoc = defaultDocuments, + HttpExpires = httpExpires, + CacheControlCustom = cacheControlCustom, + }); + + if (accessSet) + { + symbol.Access = access; + } + + if (authorizationSet) + { + symbol.Authorization = authorization; + } + + if (YesNoType.NotSet != logVisits) + { + symbol.LogVisits = YesNoType.Yes == logVisits ? 1 : 0; + } + + if (YesNoType.NotSet != index) + { + symbol.Index = YesNoType.Yes == index ? 1 : 0; + } + + if (YesNoType.NotSet != aspDetailedError) + { + symbol.AspDetailedError = YesNoType.Yes == aspDetailedError ? 1 : 0; + } + + if (CompilerConstants.LongNotSet != cacheControlMaxAge) + { + symbol.CacheControlMaxAge = unchecked((int)cacheControlMaxAge); + } + + if (YesNoType.NotSet != notCustomError) + { + symbol.NoCustomError = YesNoType.Yes == notCustomError ? 1 : 0; + } + + if (accessSSLFlagsSet) + { + symbol.AccessSSLFlags = accessSSLFlags; + } + + if (null != authenticationProviders) + { + symbol.AuthenticationProviders = authenticationProviders; + } + } + + return id?.Id; + } + + /// + /// Parses a web error element. + /// + /// Element to parse. + /// Type of the parent. + /// Id of the parent. + private void ParseWebErrorElement(Intermediate intermediate, IntermediateSection section, XElement element, WebErrorParentType parentType, string parent) + { + var sourceLineNumbers = this.ParseHelper.GetSourceLineNumbers(element); + var errorCode = CompilerConstants.IntegerNotSet; + string file = null; + string url = null; + var subCode = CompilerConstants.IntegerNotSet; + + foreach (var attrib in element.Attributes()) + { + if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || this.Namespace == attrib.Name.Namespace) + { + switch (attrib.Name.LocalName) + { + case "ErrorCode": + errorCode = this.ParseHelper.GetAttributeIntegerValue(sourceLineNumbers, attrib, 400, 599); + break; + case "File": + file = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib); + break; + case "SubCode": + subCode = this.ParseHelper.GetAttributeIntegerValue(sourceLineNumbers, attrib, 0, int.MaxValue); + break; + case "URL": + url = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib); + break; + default: + this.ParseHelper.UnexpectedAttribute(element, attrib); + break; + } + } + else + { + this.ParseHelper.ParseExtensionAttribute(this.Context.Extensions, intermediate, section, element, attrib); + } + } + + if (CompilerConstants.IntegerNotSet == errorCode) + { + this.Messaging.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, element.Name.LocalName, "ErrorCode")); + errorCode = CompilerConstants.IllegalInteger; + } + + if (CompilerConstants.IntegerNotSet == subCode) + { + this.Messaging.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, element.Name.LocalName, "SubCode")); + subCode = CompilerConstants.IllegalInteger; + } + + if (String.IsNullOrEmpty(file) && String.IsNullOrEmpty(url)) + { + this.Messaging.Write(ErrorMessages.IllegalAttributeWithOtherAttribute(sourceLineNumbers, element.Name.LocalName, "File", "URL")); + } + + this.ParseHelper.ParseForExtensionElements(this.Context.Extensions, intermediate, section, element); + + // Reference ConfigureIIs since nothing will happen without it + this.AddReferenceToConfigureIIs(section, sourceLineNumbers); + + if (!this.Messaging.EncounteredError) + { + section.AddSymbol(new IIsWebErrorSymbol(sourceLineNumbers) + { + ErrorCode = errorCode, + SubCode = subCode, + ParentType = (int)parentType, + ParentValue = parent, + File = file, + URL = url, + }); + } + } + + /// + /// Parses a web filter element. + /// + /// Element to parse. + /// Identifier of parent component. + /// Optional identifier of parent web site. + private void ParseWebFilterElement(Intermediate intermediate, IntermediateSection section, XElement element, string componentId, string parentWeb) + { + var sourceLineNumbers = this.ParseHelper.GetSourceLineNumbers(element); + Identifier id = null; + string description = null; + int flags = 0; + var loadOrder = CompilerConstants.IntegerNotSet; + string name = null; + string path = null; + + foreach (var attrib in element.Attributes()) + { + if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || this.Namespace == attrib.Name.Namespace) + { + switch (attrib.Name.LocalName) + { + case "Id": + id = this.ParseHelper.GetAttributeIdentifier(sourceLineNumbers, attrib); + break; + case "Description": + description = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib); + break; + case "Flags": + flags = this.ParseHelper.GetAttributeIntegerValue(sourceLineNumbers, attrib, 0, int.MaxValue); + break; + case "LoadOrder": + string loadOrderValue = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib); + if (0 < loadOrderValue.Length) + { + switch (loadOrderValue) + { + case "first": + loadOrder = 0; + break; + case "last": + loadOrder = -1; + break; + default: + loadOrder = this.ParseHelper.GetAttributeIntegerValue(sourceLineNumbers, attrib, 1, short.MaxValue); + break; + } + } + break; + case "Name": + name = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib); + break; + case "Path": + path = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib); + break; + case "WebSite": + if (null != parentWeb) + { + this.Messaging.Write(IIsErrors.WebSiteAttributeUnderWebSite(sourceLineNumbers, element.Name.LocalName)); + } + + parentWeb = this.ParseHelper.GetAttributeIdentifierValue(sourceLineNumbers, attrib); + this.ParseHelper.CreateSimpleReference(section, sourceLineNumbers, IisSymbolDefinitions.IIsWebSite, parentWeb); + break; + default: + this.ParseHelper.UnexpectedAttribute(element, attrib); + break; + } + } + else + { + this.ParseHelper.ParseExtensionAttribute(this.Context.Extensions, intermediate, section, element, attrib); + } + } + + if (null == id) + { + id = this.ParseHelper.CreateIdentifier("ifl", name, componentId, path, parentWeb); + } + + if (null == name) + { + this.Messaging.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, element.Name.LocalName, "Name")); + } + + if (null == path) + { + this.Messaging.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, element.Name.LocalName, "Path")); + } + + this.ParseHelper.ParseForExtensionElements(this.Context.Extensions, intermediate, section, element); + + // Reference ConfigureIIs since nothing will happen without it + this.AddReferenceToConfigureIIs(section, sourceLineNumbers); + + if (!this.Messaging.EncounteredError) + { + var symbol = section.AddSymbol(new IIsFilterSymbol(sourceLineNumbers, id) + { + Name = name, + ComponentRef = componentId, + Path = path, + WebRef = parentWeb, + Description = description, + Flags = flags, + }); + + if (CompilerConstants.IntegerNotSet != loadOrder) + { + symbol.LoadOrder = loadOrder; + } + } + } + + /// + /// Parses web log element. + /// + /// Node to be parsed. + private void ParseWebLogElement(Intermediate intermediate, IntermediateSection section, XElement element) + { + var sourceLineNumbers = this.ParseHelper.GetSourceLineNumbers(element); + Identifier id = null; + string type = null; + + foreach (var attrib in element.Attributes()) + { + if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || this.Namespace == attrib.Name.Namespace) + { + switch (attrib.Name.LocalName) + { + case "Id": + id = this.ParseHelper.GetAttributeIdentifier(sourceLineNumbers, attrib); + break; + case "Type": + var typeValue = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib); + if (0 < typeValue.Length) + { + switch (typeValue) + { + case "IIS": + type = "Microsoft IIS Log File Format"; + break; + case "NCSA": + type = "NCSA Common Log File Format"; + break; + case "none": + type = "none"; + break; + case "ODBC": + type = "ODBC Logging"; + break; + case "W3C": + type = "W3C Extended Log File Format"; + break; + default: + this.Messaging.Write(ErrorMessages.IllegalAttributeValue(sourceLineNumbers, element.Name.LocalName, "Type", typeValue, "IIS", "NCSA", "none", "ODBC", "W3C")); + break; + } + } + break; + default: + this.ParseHelper.UnexpectedAttribute(element, attrib); + break; + } + } + else + { + this.ParseHelper.ParseExtensionAttribute(this.Context.Extensions, intermediate, section, element, attrib); + } + } + + if (null == id) + { + this.Messaging.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, element.Name.LocalName, "Id")); + } + + if (null == type) + { + this.Messaging.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, element.Name.LocalName, "Type")); + } + + this.ParseHelper.ParseForExtensionElements(this.Context.Extensions, intermediate, section, element); + + if (!this.Messaging.EncounteredError) + { + section.AddSymbol(new IIsWebLogSymbol(sourceLineNumbers, id) + { + Format = type, + }); + } + } + + /// + /// Parses a web property element. + /// + /// Element to parse. + /// Identifier for parent component. + private void ParseWebPropertyElement(Intermediate intermediate, IntermediateSection section, XElement element, string componentId) + { + var sourceLineNumbers = this.ParseHelper.GetSourceLineNumbers(element); + Identifier id = null; + string value = null; + + foreach (var attrib in element.Attributes()) + { + if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || this.Namespace == attrib.Name.Namespace) + { + switch (attrib.Name.LocalName) + { + case "Id": + id = this.ParseHelper.GetAttributeIdentifier(sourceLineNumbers, attrib); + break; + case "Value": + value = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib); + break; + default: + this.ParseHelper.UnexpectedAttribute(element, attrib); + break; + } + } + else + { + this.ParseHelper.ParseExtensionAttribute(this.Context.Extensions, intermediate, section, element, attrib); + } + } + + switch (id?.Id) + { + case "ETagChangeNumber": + case "MaxGlobalBandwidth": + // Must specify a value for these + if (null == value) + { + this.Messaging.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, element.Name.LocalName, "Value", "Id", id.Id)); + } + break; + case "IIs5IsolationMode": + case "LogInUTF8": + // Can't specify a value for these + if (null != value) + { + this.Messaging.Write(ErrorMessages.IllegalAttributeWithOtherAttribute(sourceLineNumbers, element.Name.LocalName, "Value", "Id", id.Id)); + } + break; + default: + this.Messaging.Write(ErrorMessages.IllegalAttributeValue(sourceLineNumbers, element.Name.LocalName, "Id", id?.Id, "ETagChangeNumber", "IIs5IsolationMode", "LogInUTF8", "MaxGlobalBandwidth")); + break; + } + + this.ParseHelper.ParseForExtensionElements(this.Context.Extensions, intermediate, section, element); + + // Reference ConfigureIIs since nothing will happen without it + this.AddReferenceToConfigureIIs(section, sourceLineNumbers); + + if (!this.Messaging.EncounteredError) + { + section.AddSymbol(new IIsPropertySymbol(sourceLineNumbers, id) + { + ComponentRef = componentId, + Attributes = 0, + Value = value, + }); + } + } + + /// + /// Parses a web service extension element. + /// + /// Element to parse. + /// Identifier for parent component. + private void ParseWebServiceExtensionElement(Intermediate intermediate, IntermediateSection section, XElement element, string componentId) + { + var sourceLineNumbers = this.ParseHelper.GetSourceLineNumbers(element); + Identifier id = null; + int attributes = 0; + string description = null; + string file = null; + string group = null; + + foreach (XAttribute attrib in element.Attributes()) + { + if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || this.Namespace == attrib.Name.Namespace) + { + switch (attrib.Name.LocalName) + { + case "Id": + id = this.ParseHelper.GetAttributeIdentifier(sourceLineNumbers, attrib); + break; + case "Allow": + if (YesNoType.Yes == this.ParseHelper.GetAttributeYesNoValue(sourceLineNumbers, attrib)) + { + attributes |= 1; + } + else + { + attributes &= ~1; + } + break; + case "Description": + description = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib); + break; + case "File": + file = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib); + break; + case "Group": + group = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib); + break; + case "UIDeletable": + if (YesNoType.Yes == this.ParseHelper.GetAttributeYesNoValue(sourceLineNumbers, attrib)) + { + attributes |= 2; + } + else + { + attributes &= ~2; + } + break; + default: + this.ParseHelper.UnexpectedAttribute(element, attrib); + break; + } + } + else + { + this.ParseHelper.ParseExtensionAttribute(this.Context.Extensions, intermediate, section, element, attrib); + } + } + + if (null == id) + { + id = this.ParseHelper.CreateIdentifier("iwe", componentId, file); + } + + if (null == file) + { + this.Messaging.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, element.Name.LocalName, "File")); + } + + this.ParseHelper.ParseForExtensionElements(this.Context.Extensions, intermediate, section, element); + + // Reference ConfigureIIs since nothing will happen without it + this.AddReferenceToConfigureIIs(section, sourceLineNumbers); + + if (!this.Messaging.EncounteredError) + { + section.AddSymbol(new IIsWebServiceExtensionSymbol(sourceLineNumbers, id) + { + ComponentRef = componentId, + File = file, + Description = description, + Group = group, + Attributes = attributes, + }); + } + } + + /// + /// Parses a web site element. + /// + /// Element to parse. + /// Optional identifier of parent component. + private void ParseWebSiteElement(Intermediate intermediate, IntermediateSection section, XElement element, string componentId) + { + var sourceLineNumbers = this.ParseHelper.GetSourceLineNumbers(element); + Identifier id = null; + string application = null; + int attributes = 0; + var connectionTimeout = CompilerConstants.IntegerNotSet; + string description = null; + string directory = null; + string dirProperties = null; + string keyAddress = null; + string log = null; + string siteId = null; + var sequence = CompilerConstants.IntegerNotSet; + var state = CompilerConstants.IntegerNotSet; + + foreach (var attrib in element.Attributes()) + { + if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || this.Namespace == attrib.Name.Namespace) + { + switch (attrib.Name.LocalName) + { + case "Id": + id = this.ParseHelper.GetAttributeIdentifier(sourceLineNumbers, attrib); + break; + case "AutoStart": + if (null == componentId) + { + this.Messaging.Write(IIsErrors.IllegalAttributeWithoutComponent(sourceLineNumbers, element.Name.LocalName, attrib.Name.LocalName)); + } + + if (YesNoType.Yes == this.ParseHelper.GetAttributeYesNoValue(sourceLineNumbers, attrib)) + { + state = 2; + } + else if (state != 1) + { + state = 0; + } + break; + case "ConfigureIfExists": + if (null == componentId) + { + this.Messaging.Write(IIsErrors.IllegalAttributeWithoutComponent(sourceLineNumbers, element.Name.LocalName, attrib.Name.LocalName)); + } + + if (YesNoType.Yes == this.ParseHelper.GetAttributeYesNoValue(sourceLineNumbers, attrib)) + { + attributes &= ~2; + } + else + { + attributes |= 2; + } + break; + case "ConnectionTimeout": + connectionTimeout = this.ParseHelper.GetAttributeIntegerValue(sourceLineNumbers, attrib, 0, short.MaxValue); + break; + case "Description": + description = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib); + break; + case "Directory": + directory = this.ParseHelper.GetAttributeIdentifierValue(sourceLineNumbers, attrib); + this.ParseHelper.CreateSimpleReference(section, sourceLineNumbers, SymbolDefinitions.Directory, directory); + break; + case "DirProperties": + if (null == componentId) + { + this.Messaging.Write(IIsErrors.IllegalAttributeWithoutComponent(sourceLineNumbers, element.Name.LocalName, attrib.Name.LocalName)); + } + + dirProperties = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib); + break; + case "SiteId": + siteId = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib); + if ("*" == siteId) + { + siteId = "-1"; + } + break; + case "Sequence": + sequence = this.ParseHelper.GetAttributeIntegerValue(sourceLineNumbers, attrib, 1, short.MaxValue); + break; + case "StartOnInstall": + if (null == componentId) + { + this.Messaging.Write(IIsErrors.IllegalAttributeWithoutComponent(sourceLineNumbers, element.Name.LocalName, attrib.Name.LocalName)); + } + + // when state is set to 2 it implies 1, so don't set it to 1 + if (2 != state && YesNoType.Yes == this.ParseHelper.GetAttributeYesNoValue(sourceLineNumbers, attrib)) + { + state = 1; + } + else if (2 != state) + { + state = 0; + } + break; + case "WebApplication": + if (null == componentId) + { + this.Messaging.Write(IIsErrors.IllegalAttributeWithoutComponent(sourceLineNumbers, element.Name.LocalName, attrib.Name.LocalName)); + } + + application = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib); + break; + case "WebLog": + if (null == componentId) + { + this.Messaging.Write(IIsErrors.IllegalAttributeWithoutComponent(sourceLineNumbers, element.Name.LocalName, attrib.Name.LocalName)); + } + + log = this.ParseHelper.GetAttributeIdentifierValue(sourceLineNumbers, attrib); + this.ParseHelper.CreateSimpleReference(section, sourceLineNumbers, IisSymbolDefinitions.IIsWebLog, log); + break; + default: + this.ParseHelper.UnexpectedAttribute(element, attrib); + break; + } + } + else + { + this.ParseHelper.ParseExtensionAttribute(this.Context.Extensions, intermediate, section, element, attrib); + } + } + + if (null == id) + { + id = this.ParseHelper.CreateIdentifier("iws", description, componentId, siteId, application); + } + + if (null == description) + { + this.Messaging.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, element.Name.LocalName, "Description")); + } + + if (null == directory && null != componentId) + { + this.Messaging.Write(IIsErrors.RequiredAttributeUnderComponent(sourceLineNumbers, element.Name.LocalName, "Directory")); + } + + foreach (var child in element.Elements()) + { + if (this.Namespace == child.Name.Namespace) + { + var childSourceLineNumbers = this.ParseHelper.GetSourceLineNumbers(child); + switch (child.Name.LocalName) + { + case "CertificateRef": + if (null == componentId) + { + this.Messaging.Write(IIsErrors.IllegalElementWithoutComponent(childSourceLineNumbers, child.Name.LocalName)); + } + + this.ParseCertificateRefElement(intermediate, section, child, id?.Id); + break; + case "HttpHeader": + if (null == componentId) + { + this.Messaging.Write(IIsErrors.IllegalElementWithoutComponent(childSourceLineNumbers, child.Name.LocalName)); + } + + this.ParseHttpHeaderElement(intermediate, section, child, HttpHeaderParentType.WebSite, id?.Id); + break; + case "WebAddress": + string address = this.ParseWebAddressElement(intermediate, section, child, id?.Id); + if (null == keyAddress) + { + keyAddress = address; + } + break; + case "WebApplication": + if (null == componentId) + { + this.Messaging.Write(IIsErrors.IllegalElementWithoutComponent(childSourceLineNumbers, child.Name.LocalName)); + } + + if (null != application) + { + this.Messaging.Write(IIsErrors.WebApplicationAlreadySpecified(childSourceLineNumbers, element.Name.LocalName)); + } + + application = this.ParseWebApplicationElement(intermediate, section, child); + break; + case "WebDir": + if (null == componentId) + { + this.Messaging.Write(IIsErrors.IllegalElementWithoutComponent(childSourceLineNumbers, child.Name.LocalName)); + } + + this.ParseWebDirElement(intermediate, section, child, componentId, id?.Id); + break; + case "WebDirProperties": + if (null == componentId) + { + this.Messaging.Write(IIsErrors.IllegalElementWithoutComponent(childSourceLineNumbers, child.Name.LocalName)); + } + + string childWebDirProperties = this.ParseWebDirPropertiesElement(intermediate, section, child, componentId); + if (null == dirProperties) + { + dirProperties = childWebDirProperties; + } + else + { + this.Messaging.Write(ErrorMessages.IllegalParentAttributeWhenNested(sourceLineNumbers, "WebSite", "DirProperties", child.Name.LocalName)); + } + break; + case "WebError": + if (null == componentId) + { + this.Messaging.Write(IIsErrors.IllegalElementWithoutComponent(childSourceLineNumbers, child.Name.LocalName)); + } + + this.ParseWebErrorElement(intermediate, section, child, WebErrorParentType.WebSite, id?.Id); + break; + case "WebFilter": + if (null == componentId) + { + this.Messaging.Write(IIsErrors.IllegalElementWithoutComponent(childSourceLineNumbers, child.Name.LocalName)); + } + + this.ParseWebFilterElement(intermediate, section, child, componentId, id?.Id); + break; + case "WebVirtualDir": + if (null == componentId) + { + this.Messaging.Write(IIsErrors.IllegalElementWithoutComponent(childSourceLineNumbers, child.Name.LocalName)); + } + + this.ParseWebVirtualDirElement(intermediate, section, child, componentId, id?.Id, null); + break; + case "MimeMap": + this.ParseMimeMapElement(intermediate, section, child, id?.Id, MimeMapParentType.WebSite); + break; + default: + this.ParseHelper.UnexpectedElement(element, child); + break; + } + } + else + { + this.ParseHelper.ParseExtensionElement(this.Context.Extensions, intermediate, section, element, child); + } + } + + + if (null == keyAddress) + { + this.Messaging.Write(ErrorMessages.ExpectedElement(sourceLineNumbers, element.Name.LocalName, "WebAddress")); + } + + if (null != application) + { + this.ParseHelper.CreateSimpleReference(section, sourceLineNumbers, IisSymbolDefinitions.IIsWebApplication, application); + } + + if (null != dirProperties) + { + this.ParseHelper.CreateSimpleReference(section, sourceLineNumbers, IisSymbolDefinitions.IIsWebDirProperties, dirProperties); + } + + if (null != componentId) + { + // Reference ConfigureIIs since nothing will happen without it + this.AddReferenceToConfigureIIs(section, sourceLineNumbers); + } + + if (!this.Messaging.EncounteredError) + { + var symbol = section.AddSymbol(new IIsWebSiteSymbol(sourceLineNumbers, id) + { + ComponentRef = componentId, + Description = description, + DirectoryRef = directory, + KeyAddressRef = keyAddress, + DirPropertiesRef = dirProperties, + ApplicationRef = application, + LogRef = log, + WebsiteId = siteId, + }); + + if (CompilerConstants.IntegerNotSet != connectionTimeout) + { + symbol.ConnectionTimeout = connectionTimeout; + } + + if (CompilerConstants.IntegerNotSet != state) + { + symbol.State = state; + } + + if (0 != attributes) + { + symbol.Attributes = attributes; + } + + if (CompilerConstants.IntegerNotSet != sequence) + { + symbol.Sequence = sequence; + } + } + } + + /// + /// Parses a HTTP Header element. + /// + /// Element to parse. + /// Type of the parent. + /// Id of the parent. + private void ParseHttpHeaderElement(Intermediate intermediate, IntermediateSection section, XElement element, HttpHeaderParentType parentType, string parent) + { + var sourceLineNumbers = this.ParseHelper.GetSourceLineNumbers(element); + Identifier id = null; + string headerName = null; + string headerValue = null; + + foreach (var attrib in element.Attributes()) + { + if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || this.Namespace == attrib.Name.Namespace) + { + switch (attrib.Name.LocalName) + { + case "Id": + id = this.ParseHelper.GetAttributeIdentifier(sourceLineNumbers, attrib); + break; + case "Name": + headerName = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib); + break; + case "Value": + headerValue = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib); + break; + default: + this.ParseHelper.UnexpectedAttribute(element, attrib); + break; + } + } + else + { + this.ParseHelper.ParseExtensionAttribute(this.Context.Extensions, intermediate, section, element, attrib); + } + } + + if (null == headerName) + { + this.Messaging.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, element.Name.LocalName, "Name")); + } + else if (null == id) + { + id = this.ParseHelper.CreateIdentifierFromFilename(headerName); + } + + this.ParseHelper.ParseForExtensionElements(this.Context.Extensions, intermediate, section, element); + + // Reference ConfigureIIs since nothing will happen without it + this.AddReferenceToConfigureIIs(section, sourceLineNumbers); + + if (!this.Messaging.EncounteredError) + { + section.AddSymbol(new IIsHttpHeaderSymbol(sourceLineNumbers, id) + { + HttpHeader = id.Id, + ParentType = (int)parentType, + ParentValue = parent, + Name = headerName, + Value = headerValue, + Attributes = 0, + }); + } + } + + /// + /// Parses a virtual directory element. + /// + /// Element to parse. + /// Identifier of parent component. + /// Identifier of parent web site. + /// Alias of the parent web site. + private void ParseWebVirtualDirElement(Intermediate intermediate, IntermediateSection section, XElement element, string componentId, string parentWeb, string parentAlias) + { + var sourceLineNumbers = this.ParseHelper.GetSourceLineNumbers(element); + Identifier id = null; + string alias = null; + string application = null; + string directory = null; + string dirProperties = null; + + foreach (var attrib in element.Attributes()) + { + if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || this.Namespace == attrib.Name.Namespace) + { + switch (attrib.Name.LocalName) + { + case "Id": + id = this.ParseHelper.GetAttributeIdentifier(sourceLineNumbers, attrib); + break; + case "Alias": + alias = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib); + break; + case "Directory": + directory = this.ParseHelper.GetAttributeIdentifierValue(sourceLineNumbers, attrib); + this.ParseHelper.CreateSimpleReference(section, sourceLineNumbers, SymbolDefinitions.Directory, directory); + break; + case "DirProperties": + dirProperties = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib); + break; + case "WebApplication": + application = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib); + break; + case "WebSite": + if (null != parentWeb) + { + this.Messaging.Write(IIsErrors.WebSiteAttributeUnderWebSite(sourceLineNumbers, element.Name.LocalName)); + } + + parentWeb = this.ParseHelper.GetAttributeIdentifierValue(sourceLineNumbers, attrib); + this.ParseHelper.CreateSimpleReference(section, sourceLineNumbers, IisSymbolDefinitions.IIsWebSite, parentWeb); + break; + default: + this.ParseHelper.UnexpectedAttribute(element, attrib); + break; + } + } + else + { + this.ParseHelper.ParseExtensionAttribute(this.Context.Extensions, intermediate, section, element, attrib); + } + } + + if (null == id) + { + id = this.ParseHelper.CreateIdentifier("wvd", alias, directory, dirProperties, application, parentWeb); + } + + if (null == alias) + { + this.Messaging.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, element.Name.LocalName, "Alias")); + } + else if (-1 != alias.IndexOf("\\", StringComparison.Ordinal)) + { + this.Messaging.Write(IIsErrors.IllegalCharacterInAttributeValue(sourceLineNumbers, element.Name.LocalName, "Alias", alias, '\\')); + } + + if (null == directory) + { + this.Messaging.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, element.Name.LocalName, "Directory")); + } + + if (null == parentWeb) + { + this.Messaging.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, element.Name.LocalName, "WebSite")); + } + + if (null == componentId) + { + this.Messaging.Write(IIsErrors.IllegalElementWithoutComponent(sourceLineNumbers, element.Name.LocalName)); + } + + if (null != parentAlias) + { + alias = String.Concat(parentAlias, "/", alias); + } + + foreach (var child in element.Elements()) + { + if (this.Namespace == child.Name.Namespace) + { + var childSourceLineNumbers = this.ParseHelper.GetSourceLineNumbers(child); + switch (child.Name.LocalName) + { + case "WebApplication": + if (null != application) + { + this.Messaging.Write(IIsErrors.WebApplicationAlreadySpecified(childSourceLineNumbers, element.Name.LocalName)); + } + + application = this.ParseWebApplicationElement(intermediate, section, child); + break; + case "WebDirProperties": + if (null == componentId) + { + this.Messaging.Write(IIsErrors.IllegalElementWithoutComponent(childSourceLineNumbers, child.Name.LocalName)); + } + + string childWebDirProperties = this.ParseWebDirPropertiesElement(intermediate, section, child, componentId); + if (null == dirProperties) + { + dirProperties = childWebDirProperties; + } + else + { + this.Messaging.Write(ErrorMessages.IllegalAttributeWhenNested(sourceLineNumbers, child.Name.LocalName, "DirProperties", child.Name.LocalName)); + } + break; + + case "WebError": + this.ParseWebErrorElement(intermediate, section, child, WebErrorParentType.WebVirtualDir, id?.Id); + break; + case "WebVirtualDir": + this.ParseWebVirtualDirElement(intermediate, section, child, componentId, parentWeb, alias); + break; + case "HttpHeader": + this.ParseHttpHeaderElement(intermediate, section, child, HttpHeaderParentType.WebVirtualDir, id?.Id); + break; + case "MimeMap": + this.ParseMimeMapElement(intermediate, section, child, id?.Id, MimeMapParentType.WebVirtualDir); + break; + default: + this.ParseHelper.UnexpectedElement(element, child); + break; + } + } + else + { + this.ParseHelper.ParseExtensionElement(this.Context.Extensions, intermediate, section, element, child); + } + } + + if (null != dirProperties) + { + this.ParseHelper.CreateSimpleReference(section, sourceLineNumbers, IisSymbolDefinitions.IIsWebDirProperties, dirProperties); + } + + if (null != application) + { + this.ParseHelper.CreateSimpleReference(section, sourceLineNumbers, IisSymbolDefinitions.IIsWebApplication, application); + } + + // Reference ConfigureIIs since nothing will happen without it + this.AddReferenceToConfigureIIs(section, sourceLineNumbers); + + if (!this.Messaging.EncounteredError) + { + section.AddSymbol(new IIsWebVirtualDirSymbol(sourceLineNumbers, id) + { + ComponentRef = componentId, + WebRef = parentWeb, + Alias = alias, + DirectoryRef = directory, + DirPropertiesRef = dirProperties, + ApplicationRef = application, + }); + } + } + + private void AddReferenceToConfigureIIs(IntermediateSection section, SourceLineNumber sourceLineNumbers) + { + this.ParseHelper.CreateCustomActionReference(sourceLineNumbers, section, "Wix4ConfigureIIs", this.Context.Platform, CustomActionPlatforms.X86 | CustomActionPlatforms.X64 | CustomActionPlatforms.ARM64); + } + } +} diff --git a/src/ext/Iis/wixext/IIsDecompiler.cs b/src/ext/Iis/wixext/IIsDecompiler.cs new file mode 100644 index 00000000..17e15348 --- /dev/null +++ b/src/ext/Iis/wixext/IIsDecompiler.cs @@ -0,0 +1,1549 @@ +// 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. + +namespace WixToolset.Iis +{ +#if TODO_CONSIDER_DECOMPILER + using System; + using System.Collections; + using System.Globalization; + using WixToolset.Data; + using WixToolset.Extensibility; + using IIs = WixToolset.Extensions.Serialize.IIs; + using Wix = WixToolset.Data.Serialize; + + /// + /// The decompiler for the WiX Toolset Internet Information Services Extension. + /// + public sealed class IIsDecompiler : DecompilerExtension + { + /// + /// Creates a decompiler for IIs Extension. + /// + public IIsDecompiler() + { + this.TableDefinitions = IIsExtensionData.GetExtensionTableDefinitions(); + } + + /// + /// Get the extensions library to be removed. + /// + /// Table definitions for library. + /// Library to remove from decompiled output. + public override Library GetLibraryToRemove(TableDefinitionCollection tableDefinitions) + { + return IIsExtensionData.GetExtensionLibrary(tableDefinitions); + } + + /// + /// Decompiles an extension table. + /// + /// The table to decompile. + public override void DecompileTable(Table table) + { + switch (table.Name) + { + case "Certificate": + this.DecompileCertificateTable(table); + break; + case "CertificateHash": + // There is nothing to do for this table, it contains no authored data + // to be decompiled. + break; + case "IIsAppPool": + this.DecompileIIsAppPoolTable(table); + break; + case "IIsFilter": + this.DecompileIIsFilterTable(table); + break; + case "IIsProperty": + this.DecompileIIsPropertyTable(table); + break; + case "IIsHttpHeader": + this.DecompileIIsHttpHeaderTable(table); + break; + case "IIsMimeMap": + this.DecompileIIsMimeMapTable(table); + break; + case "IIsWebAddress": + this.DecompileIIsWebAddressTable(table); + break; + case "IIsWebApplication": + this.DecompileIIsWebApplicationTable(table); + break; + case "IIsWebDirProperties": + this.DecompileIIsWebDirPropertiesTable(table); + break; + case "IIsWebError": + this.DecompileIIsWebErrorTable(table); + break; + case "IIsWebLog": + this.DecompileIIsWebLogTable(table); + break; + case "IIsWebServiceExtension": + this.DecompileIIsWebServiceExtensionTable(table); + break; + case "IIsWebSite": + this.DecompileIIsWebSiteTable(table); + break; + case "IIsWebVirtualDir": + this.DecompileIIsWebVirtualDirTable(table); + break; + case "IIsWebSiteCertificates": + this.DecompileIIsWebSiteCertificatesTable(table); + break; + default: + base.DecompileTable(table); + break; + } + } + + /// + /// Finalize decompilation. + /// + /// The collection of all tables. + public override void Finish(TableIndexedCollection tables) + { + this.FinalizeIIsMimeMapTable(tables); + this.FinalizeIIsHttpHeaderTable(tables); + this.FinalizeIIsWebApplicationTable(tables); + this.FinalizeIIsWebErrorTable(tables); + this.FinalizeIIsWebVirtualDirTable(tables); + this.FinalizeIIsWebSiteCertificatesTable(tables); + this.FinalizeWebAddressTable(tables); + } + + /// + /// Decompile the Certificate table. + /// + /// The table to decompile. + private void DecompileCertificateTable(Table table) + { + foreach (Row row in table.Rows) + { + IIs.Certificate certificate = new IIs.Certificate(); + + certificate.Id = (string)row[0]; + certificate.Name = (string)row[2]; + + switch ((int)row[3]) + { + case 1: + certificate.StoreLocation = IIs.Certificate.StoreLocationType.currentUser; + break; + case 2: + certificate.StoreLocation = IIs.Certificate.StoreLocationType.localMachine; + break; + default: + // TODO: warn + break; + } + + switch ((string)row[4]) + { + case "CA": + certificate.StoreName = IIs.Certificate.StoreNameType.ca; + break; + case "MY": + certificate.StoreName = IIs.Certificate.StoreNameType.my; + break; + case "REQUEST": + certificate.StoreName = IIs.Certificate.StoreNameType.request; + break; + case "Root": + certificate.StoreName = IIs.Certificate.StoreNameType.root; + break; + case "AddressBook": + certificate.StoreName = IIs.Certificate.StoreNameType.otherPeople; + break; + case "TrustedPeople": + certificate.StoreName = IIs.Certificate.StoreNameType.trustedPeople; + break; + case "TrustedPublisher": + certificate.StoreName = IIs.Certificate.StoreNameType.trustedPublisher; + break; + default: + // TODO: warn + break; + } + + int attribute = (int)row[5]; + + if (0x1 == (attribute & 0x1)) + { + certificate.Request = IIs.YesNoType.yes; + } + + if (0x2 == (attribute & 0x2)) + { + if (null != row[6]) + { + certificate.BinaryKey = (string)row[6]; + } + else + { + // TODO: warn about expected value in row 5 + } + } + else if (null != row[7]) + { + certificate.CertificatePath = (string)row[7]; + } + + if (0x4 == (attribute & 0x4)) + { + certificate.Overwrite = IIs.YesNoType.yes; + } + + if (null != row[8]) + { + certificate.PFXPassword = (string)row[8]; + } + + Wix.Component component = (Wix.Component)this.Core.GetIndexedElement("Component", (string)row[1]); + if (null != component) + { + component.AddChild(certificate); + } + else + { + this.Core.OnMessage(WixWarnings.ExpectedForeignRow(row.SourceLineNumbers, table.Name, row.GetPrimaryKey(DecompilerConstants.PrimaryKeyDelimiter), "Component_", (string)row[1], "Component")); + } + } + } + + /// + /// Decompile the IIsAppPool table. + /// + /// The table to decompile. + private void DecompileIIsAppPoolTable(Table table) + { + foreach (Row row in table.Rows) + { + IIs.WebAppPool webAppPool = new IIs.WebAppPool(); + + webAppPool.Id = (string)row[0]; + + webAppPool.Name = (string)row[1]; + + switch ((int)row[3] & 0x1F) + { + case 1: + webAppPool.Identity = IIs.WebAppPool.IdentityType.networkService; + break; + case 2: + webAppPool.Identity = IIs.WebAppPool.IdentityType.localService; + break; + case 4: + webAppPool.Identity = IIs.WebAppPool.IdentityType.localSystem; + break; + case 8: + webAppPool.Identity = IIs.WebAppPool.IdentityType.other; + break; + case 0x10: + webAppPool.Identity = IIs.WebAppPool.IdentityType.applicationPoolIdentity; + break; + default: + // TODO: warn + break; + } + + if (null != row[4]) + { + webAppPool.User = (string)row[4]; + } + + if (null != row[5]) + { + webAppPool.RecycleMinutes = (int)row[5]; + } + + if (null != row[6]) + { + webAppPool.RecycleRequests = (int)row[6]; + } + + if (null != row[7]) + { + string[] recycleTimeValues = ((string)row[7]).Split(','); + + foreach (string recycleTimeValue in recycleTimeValues) + { + IIs.RecycleTime recycleTime = new IIs.RecycleTime(); + + recycleTime.Value = recycleTimeValue; + + webAppPool.AddChild(recycleTime); + } + } + + if (null != row[8]) + { + webAppPool.IdleTimeout = (int)row[8]; + } + + if (null != row[9]) + { + webAppPool.QueueLimit = (int)row[9]; + } + + if (null != row[10]) + { + string[] cpuMon = ((string)row[10]).Split(','); + + if (0 < cpuMon.Length && "0" != cpuMon[0]) + { + webAppPool.MaxCpuUsage = Convert.ToInt32(cpuMon[0], CultureInfo.InvariantCulture); + } + + if (1 < cpuMon.Length) + { + webAppPool.RefreshCpu = Convert.ToInt32(cpuMon[1], CultureInfo.InvariantCulture); + } + + if (2 < cpuMon.Length) + { + switch (Convert.ToInt32(cpuMon[2], CultureInfo.InvariantCulture)) + { + case 0: + webAppPool.CpuAction = IIs.WebAppPool.CpuActionType.none; + break; + case 1: + webAppPool.CpuAction = IIs.WebAppPool.CpuActionType.shutdown; + break; + default: + // TODO: warn + break; + } + } + + if (3 < cpuMon.Length) + { + // TODO: warn + } + } + + if (null != row[11]) + { + webAppPool.MaxWorkerProcesses = (int)row[11]; + } + + if (null != row[12]) + { + webAppPool.VirtualMemory = (int)row[12]; + } + + if (null != row[13]) + { + webAppPool.PrivateMemory = (int)row[13]; + } + + if (null != row[14]) + { + webAppPool.ManagedRuntimeVersion = (string)row[14]; + } + + if (null != row[15]) + { + webAppPool.ManagedPipelineMode = (string)row[15]; + } + + if (null != row[2]) + { + Wix.Component component = (Wix.Component)this.Core.GetIndexedElement("Component", (string)row[2]); + + if (null != component) + { + component.AddChild(webAppPool); + } + else + { + this.Core.OnMessage(WixWarnings.ExpectedForeignRow(row.SourceLineNumbers, table.Name, row.GetPrimaryKey(DecompilerConstants.PrimaryKeyDelimiter), "Component_", (string)row[2], "Component")); + } + } + else + { + this.Core.RootElement.AddChild(webAppPool); + } + } + } + + /// + /// Decompile the IIsProperty table. + /// + /// The table to decompile. + private void DecompileIIsPropertyTable(Table table) + { + foreach (Row row in table.Rows) + { + IIs.WebProperty webProperty = new IIs.WebProperty(); + + switch ((string)row[0]) + { + case "ETagChangeNumber": + webProperty.Id = IIs.WebProperty.IdType.ETagChangeNumber; + break; + case "IIs5IsolationMode": + webProperty.Id = IIs.WebProperty.IdType.IIs5IsolationMode; + break; + case "LogInUTF8": + webProperty.Id = IIs.WebProperty.IdType.LogInUTF8; + break; + case "MaxGlobalBandwidth": + webProperty.Id = IIs.WebProperty.IdType.MaxGlobalBandwidth; + break; + } + + if (0 != (int)row[2]) + { + // TODO: warn about value in unused column + } + + if (null != row[3]) + { + webProperty.Value = (string)row[3]; + } + + Wix.Component component = (Wix.Component)this.Core.GetIndexedElement("Component", (string)row[1]); + if (null != component) + { + component.AddChild(webProperty); + } + else + { + this.Core.OnMessage(WixWarnings.ExpectedForeignRow(row.SourceLineNumbers, table.Name, row.GetPrimaryKey(DecompilerConstants.PrimaryKeyDelimiter), "Component_", (string)row[1], "Component")); + } + } + } + + /// + /// Decompile the IIsHttpHeader table. + /// + /// The table to decompile. + private void DecompileIIsHttpHeaderTable(Table table) + { + foreach (Row row in table.Rows) + { + IIs.HttpHeader httpHeader = new IIs.HttpHeader(); + + httpHeader.Name = (string)row[3]; + + // the ParentType and Parent columns are handled in FinalizeIIsHttpHeaderTable + + httpHeader.Value = (string)row[4]; + + this.Core.IndexElement(row, httpHeader); + } + } + + /// + /// Decompile the IIsMimeMap table. + /// + /// The table to decompile. + private void DecompileIIsMimeMapTable(Table table) + { + foreach (Row row in table.Rows) + { + IIs.MimeMap mimeMap = new IIs.MimeMap(); + + mimeMap.Id = (string)row[0]; + + // the ParentType and ParentValue columns are handled in FinalizeIIsMimeMapTable + + mimeMap.Type = (string)row[3]; + + mimeMap.Extension = (string)row[4]; + + this.Core.IndexElement(row, mimeMap); + } + } + + /// + /// Decompile the IIsWebAddress table. + /// + /// The table to decompile. + private void DecompileIIsWebAddressTable(Table table) + { + foreach (Row row in table.Rows) + { + IIs.WebAddress webAddress = new IIs.WebAddress(); + + webAddress.Id = (string)row[0]; + + if (null != row[2]) + { + webAddress.IP = (string)row[2]; + } + + webAddress.Port = (string)row[3]; + + if (null != row[4]) + { + webAddress.Header = (string)row[4]; + } + + if (null != row[5] && 1 == (int)row[5]) + { + webAddress.Secure = IIs.YesNoType.yes; + } + + this.Core.IndexElement(row, webAddress); + } + } + + /// + /// Decompile the IIsWebApplication table. + /// + /// The table to decompile. + private void DecompileIIsWebApplicationTable(Table table) + { + foreach (Row row in table.Rows) + { + IIs.WebApplication webApplication = new IIs.WebApplication(); + + webApplication.Id = (string)row[0]; + + webApplication.Name = (string)row[1]; + + // these are not listed incorrectly - the order is low, high, medium + switch ((int)row[2]) + { + case 0: + webApplication.Isolation = IIs.WebApplication.IsolationType.low; + break; + case 1: + webApplication.Isolation = IIs.WebApplication.IsolationType.high; + break; + case 2: + webApplication.Isolation = IIs.WebApplication.IsolationType.medium; + break; + default: + // TODO: warn + break; + } + + if (null != row[3]) + { + switch ((int)row[3]) + { + case 0: + webApplication.AllowSessions = IIs.YesNoDefaultType.no; + break; + case 1: + webApplication.AllowSessions = IIs.YesNoDefaultType.yes; + break; + default: + // TODO: warn + break; + } + } + + if (null != row[4]) + { + webApplication.SessionTimeout = (int)row[4]; + } + + if (null != row[5]) + { + switch ((int)row[5]) + { + case 0: + webApplication.Buffer = IIs.YesNoDefaultType.no; + break; + case 1: + webApplication.Buffer = IIs.YesNoDefaultType.yes; + break; + default: + // TODO: warn + break; + } + } + + if (null != row[6]) + { + switch ((int)row[6]) + { + case 0: + webApplication.ParentPaths = IIs.YesNoDefaultType.no; + break; + case 1: + webApplication.ParentPaths = IIs.YesNoDefaultType.yes; + break; + default: + // TODO: warn + break; + } + } + + if (null != row[7]) + { + switch ((string)row[7]) + { + case "JScript": + webApplication.DefaultScript = IIs.WebApplication.DefaultScriptType.JScript; + break; + case "VBScript": + webApplication.DefaultScript = IIs.WebApplication.DefaultScriptType.VBScript; + break; + default: + // TODO: warn + break; + } + } + + if (null != row[8]) + { + webApplication.ScriptTimeout = (int)row[8]; + } + + if (null != row[9]) + { + switch ((int)row[9]) + { + case 0: + webApplication.ServerDebugging = IIs.YesNoDefaultType.no; + break; + case 1: + webApplication.ServerDebugging = IIs.YesNoDefaultType.yes; + break; + default: + // TODO: warn + break; + } + } + + if (null != row[10]) + { + switch ((int)row[10]) + { + case 0: + webApplication.ClientDebugging = IIs.YesNoDefaultType.no; + break; + case 1: + webApplication.ClientDebugging = IIs.YesNoDefaultType.yes; + break; + default: + // TODO: warn + break; + } + } + + if (null != row[11]) + { + webApplication.WebAppPool = (string)row[11]; + } + + this.Core.IndexElement(row, webApplication); + } + } + + /// + /// Decompile the IIsWebDirProperties table. + /// + /// The table to decompile. + private void DecompileIIsWebDirPropertiesTable(Table table) + { + foreach (Row row in table.Rows) + { + IIs.WebDirProperties webDirProperties = new IIs.WebDirProperties(); + + webDirProperties.Id = (string)row[0]; + + if (null != row[1]) + { + int access = (int)row[1]; + + if (0x1 == (access & 0x1)) + { + webDirProperties.Read = IIs.YesNoType.yes; + } + + if (0x2 == (access & 0x2)) + { + webDirProperties.Write = IIs.YesNoType.yes; + } + + if (0x4 == (access & 0x4)) + { + webDirProperties.Execute = IIs.YesNoType.yes; + } + + if (0x200 == (access & 0x200)) + { + webDirProperties.Script = IIs.YesNoType.yes; + } + } + + if (null != row[2]) + { + int authorization = (int)row[2]; + + if (0x1 == (authorization & 0x1)) + { + webDirProperties.AnonymousAccess = IIs.YesNoType.yes; + } + else // set one of the properties to 'no' to force the output value to be '0' if not other attributes are set + { + webDirProperties.AnonymousAccess = IIs.YesNoType.no; + } + + if (0x2 == (authorization & 0x2)) + { + webDirProperties.BasicAuthentication = IIs.YesNoType.yes; + } + + if (0x4 == (authorization & 0x4)) + { + webDirProperties.WindowsAuthentication = IIs.YesNoType.yes; + } + + if (0x10 == (authorization & 0x10)) + { + webDirProperties.DigestAuthentication = IIs.YesNoType.yes; + } + + if (0x40 == (authorization & 0x40)) + { + webDirProperties.PassportAuthentication = IIs.YesNoType.yes; + } + } + + if (null != row[3]) + { + webDirProperties.AnonymousUser = (string)row[3]; + } + + if (null != row[4] && 1 == (int)row[4]) + { + webDirProperties.IIsControlledPassword = IIs.YesNoType.yes; + } + + if (null != row[5]) + { + switch ((int)row[5]) + { + case 0: + webDirProperties.LogVisits = IIs.YesNoType.no; + break; + case 1: + webDirProperties.LogVisits = IIs.YesNoType.yes; + break; + default: + // TODO: warn + break; + } + } + + if (null != row[6]) + { + switch ((int)row[6]) + { + case 0: + webDirProperties.Index = IIs.YesNoType.no; + break; + case 1: + webDirProperties.Index = IIs.YesNoType.yes; + break; + default: + // TODO: warn + break; + } + } + + if (null != row[7]) + { + webDirProperties.DefaultDocuments = (string)row[7]; + } + + if (null != row[8]) + { + switch ((int)row[8]) + { + case 0: + webDirProperties.AspDetailedError = IIs.YesNoType.no; + break; + case 1: + webDirProperties.AspDetailedError = IIs.YesNoType.yes; + break; + default: + // TODO: warn + break; + } + } + + if (null != row[9]) + { + webDirProperties.HttpExpires = (string)row[9]; + } + + if (null != row[10]) + { + // force the value to be a positive number + webDirProperties.CacheControlMaxAge = unchecked((uint)(int)row[10]); + } + + if (null != row[11]) + { + webDirProperties.CacheControlCustom = (string)row[11]; + } + + if (null != row[12]) + { + switch ((int)row[8]) + { + case 0: + webDirProperties.ClearCustomError = IIs.YesNoType.no; + break; + case 1: + webDirProperties.ClearCustomError = IIs.YesNoType.yes; + break; + default: + // TODO: warn + break; + } + } + + if (null != row[13]) + { + int accessSSLFlags = (int)row[13]; + + if (0x8 == (accessSSLFlags & 0x8)) + { + webDirProperties.AccessSSL = IIs.YesNoType.yes; + } + + if (0x20 == (accessSSLFlags & 0x20)) + { + webDirProperties.AccessSSLNegotiateCert = IIs.YesNoType.yes; + } + + if (0x40 == (accessSSLFlags & 0x40)) + { + webDirProperties.AccessSSLRequireCert = IIs.YesNoType.yes; + } + + if (0x80 == (accessSSLFlags & 0x80)) + { + webDirProperties.AccessSSLMapCert = IIs.YesNoType.yes; + } + + if (0x100 == (accessSSLFlags & 0x100)) + { + webDirProperties.AccessSSL128 = IIs.YesNoType.yes; + } + } + + if (null != row[14]) + { + webDirProperties.AuthenticationProviders = (string)row[14]; + } + + this.Core.RootElement.AddChild(webDirProperties); + } + } + + /// + /// Decompile the IIsWebError table. + /// + /// The table to decompile. + private void DecompileIIsWebErrorTable(Table table) + { + foreach (Row row in table.Rows) + { + IIs.WebError webError = new IIs.WebError(); + + webError.ErrorCode = (int)row[0]; + + webError.SubCode = (int)row[1]; + + // the ParentType and ParentValue columns are handled in FinalizeIIsWebErrorTable + + if (null != row[4]) + { + webError.File = (string)row[4]; + } + + if (null != row[5]) + { + webError.URL = (string)row[5]; + } + + this.Core.IndexElement(row, webError); + } + } + + /// + /// Decompile the IIsFilter table. + /// + /// The table to decompile. + private void DecompileIIsFilterTable(Table table) + { + foreach (Row row in table.Rows) + { + IIs.WebFilter webFilter = new IIs.WebFilter(); + + webFilter.Id = (string)row[0]; + + webFilter.Name = (string)row[1]; + + if (null != row[3]) + { + webFilter.Path = (string)row[3]; + } + + if (null != row[5]) + { + webFilter.Description = (string)row[5]; + } + + webFilter.Flags = (int)row[6]; + + if (null != row[7]) + { + switch ((int)row[7]) + { + case (-1): + webFilter.LoadOrder = "last"; + break; + case 0: + webFilter.LoadOrder = "first"; + break; + default: + webFilter.LoadOrder = Convert.ToString((int)row[7], CultureInfo.InvariantCulture); + break; + } + } + + if (null != row[4]) + { + IIs.WebSite webSite = (IIs.WebSite)this.Core.GetIndexedElement("IIsWebSite", (string)row[4]); + + if (null != webSite) + { + webSite.AddChild(webFilter); + } + else + { + this.Core.OnMessage(WixWarnings.ExpectedForeignRow(row.SourceLineNumbers, table.Name, row.GetPrimaryKey(DecompilerConstants.PrimaryKeyDelimiter), "Web_", (string)row[4], "IIsWebSite")); + } + } + else // Component parent + { + Wix.Component component = (Wix.Component)this.Core.GetIndexedElement("Component", (string)row[2]); + + if (null != component) + { + component.AddChild(webFilter); + } + else + { + this.Core.OnMessage(WixWarnings.ExpectedForeignRow(row.SourceLineNumbers, table.Name, row.GetPrimaryKey(DecompilerConstants.PrimaryKeyDelimiter), "Component_", (string)row[2], "Component")); + } + } + } + } + + /// + /// Decompile the IIsWebLog table. + /// + /// The table to decompile. + private void DecompileIIsWebLogTable(Table table) + { + foreach (Row row in table.Rows) + { + IIs.WebLog webLog = new IIs.WebLog(); + + webLog.Id = (string)row[0]; + + switch ((string)row[1]) + { + case "Microsoft IIS Log File Format": + webLog.Type = IIs.WebLog.TypeType.IIS; + break; + case "NCSA Common Log File Format": + webLog.Type = IIs.WebLog.TypeType.NCSA; + break; + case "none": + webLog.Type = IIs.WebLog.TypeType.none; + break; + case "ODBC Logging": + webLog.Type = IIs.WebLog.TypeType.ODBC; + break; + case "W3C Extended Log File Format": + webLog.Type = IIs.WebLog.TypeType.W3C; + break; + default: + // TODO: warn + break; + } + + this.Core.RootElement.AddChild(webLog); + } + } + + /// + /// Decompile the IIsWebServiceExtension table. + /// + /// The table to decompile. + private void DecompileIIsWebServiceExtensionTable(Table table) + { + foreach (Row row in table.Rows) + { + IIs.WebServiceExtension webServiceExtension = new IIs.WebServiceExtension(); + + webServiceExtension.Id = (string)row[0]; + + webServiceExtension.File = (string)row[2]; + + if (null != row[3]) + { + webServiceExtension.Description = (string)row[3]; + } + + if (null != row[4]) + { + webServiceExtension.Group = (string)row[4]; + } + + int attributes = (int)row[5]; + + if (0x1 == (attributes & 0x1)) + { + webServiceExtension.Allow = IIs.YesNoType.yes; + } + else + { + webServiceExtension.Allow = IIs.YesNoType.no; + } + + if (0x2 == (attributes & 0x2)) + { + webServiceExtension.UIDeletable = IIs.YesNoType.yes; + } + + Wix.Component component = (Wix.Component)this.Core.GetIndexedElement("Component", (string)row[1]); + if (null != component) + { + component.AddChild(webServiceExtension); + } + else + { + this.Core.OnMessage(WixWarnings.ExpectedForeignRow(row.SourceLineNumbers, table.Name, row.GetPrimaryKey(DecompilerConstants.PrimaryKeyDelimiter), "Component_", (string)row[1], "Component")); + } + } + } + + /// + /// Decompile the IIsWebSite table. + /// + /// The table to decompile. + private void DecompileIIsWebSiteTable(Table table) + { + foreach (Row row in table.Rows) + { + IIs.WebSite webSite = new IIs.WebSite(); + + webSite.Id = (string)row[0]; + + if (null != row[2]) + { + webSite.Description = (string)row[2]; + } + + if (null != row[3]) + { + webSite.ConnectionTimeout = (int)row[3]; + } + + if (null != row[4]) + { + webSite.Directory = (string)row[4]; + } + + if (null != row[5]) + { + switch ((int)row[5]) + { + case 0: + // this is the default + break; + case 1: + webSite.StartOnInstall = IIs.YesNoType.yes; + break; + case 2: + webSite.AutoStart = IIs.YesNoType.yes; + break; + default: + // TODO: warn + break; + } + } + + if (null != row[6]) + { + int attributes = (int)row[6]; + + if (0x2 == (attributes & 0x2)) + { + webSite.ConfigureIfExists = IIs.YesNoType.no; + } + } + + // the KeyAddress_ column is handled in FinalizeWebAddressTable + + if (null != row[8]) + { + webSite.DirProperties = (string)row[8]; + } + + // the Application_ column is handled in FinalizeIIsWebApplicationTable + + if (null != row[10]) + { + if (-1 != (int)row[10]) + { + webSite.Sequence = (int)row[10]; + } + } + + if (null != row[11]) + { + webSite.WebLog = (string)row[11]; + } + + if (null != row[12]) + { + webSite.SiteId = (string)row[12]; + } + + if (null != row[1]) + { + Wix.Component component = (Wix.Component)this.Core.GetIndexedElement("Component", (string)row[1]); + + if (null != component) + { + component.AddChild(webSite); + } + else + { + this.Core.OnMessage(WixWarnings.ExpectedForeignRow(row.SourceLineNumbers, table.Name, row.GetPrimaryKey(DecompilerConstants.PrimaryKeyDelimiter), "Component_", (string)row[1], "Component")); + } + } + else + { + this.Core.RootElement.AddChild(webSite); + } + this.Core.IndexElement(row, webSite); + } + } + + /// + /// Decompile the IIsWebVirtualDir table. + /// + /// The table to decompile. + private void DecompileIIsWebVirtualDirTable(Table table) + { + foreach (Row row in table.Rows) + { + IIs.WebVirtualDir webVirtualDir = new IIs.WebVirtualDir(); + + webVirtualDir.Id = (string)row[0]; + + // the Component_ and Web_ columns are handled in FinalizeIIsWebVirtualDirTable + + webVirtualDir.Alias = (string)row[3]; + + webVirtualDir.Directory = (string)row[4]; + + if (null != row[5]) + { + webVirtualDir.DirProperties = (string)row[5]; + } + + // the Application_ column is handled in FinalizeIIsWebApplicationTable + + this.Core.IndexElement(row, webVirtualDir); + } + } + + /// + /// Decompile the IIsWebSiteCertificates table. + /// + /// The table to decompile. + private void DecompileIIsWebSiteCertificatesTable(Table table) + { + foreach (Row row in table.Rows) + { + IIs.CertificateRef certificateRef = new IIs.CertificateRef(); + + certificateRef.Id = (string)row[1]; + + this.Core.IndexElement(row, certificateRef); + } + } + + /// + /// Finalize the IIsHttpHeader table. + /// + /// The collection of all tables. + /// + /// The IIsHttpHeader table supports multiple parent types so no foreign key + /// is declared and thus nesting must be done late. + /// + private void FinalizeIIsHttpHeaderTable(TableIndexedCollection tables) + { + Table iisHttpHeaderTable = tables["IIsHttpHeader"]; + + if (null != iisHttpHeaderTable) + { + foreach (Row row in iisHttpHeaderTable.Rows) + { + IIs.HttpHeader httpHeader = (IIs.HttpHeader)this.Core.GetIndexedElement(row); + + if (1 == (int)row[1]) + { + IIs.WebVirtualDir webVirtualDir = (IIs.WebVirtualDir)this.Core.GetIndexedElement("IIsWebVirtualDir", (string)row[2]); + if (null != webVirtualDir) + { + webVirtualDir.AddChild(httpHeader); + } + else + { + this.Core.OnMessage(WixWarnings.ExpectedForeignRow(row.SourceLineNumbers, iisHttpHeaderTable.Name, row.GetPrimaryKey(DecompilerConstants.PrimaryKeyDelimiter), "ParentValue", (string)row[2], "IIsWebVirtualDir")); + } + } + else if (2 == (int)row[1]) + { + IIs.WebSite webSite = (IIs.WebSite)this.Core.GetIndexedElement("IIsWebSite", (string)row[2]); + if (null != webSite) + { + webSite.AddChild(httpHeader); + } + else + { + this.Core.OnMessage(WixWarnings.ExpectedForeignRow(row.SourceLineNumbers, iisHttpHeaderTable.Name, row.GetPrimaryKey(DecompilerConstants.PrimaryKeyDelimiter), "ParentValue", (string)row[2], "IIsWebSite")); + } + } + } + } + } + + /// + /// Finalize the IIsMimeMap table. + /// + /// The collection of all tables. + /// + /// The IIsMimeMap table supports multiple parent types so no foreign key + /// is declared and thus nesting must be done late. + /// + private void FinalizeIIsMimeMapTable(TableIndexedCollection tables) + { + Table iisMimeMapTable = tables["IIsMimeMap"]; + + if (null != iisMimeMapTable) + { + foreach (Row row in iisMimeMapTable.Rows) + { + IIs.MimeMap mimeMap = (IIs.MimeMap)this.Core.GetIndexedElement(row); + + if (2 < (int)row[1] || 0 >= (int)row[1]) + { + // TODO: warn about unknown parent type + } + + IIs.WebVirtualDir webVirtualDir = (IIs.WebVirtualDir)this.Core.GetIndexedElement("IIsWebVirtualDir", (string)row[2]); + IIs.WebSite webSite = (IIs.WebSite)this.Core.GetIndexedElement("IIsWebSite", (string)row[2]); + if (null != webVirtualDir) + { + webVirtualDir.AddChild(mimeMap); + } + else if (null != webSite) + { + webSite.AddChild(mimeMap); + } + else + { + this.Core.OnMessage(WixWarnings.ExpectedForeignRow(row.SourceLineNumbers, iisMimeMapTable.Name, row.GetPrimaryKey(DecompilerConstants.PrimaryKeyDelimiter), "ParentValue", (string)row[2], "IIsWebVirtualDir")); + } + } + } + } + + /// + /// Finalize the IIsWebApplication table. + /// + /// The collection of all tables. + /// + /// Since WebApplication elements may nest under a specific WebSite or + /// WebVirtualDir (or just the root element), the nesting must be done late. + /// + private void FinalizeIIsWebApplicationTable(TableIndexedCollection tables) + { + Table iisWebApplicationTable = tables["IIsWebApplication"]; + Table iisWebSiteTable = tables["IIsWebSite"]; + Table iisWebVirtualDirTable = tables["IIsWebVirtualDir"]; + + Hashtable addedWebApplications = new Hashtable(); + + if (null != iisWebSiteTable) + { + foreach (Row row in iisWebSiteTable.Rows) + { + if (null != row[9]) + { + IIs.WebSite webSite = (IIs.WebSite)this.Core.GetIndexedElement(row); + + IIs.WebApplication webApplication = (IIs.WebApplication)this.Core.GetIndexedElement("IIsWebApplication", (string)row[9]); + if (null != webApplication) + { + webSite.AddChild(webApplication); + addedWebApplications[webApplication] = null; + } + else + { + this.Core.OnMessage(WixWarnings.ExpectedForeignRow(row.SourceLineNumbers, iisWebSiteTable.Name, row.GetPrimaryKey(DecompilerConstants.PrimaryKeyDelimiter), "Application_", (string)row[9], "IIsWebApplication")); + } + } + } + } + + if (null != iisWebVirtualDirTable) + { + foreach (Row row in iisWebVirtualDirTable.Rows) + { + if (null != row[6]) + { + IIs.WebVirtualDir webVirtualDir = (IIs.WebVirtualDir)this.Core.GetIndexedElement(row); + + IIs.WebApplication webApplication = (IIs.WebApplication)this.Core.GetIndexedElement("IIsWebApplication", (string)row[6]); + if (null != webApplication) + { + webVirtualDir.AddChild(webApplication); + addedWebApplications[webApplication] = null; + } + else + { + this.Core.OnMessage(WixWarnings.ExpectedForeignRow(row.SourceLineNumbers, iisWebVirtualDirTable.Name, row.GetPrimaryKey(DecompilerConstants.PrimaryKeyDelimiter), "Application_", (string)row[6], "IIsWebApplication")); + } + } + } + } + + if (null != iisWebApplicationTable) + { + foreach (Row row in iisWebApplicationTable.Rows) + { + IIs.WebApplication webApplication = (IIs.WebApplication)this.Core.GetIndexedElement(row); + + if (!addedWebApplications.Contains(webApplication)) + { + this.Core.RootElement.AddChild(webApplication); + } + } + } + } + + /// + /// Finalize the IIsWebError table. + /// + /// The collection of all tables. + /// + /// Since there is no foreign key relationship declared for this table + /// (because it takes various parent types), it must be nested late. + /// + private void FinalizeIIsWebErrorTable(TableIndexedCollection tables) + { + Table iisWebErrorTable = tables["IIsWebError"]; + + if (null != iisWebErrorTable) + { + foreach (Row row in iisWebErrorTable.Rows) + { + IIs.WebError webError = (IIs.WebError)this.Core.GetIndexedElement(row); + + if (1 == (int)row[2]) // WebVirtualDir parent + { + IIs.WebVirtualDir webVirtualDir = (IIs.WebVirtualDir)this.Core.GetIndexedElement("IIsWebVirtualDir", (string)row[3]); + + if (null != webVirtualDir) + { + webVirtualDir.AddChild(webError); + } + else + { + this.Core.OnMessage(WixWarnings.ExpectedForeignRow(row.SourceLineNumbers, iisWebErrorTable.Name, row.GetPrimaryKey(DecompilerConstants.PrimaryKeyDelimiter), "ParentValue", (string)row[3], "IIsWebVirtualDir")); + } + } + else if (2 == (int)row[2]) // WebSite parent + { + IIs.WebSite webSite = (IIs.WebSite)this.Core.GetIndexedElement("IIsWebSite", (string)row[3]); + + if (null != webSite) + { + webSite.AddChild(webError); + } + else + { + this.Core.OnMessage(WixWarnings.ExpectedForeignRow(row.SourceLineNumbers, iisWebErrorTable.Name, row.GetPrimaryKey(DecompilerConstants.PrimaryKeyDelimiter), "ParentValue", (string)row[3], "IIsWebSite")); + } + } + else + { + // TODO: warn unknown parent type + } + } + } + } + + /// + /// Finalize the IIsWebVirtualDir table. + /// + /// The collection of all tables. + /// + /// WebVirtualDir elements nest under either a WebSite or component + /// depending upon whether the component in the IIsWebVirtualDir row + /// is the same as the one in the parent IIsWebSite row. + /// + private void FinalizeIIsWebVirtualDirTable(TableIndexedCollection tables) + { + Table iisWebSiteTable = tables["IIsWebSite"]; + Table iisWebVirtualDirTable = tables["IIsWebVirtualDir"]; + + Hashtable iisWebSiteRows = new Hashtable(); + + // index the IIsWebSite rows by their primary keys + if (null != iisWebSiteTable) + { + foreach (Row row in iisWebSiteTable.Rows) + { + iisWebSiteRows.Add(row[0], row); + } + } + + if (null != iisWebVirtualDirTable) + { + foreach (Row row in iisWebVirtualDirTable.Rows) + { + IIs.WebVirtualDir webVirtualDir = (IIs.WebVirtualDir)this.Core.GetIndexedElement(row); + Row iisWebSiteRow = (Row)iisWebSiteRows[row[2]]; + + if (null != iisWebSiteRow) + { + if ((string)iisWebSiteRow[1] == (string)row[1]) + { + IIs.WebSite webSite = (IIs.WebSite)this.Core.GetIndexedElement(iisWebSiteRow); + + webSite.AddChild(webVirtualDir); + } + else + { + Wix.Component component = (Wix.Component)this.Core.GetIndexedElement("Component", (string)row[1]); + + if (null != component) + { + webVirtualDir.WebSite = (string)row[2]; + component.AddChild(webVirtualDir); + } + else + { + this.Core.OnMessage(WixWarnings.ExpectedForeignRow(row.SourceLineNumbers, iisWebVirtualDirTable.Name, row.GetPrimaryKey(DecompilerConstants.PrimaryKeyDelimiter), "Component_", (string)row[1], "Component")); + } + } + } + else + { + this.Core.OnMessage(WixWarnings.ExpectedForeignRow(row.SourceLineNumbers, iisWebVirtualDirTable.Name, row.GetPrimaryKey(DecompilerConstants.PrimaryKeyDelimiter), "Web_", (string)row[2], "IIsWebSite")); + } + } + } + } + + /// + /// Finalize the IIsWebSiteCertificates table. + /// + /// The collection of all tables. + /// + /// This table creates CertificateRef elements which nest under WebSite + /// elements. + /// + private void FinalizeIIsWebSiteCertificatesTable(TableIndexedCollection tables) + { + Table IIsWebSiteCertificatesTable = tables["IIsWebSiteCertificates"]; + + if (null != IIsWebSiteCertificatesTable) + { + foreach (Row row in IIsWebSiteCertificatesTable.Rows) + { + IIs.CertificateRef certificateRef = (IIs.CertificateRef)this.Core.GetIndexedElement(row); + IIs.WebSite webSite = (IIs.WebSite)this.Core.GetIndexedElement("IIsWebSite", (string)row[0]); + + if (null != webSite) + { + webSite.AddChild(certificateRef); + } + else + { + this.Core.OnMessage(WixWarnings.ExpectedForeignRow(row.SourceLineNumbers, IIsWebSiteCertificatesTable.Name, row.GetPrimaryKey(DecompilerConstants.PrimaryKeyDelimiter), "Web_", (string)row[0], "IIsWebSite")); + } + } + } + } + + /// + /// Finalize the WebAddress table. + /// + /// The collection of all tables. + /// + /// There is a circular dependency between the WebAddress and WebSite + /// tables, so nesting must be handled here. + /// + private void FinalizeWebAddressTable(TableIndexedCollection tables) + { + Table iisWebAddressTable = tables["IIsWebAddress"]; + Table iisWebSiteTable = tables["IIsWebSite"]; + + Hashtable addedWebAddresses = new Hashtable(); + + if (null != iisWebSiteTable) + { + foreach (Row row in iisWebSiteTable.Rows) + { + IIs.WebSite webSite = (IIs.WebSite)this.Core.GetIndexedElement(row); + + IIs.WebAddress webAddress = (IIs.WebAddress)this.Core.GetIndexedElement("IIsWebAddress", (string)row[7]); + if (null != webAddress) + { + webSite.AddChild(webAddress); + addedWebAddresses[webAddress] = null; + } + else + { + this.Core.OnMessage(WixWarnings.ExpectedForeignRow(row.SourceLineNumbers, iisWebSiteTable.Name, row.GetPrimaryKey(DecompilerConstants.PrimaryKeyDelimiter), "KeyAddress_", (string)row[7], "IIsWebAddress")); + } + } + } + + if (null != iisWebAddressTable) + { + foreach (Row row in iisWebAddressTable.Rows) + { + IIs.WebAddress webAddress = (IIs.WebAddress)this.Core.GetIndexedElement(row); + + if (!addedWebAddresses.Contains(webAddress)) + { + IIs.WebSite webSite = (IIs.WebSite)this.Core.GetIndexedElement("IIsWebSite", (string)row[1]); + + if (null != webSite) + { + webSite.AddChild(webAddress); + } + else + { + this.Core.OnMessage(WixWarnings.ExpectedForeignRow(row.SourceLineNumbers, iisWebAddressTable.Name, row.GetPrimaryKey(DecompilerConstants.PrimaryKeyDelimiter), "Web_", (string)row[1], "IIsWebSite")); + } + } + } + } + } + } +#endif +} diff --git a/src/ext/Iis/wixext/IIsExtensionData.cs b/src/ext/Iis/wixext/IIsExtensionData.cs new file mode 100644 index 00000000..6a0e1f09 --- /dev/null +++ b/src/ext/Iis/wixext/IIsExtensionData.cs @@ -0,0 +1,30 @@ +// 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. + +namespace WixToolset.Iis +{ + using WixToolset.Data; + using WixToolset.Extensibility; + + /// + /// The WiX Toolset Internet Information Services Extension. + /// + public sealed class IIsExtensionData : BaseExtensionData + { + /// + /// Gets the default culture. + /// + /// The default culture. + public override string DefaultCulture => "en-US"; + + public override bool TryGetSymbolDefinitionByName(string name, out IntermediateSymbolDefinition symbolDefinition) + { + symbolDefinition = IisSymbolDefinitions.ByName(name); + return symbolDefinition != null; + } + + public override Intermediate GetLibrary(ISymbolDefinitionCreator symbolDefinitions) + { + return Intermediate.Load(typeof(IIsExtensionData).Assembly, "WixToolset.Iis.iis.wixlib", symbolDefinitions); + } + } +} diff --git a/src/ext/Iis/wixext/IisErrors.cs b/src/ext/Iis/wixext/IisErrors.cs new file mode 100644 index 00000000..2f226217 --- /dev/null +++ b/src/ext/Iis/wixext/IisErrors.cs @@ -0,0 +1,78 @@ +// 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. + +namespace WixToolset.Data +{ + using System; + using System.Resources; + + public static class IIsErrors + { + public static Message DeprecatedBinaryChildElement(SourceLineNumber sourceLineNumbers, string elementName) + { + return Message(sourceLineNumbers, Ids.DeprecatedBinaryChildElement, "The {0} element contains a deprecated child Binary element. Please move the Binary element under a Fragment, Module, or Product element and set the {0}/@BinaryKey attribute to the value of the Binary/@Id attribute.", elementName); + } + + public static Message IllegalAttributeWithoutComponent(SourceLineNumber sourceLineNumbers, string elementName, string attributeName) + { + return Message(sourceLineNumbers, Ids.IllegalAttributeWithoutComponent, "The {0}/@{1} attribute cannot be specified unless the element has a Component as an ancestor. A {0} that does not have a Component ancestor is not installed.", elementName, attributeName); + } + + public static Message IllegalCharacterInAttributeValue(SourceLineNumber sourceLineNumbers, string elementName, string attributeName, string value, Char illegalCharacter) + { + return Message(sourceLineNumbers, Ids.IllegalCharacterInAttributeValue, "The {0}/@{1} attribute's value, '{2}', is invalid. It cannot contain the character '{3}'.", elementName, attributeName, value, illegalCharacter); + } + + public static Message IllegalElementWithoutComponent(SourceLineNumber sourceLineNumbers, string elementName) + { + return Message(sourceLineNumbers, Ids.IllegalElementWithoutComponent, "The {0} element cannot be specified unless the element has a Component as an ancestor. A {0} that does not have a Component ancestor is not installed.", elementName); + } + + public static Message MimeMapExtensionMissingPeriod(SourceLineNumber sourceLineNumbers, string elementName, string attributeName, string attributeValue) + { + return Message(sourceLineNumbers, Ids.MimeMapExtensionMissingPeriod, "The {0}/@{1} attribute's value, '{2}', is not a valid mime map extension. It must begin with a period.", elementName, attributeName, attributeValue); + } + + public static Message OneOfAttributesRequiredUnderComponent(SourceLineNumber sourceLineNumbers, string elementName, string attributeName1, string attributeName2, string attributeName3, string attributeName4) + { + return Message(sourceLineNumbers, Ids.OneOfAttributesRequiredUnderComponent, "When nested under a Component, the {0} element must have one of the following attributes specified: {1}, {2}, {3} or {4}.", elementName, attributeName1, attributeName2, attributeName3, attributeName4); + } + + public static Message RequiredAttributeUnderComponent(SourceLineNumber sourceLineNumbers, string elementName, string attributeName) + { + return Message(sourceLineNumbers, Ids.RequiredAttributeUnderComponent, "The {0}/@{1} attribute must be specified when the element has a Component as an ancestor.", elementName, attributeName); + } + + public static Message WebApplicationAlreadySpecified(SourceLineNumber sourceLineNumbers, string elementName) + { + return Message(sourceLineNumbers, Ids.WebApplicationAlreadySpecified, "The {0} element can have at most a single WebApplication specified. This can be either through the WebApplication attribute, or through a nested WebApplication element, but not both.", elementName); + } + + public static Message WebSiteAttributeUnderWebSite(SourceLineNumber sourceLineNumbers, string elementName) + { + return Message(sourceLineNumbers, Ids.WebSiteAttributeUnderWebSite, "The {0}/@WebSite attribute cannot be specified when the {0} element is nested under a WebSite element.", elementName); + } + + private static Message Message(SourceLineNumber sourceLineNumber, Ids id, string format, params object[] args) + { + return new Message(sourceLineNumber, MessageLevel.Error, (int)id, format, args); + } + + private static Message Message(SourceLineNumber sourceLineNumber, Ids id, ResourceManager resourceManager, string resourceName, params object[] args) + { + return new Message(sourceLineNumber, MessageLevel.Error, (int)id, resourceManager, resourceName, args); + } + + public enum Ids + { + MimeMapExtensionMissingPeriod = 5150, + IllegalAttributeWithoutComponent = 5151, + IllegalElementWithoutComponent = 5152, + OneOfAttributesRequiredUnderComponent = 5153, + WebSiteAttributeUnderWebSite = 5154, + WebApplicationAlreadySpecified = 5155, + IllegalCharacterInAttributeValue = 5156, + DeprecatedBinaryChildElement = 5157, + RequiredAttributeUnderComponent = 5161, + } + } +} diff --git a/src/ext/Iis/wixext/IisExtensionFactory.cs b/src/ext/Iis/wixext/IisExtensionFactory.cs new file mode 100644 index 00000000..2fb7e682 --- /dev/null +++ b/src/ext/Iis/wixext/IisExtensionFactory.cs @@ -0,0 +1,18 @@ +// 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. + +namespace WixToolset.Iis +{ + using System; + using System.Collections.Generic; + using WixToolset.Extensibility; + + public class IisExtensionFactory : BaseExtensionFactory + { + protected override IReadOnlyCollection ExtensionTypes => new[] + { + typeof(IIsCompiler), + typeof(IIsExtensionData), + typeof(IisWindowsInstallerBackendBinderExtension), + }; + } +} diff --git a/src/ext/Iis/wixext/IisTableDefinitions.cs b/src/ext/Iis/wixext/IisTableDefinitions.cs new file mode 100644 index 00000000..f513152e --- /dev/null +++ b/src/ext/Iis/wixext/IisTableDefinitions.cs @@ -0,0 +1,324 @@ +// 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. + +namespace WixToolset.Iis +{ + using WixToolset.Data.WindowsInstaller; + + public static class IisTableDefinitions + { + public static readonly TableDefinition Certificate = new TableDefinition( + "Certificate", + IisSymbolDefinitions.Certificate, + new[] + { + new ColumnDefinition("Certificate", ColumnType.String, 72, primaryKey: true, nullable: false, ColumnCategory.Identifier, keyColumn: 1, description: "Identifier for the certificate in the package.", modularizeType: ColumnModularizeType.Column), + new ColumnDefinition("Component_", ColumnType.String, 72, primaryKey: false, nullable: false, ColumnCategory.Identifier, description: "Foreign key into the Component table used to determine install state", modularizeType: ColumnModularizeType.Column), + new ColumnDefinition("Name", ColumnType.String, 255, primaryKey: false, nullable: false, ColumnCategory.Formatted, description: "Name to be used for the Certificate."), + new ColumnDefinition("StoreLocation", ColumnType.Number, 2, primaryKey: false, nullable: false, ColumnCategory.Unknown, minValue: 1, maxValue: 2, description: "Location of the target certificate store (CurrentUser == 1, LocalMachine == 2)"), + new ColumnDefinition("StoreName", ColumnType.String, 64, primaryKey: false, nullable: false, ColumnCategory.Formatted, description: "Name of the target certificate store"), + new ColumnDefinition("Attributes", ColumnType.Number, 4, primaryKey: false, nullable: false, ColumnCategory.Unknown, minValue: 0, maxValue: 2147483647, description: "Attributes of the certificate"), + new ColumnDefinition("Binary_", ColumnType.String, 72, primaryKey: false, nullable: true, ColumnCategory.Identifier, keyTable: "Binary", keyColumn: 1, description: "Identifier to Binary table containing certificate.", modularizeType: ColumnModularizeType.Column), + new ColumnDefinition("CertificatePath", ColumnType.String, 0, primaryKey: false, nullable: true, ColumnCategory.Formatted, description: "Property to path of certificate.", modularizeType: ColumnModularizeType.Property), + new ColumnDefinition("PFXPassword", ColumnType.String, 0, primaryKey: false, nullable: true, ColumnCategory.Formatted, description: "Hidden property to a pfx password", modularizeType: ColumnModularizeType.Property), + }, + symbolIdIsPrimaryKey: true + ); + + public static readonly TableDefinition CertificateHash = new TableDefinition( + "CertificateHash", + IisSymbolDefinitions.CertificateHash, + new[] + { + new ColumnDefinition("Certificate_", ColumnType.String, 72, primaryKey: true, nullable: false, ColumnCategory.Identifier, keyColumn: 1, description: "Foreign key to certificate in Certificate table.", modularizeType: ColumnModularizeType.Column), + new ColumnDefinition("Hash", ColumnType.String, 0, primaryKey: false, nullable: true, ColumnCategory.Text, description: "Base64 encoded SHA1 hash of certificate populated at run-time."), + }, + symbolIdIsPrimaryKey: false + ); + + public static readonly TableDefinition IIsWebSiteCertificates = new TableDefinition( + "IIsWebSiteCertificates", + IisSymbolDefinitions.IIsWebSiteCertificates, + new[] + { + new ColumnDefinition("Web_", ColumnType.String, 72, primaryKey: true, nullable: false, ColumnCategory.Identifier, keyTable: "IIsWebSite", keyColumn: 1, description: "The index into the IIsWebSite table.", modularizeType: ColumnModularizeType.Column), + new ColumnDefinition("Certificate_", ColumnType.String, 72, primaryKey: true, nullable: false, ColumnCategory.Text, keyTable: "Certificate", keyColumn: 1, description: "The index into the Certificate table.", modularizeType: ColumnModularizeType.Column), + }, + symbolIdIsPrimaryKey: false + ); + + public static readonly TableDefinition IIsAppPool = new TableDefinition( + "IIsAppPool", + IisSymbolDefinitions.IIsAppPool, + new[] + { + new ColumnDefinition("AppPool", ColumnType.String, 72, primaryKey: true, nullable: false, ColumnCategory.Identifier, description: "Primary key, non-localized token for apppool", modularizeType: ColumnModularizeType.Column), + new ColumnDefinition("Name", ColumnType.String, 72, primaryKey: false, nullable: false, ColumnCategory.Formatted, description: "Name to be used for the IIs AppPool.", modularizeType: ColumnModularizeType.Property), + new ColumnDefinition("Component_", ColumnType.String, 72, primaryKey: false, nullable: true, ColumnCategory.Identifier, keyTable: "Component", keyColumn: 1, description: "Foreign key referencing Component that controls the app pool", modularizeType: ColumnModularizeType.Column), + new ColumnDefinition("Attributes", ColumnType.Number, 2, primaryKey: false, nullable: false, ColumnCategory.Unknown, description: "Attributes of the AppPool"), + new ColumnDefinition("User_", ColumnType.String, 72, primaryKey: false, nullable: true, ColumnCategory.Identifier, keyTable: "User", keyColumn: 1, description: "User account to run the app pool as", modularizeType: ColumnModularizeType.Column), + new ColumnDefinition("RecycleMinutes", ColumnType.Number, 2, primaryKey: false, nullable: true, ColumnCategory.Unknown, description: "Number of minutes between recycling app pool"), + new ColumnDefinition("RecycleRequests", ColumnType.Number, 2, primaryKey: false, nullable: true, ColumnCategory.Unknown, description: "Number of requests between recycling app pool"), + new ColumnDefinition("RecycleTimes", ColumnType.String, 72, primaryKey: false, nullable: true, ColumnCategory.Unknown, description: "Times to recycle app pool (comma delimited - i.e. 1:45,13:30)"), + new ColumnDefinition("IdleTimeout", ColumnType.Number, 2, primaryKey: false, nullable: true, ColumnCategory.Unknown, description: "Amount of idle time before shutting down"), + new ColumnDefinition("QueueLimit", ColumnType.Number, 2, primaryKey: false, nullable: true, ColumnCategory.Unknown, description: "Reject requests after queue gets how large"), + new ColumnDefinition("CPUMon", ColumnType.String, 72, primaryKey: false, nullable: true, ColumnCategory.Unknown, description: "CPUMon is a comma delimeted list of the following format: ,,. The values for Action are 1 (Shutdown) and 0 (No Action)."), + new ColumnDefinition("MaxProc", ColumnType.Number, 2, primaryKey: false, nullable: true, ColumnCategory.Unknown, description: "Maximum number of processes to use"), + new ColumnDefinition("VirtualMemory", ColumnType.Number, 4, primaryKey: false, nullable: true, ColumnCategory.Unknown, description: "Amount of virtual memory (in KB) that a worker process can use before the worker process recycles. The maximum value supported for this field is 4,294,967 KB."), + new ColumnDefinition("PrivateMemory", ColumnType.Number, 4, primaryKey: false, nullable: true, ColumnCategory.Unknown, description: "Amount of private memory (in KB) that a worker process can use before the worker process recycles. The maximum value supported for this field is 4,294,967 KB."), + new ColumnDefinition("ManagedRuntimeVersion", ColumnType.String, 72, primaryKey: false, nullable: true, ColumnCategory.Unknown, description: "Specifies the .NET Framework version to be used by the application pool."), + new ColumnDefinition("ManagedPipelineMode", ColumnType.String, 72, primaryKey: false, nullable: true, ColumnCategory.Unknown, description: "Specifies the request-processing mode that is used to process requests for managed content."), + }, + symbolIdIsPrimaryKey: true + ); + + public static readonly TableDefinition IIsMimeMap = new TableDefinition( + "IIsMimeMap", + IisSymbolDefinitions.IIsMimeMap, + new[] + { + new ColumnDefinition("MimeMap", ColumnType.String, 72, primaryKey: true, nullable: false, ColumnCategory.Identifier, description: "Primary key, non-localized token for Mime Map definitions", modularizeType: ColumnModularizeType.Column), + new ColumnDefinition("ParentType", ColumnType.Number, 2, primaryKey: false, nullable: false, ColumnCategory.Unknown, possibilities: "1;2", description: "Type of parent: 1=vdir 2=website"), + new ColumnDefinition("ParentValue", ColumnType.String, 72, primaryKey: false, nullable: false, ColumnCategory.Identifier, description: "Name of the parent value.", modularizeType: ColumnModularizeType.Column), + new ColumnDefinition("MimeType", ColumnType.String, 72, primaryKey: false, nullable: false, ColumnCategory.Text, description: "Mime-type covered by the MimeMap."), + new ColumnDefinition("Extension", ColumnType.String, 72, primaryKey: false, nullable: false, ColumnCategory.Text, description: "Extension covered by the MimeMap."), + }, + symbolIdIsPrimaryKey: true + ); + + public static readonly TableDefinition IIsProperty = new TableDefinition( + "IIsProperty", + IisSymbolDefinitions.IIsProperty, + new[] + { + new ColumnDefinition("Property", ColumnType.String, 72, primaryKey: true, nullable: false, ColumnCategory.Identifier, description: "Unique name of the IIsProperty"), + new ColumnDefinition("Component_", ColumnType.String, 72, primaryKey: false, nullable: false, ColumnCategory.Identifier, keyTable: "Component", keyColumn: 1, description: "Component that the property is linked to", modularizeType: ColumnModularizeType.Column), + new ColumnDefinition("Attributes", ColumnType.Number, 2, primaryKey: false, nullable: false, ColumnCategory.Unknown, description: "Attributes of the IIsProperty (unused)"), + new ColumnDefinition("Value", ColumnType.String, 72, primaryKey: false, nullable: true, ColumnCategory.Unknown, description: "Value of the IIsProperty"), + }, + symbolIdIsPrimaryKey: true + ); + + public static readonly TableDefinition IIsWebDirProperties = new TableDefinition( + "IIsWebDirProperties", + IisSymbolDefinitions.IIsWebDirProperties, + new[] + { + new ColumnDefinition("DirProperties", ColumnType.String, 72, primaryKey: true, nullable: false, ColumnCategory.Identifier, description: "Primary key, non-localized token for Web Properties", modularizeType: ColumnModularizeType.Column), + new ColumnDefinition("Access", ColumnType.Number, 2, primaryKey: false, nullable: true, ColumnCategory.Unknown, description: "Access rights to the web server"), + new ColumnDefinition("Authorization", ColumnType.Number, 2, primaryKey: false, nullable: true, ColumnCategory.Unknown, description: "Authorization policy to web server (anonymous access, NTLM, etc)"), + new ColumnDefinition("AnonymousUser_", ColumnType.String, 72, primaryKey: false, nullable: true, ColumnCategory.Identifier, keyTable: "User", keyColumn: 1, description: "Foreign key, User used to log into database", modularizeType: ColumnModularizeType.Column), + new ColumnDefinition("IIsControlledPassword", ColumnType.Number, 2, primaryKey: false, nullable: true, ColumnCategory.Unknown, possibilities: "0;1", description: "Specifies whether IIs is allowed to set the AnonymousUser_ password"), + new ColumnDefinition("LogVisits", ColumnType.Number, 2, primaryKey: false, nullable: true, ColumnCategory.Unknown, possibilities: "0;1", description: "Specifies whether IIs tracks all access to the directory"), + new ColumnDefinition("Index", ColumnType.Number, 2, primaryKey: false, nullable: true, ColumnCategory.Unknown, possibilities: "0;1", description: "Specifies whether IIs searches the directory"), + new ColumnDefinition("DefaultDoc", ColumnType.String, 255, primaryKey: false, nullable: true, ColumnCategory.Text, description: "Comma delimited list of file names to act as a default document"), + new ColumnDefinition("AspDetailedError", ColumnType.Number, 2, primaryKey: false, nullable: true, ColumnCategory.Unknown, possibilities: "0;1", description: "Specifies whether detailed ASP errors are sent to browser"), + new ColumnDefinition("HttpExpires", ColumnType.String, 255, primaryKey: false, nullable: true, ColumnCategory.Text, description: "Value to set the HttpExpires attribute to for a Web Dir in the metabase"), + new ColumnDefinition("CacheControlMaxAge", ColumnType.Number, 4, primaryKey: false, nullable: true, ColumnCategory.Unknown, description: "Integer value specifying the cache control maximum age value."), + new ColumnDefinition("CacheControlCustom", ColumnType.String, 255, primaryKey: false, nullable: true, ColumnCategory.Text, description: "Custom HTTP 1.1 cache control directives."), + new ColumnDefinition("NoCustomError", ColumnType.Number, 2, primaryKey: false, nullable: true, ColumnCategory.Unknown, possibilities: "0;1", description: "Specifies whether IIs will return custom errors for this directory."), + new ColumnDefinition("AccessSSLFlags", ColumnType.Number, 2, primaryKey: false, nullable: true, ColumnCategory.Unknown, description: "Specifies AccessSSLFlags IIS metabase property."), + new ColumnDefinition("AuthenticationProviders", ColumnType.String, 255, primaryKey: false, nullable: true, ColumnCategory.Text, description: "Comma delimited list, in order of precedence, of Windows authentication providers that IIS will attempt to use: NTLM, Kerberos, Negotiate, and others."), + }, + symbolIdIsPrimaryKey: true + ); + + public static readonly TableDefinition IIsWebAddress = new TableDefinition( + "IIsWebAddress", + IisSymbolDefinitions.IIsWebAddress, + new[] + { + new ColumnDefinition("Address", ColumnType.String, 72, primaryKey: true, nullable: false, ColumnCategory.Identifier, description: "Primary key, non-localized token", modularizeType: ColumnModularizeType.Column), + new ColumnDefinition("Web_", ColumnType.String, 72, primaryKey: false, nullable: false, ColumnCategory.Identifier, keyTable: "IIsWebSite", keyColumn: 1, description: "Foreign key referencing Web that uses the address.", modularizeType: ColumnModularizeType.Column), + new ColumnDefinition("IP", ColumnType.String, 255, primaryKey: false, nullable: true, ColumnCategory.Text, description: "String representing IP address (#.#.#.#) or NT machine name (fooserver)", modularizeType: ColumnModularizeType.Property), + new ColumnDefinition("Port", ColumnType.String, 72, primaryKey: false, nullable: false, ColumnCategory.Formatted, description: "Port web site listens on", modularizeType: ColumnModularizeType.Property), + new ColumnDefinition("Header", ColumnType.String, 255, primaryKey: false, nullable: true, ColumnCategory.Text, description: "Special header information for the web site"), + new ColumnDefinition("Secure", ColumnType.Number, 2, primaryKey: false, nullable: true, ColumnCategory.Unknown, possibilities: "0;1", description: "Specifies whether SSL is used to communicate with web site"), + }, + symbolIdIsPrimaryKey: true + ); + + public static readonly TableDefinition IIsWebSite = new TableDefinition( + "IIsWebSite", + IisSymbolDefinitions.IIsWebSite, + new[] + { + new ColumnDefinition("Web", ColumnType.String, 72, primaryKey: true, nullable: false, ColumnCategory.Identifier, description: "Primary key, non-localized token", modularizeType: ColumnModularizeType.Column), + new ColumnDefinition("Component_", ColumnType.String, 72, primaryKey: false, nullable: true, ColumnCategory.Identifier, keyTable: "Component", keyColumn: 1, description: "Foreign key referencing Component that controls the web site", modularizeType: ColumnModularizeType.Column), + new ColumnDefinition("Description", ColumnType.String, 255, primaryKey: false, nullable: true, ColumnCategory.Formatted, description: "Description displayed in IIS MMC applet"), + new ColumnDefinition("ConnectionTimeout", ColumnType.Number, 2, primaryKey: false, nullable: true, ColumnCategory.Unknown, description: "Time connection is maintained without activity (in seconds)"), + new ColumnDefinition("Directory_", ColumnType.String, 72, primaryKey: false, nullable: true, ColumnCategory.Identifier, keyTable: "Directory", keyColumn: 1, description: "Foreign key referencing directory that the web site points at", modularizeType: ColumnModularizeType.Column), + new ColumnDefinition("State", ColumnType.Number, 2, primaryKey: false, nullable: true, ColumnCategory.Unknown, possibilities: "0;1;2", description: "Sets intial state of web site"), + new ColumnDefinition("Attributes", ColumnType.Number, 2, primaryKey: false, nullable: true, ColumnCategory.Unknown, possibilities: "2", description: "Control the install behavior of web site"), + new ColumnDefinition("KeyAddress_", ColumnType.String, 72, primaryKey: false, nullable: false, ColumnCategory.Identifier, keyTable: "IIsWebAddress", keyColumn: 1, description: "Foreign key referencing primary address for the web site", modularizeType: ColumnModularizeType.Column), + new ColumnDefinition("DirProperties_", ColumnType.String, 72, primaryKey: false, nullable: true, ColumnCategory.Identifier, keyTable: "IIsWebDirProperties", keyColumn: 1, description: "Foreign key referencing possible security information for the web site", modularizeType: ColumnModularizeType.Column), + new ColumnDefinition("Application_", ColumnType.String, 72, primaryKey: false, nullable: true, ColumnCategory.Identifier, keyTable: "IIsWebApplication", keyColumn: 1, description: "Foreign key referencing possible ASP application for the web site.", modularizeType: ColumnModularizeType.Column), + new ColumnDefinition("Sequence", ColumnType.Number, 2, primaryKey: false, nullable: true, ColumnCategory.Unknown, description: "Allows ordering of web site install"), + new ColumnDefinition("Log_", ColumnType.String, 72, primaryKey: false, nullable: true, ColumnCategory.Unknown, keyTable: "IIsWebLog", keyColumn: 1, description: "Foreign key reference to IIsWebLog data", modularizeType: ColumnModularizeType.Column), + new ColumnDefinition("Id", ColumnType.String, 74, primaryKey: false, nullable: true, ColumnCategory.Formatted, description: "Optional number or formatted value that resolves to number that acts as the WebSite Id."), + }, + symbolIdIsPrimaryKey: true + ); + + public static readonly TableDefinition IIsWebApplication = new TableDefinition( + "IIsWebApplication", + IisSymbolDefinitions.IIsWebApplication, + new[] + { + new ColumnDefinition("Application", ColumnType.String, 72, primaryKey: true, nullable: false, ColumnCategory.Identifier, description: "Primary key, non-localized token for ASP Application", modularizeType: ColumnModularizeType.Column), + new ColumnDefinition("Name", ColumnType.Localized, 255, primaryKey: false, nullable: false, ColumnCategory.Formatted, description: "Name of application in IIS MMC applet", modularizeType: ColumnModularizeType.Property), + new ColumnDefinition("Isolation", ColumnType.Number, 2, primaryKey: false, nullable: false, ColumnCategory.Unknown, possibilities: "0;1;2", description: "Isolation level for ASP Application: 0 == Low, 2 == Medium, 1 == High"), + new ColumnDefinition("AllowSessions", ColumnType.Number, 2, primaryKey: false, nullable: true, ColumnCategory.Unknown, possibilities: "0;1", description: "Specifies whether application may maintain session state"), + new ColumnDefinition("SessionTimeout", ColumnType.Number, 2, primaryKey: false, nullable: true, ColumnCategory.Unknown, description: "Time session state is maintained without user interaction"), + new ColumnDefinition("Buffer", ColumnType.Number, 2, primaryKey: false, nullable: true, ColumnCategory.Unknown, possibilities: "0;1", description: "Specifies whether application buffers its output"), + new ColumnDefinition("ParentPaths", ColumnType.Number, 2, primaryKey: false, nullable: true, ColumnCategory.Unknown, possibilities: "0;1", description: "What is this for anyway?"), + new ColumnDefinition("DefaultScript", ColumnType.String, 26, primaryKey: false, nullable: true, ColumnCategory.Text, possibilities: "VBScript;JScript", description: "Default scripting language for ASP applications"), + new ColumnDefinition("ScriptTimeout", ColumnType.Number, 2, primaryKey: false, nullable: true, ColumnCategory.Unknown, description: "Time ASP application page is permitted to process"), + new ColumnDefinition("ServerDebugging", ColumnType.Number, 2, primaryKey: false, nullable: true, ColumnCategory.Unknown, possibilities: "0;1", description: "Specifies whether to allow ASP server-side script debugging"), + new ColumnDefinition("ClientDebugging", ColumnType.Number, 2, primaryKey: false, nullable: true, ColumnCategory.Unknown, possibilities: "0;1", description: "Specifies whether to allow ASP client-side script debugging"), + new ColumnDefinition("AppPool_", ColumnType.String, 72, primaryKey: false, nullable: true, ColumnCategory.Identifier, keyTable: "IIsAppPool", keyColumn: 1, description: "App Pool this application should run under", modularizeType: ColumnModularizeType.Column), + }, + symbolIdIsPrimaryKey: true + ); + + public static readonly TableDefinition IIsWebApplicationExtension = new TableDefinition( + "IIsWebApplicationExtension", + IisSymbolDefinitions.IIsWebApplicationExtension, + new[] + { + new ColumnDefinition("Application_", ColumnType.String, 72, primaryKey: true, nullable: false, ColumnCategory.Identifier, keyTable: "IIsWebApplication", keyColumn: 1, description: "Foreign key referencing possible ASP application for the web site", modularizeType: ColumnModularizeType.Column), + new ColumnDefinition("Extension", ColumnType.String, 255, primaryKey: true, nullable: true, ColumnCategory.Text, description: "Primary key, Extension that should be registered for this ASP application"), + new ColumnDefinition("Verbs", ColumnType.String, 255, primaryKey: false, nullable: true, ColumnCategory.Text, description: "Comma delimited list of HTTP verbs the extension should be registered with"), + new ColumnDefinition("Executable", ColumnType.String, 255, primaryKey: false, nullable: false, ColumnCategory.Formatted, description: "Path to extension (usually file property: [#file])", modularizeType: ColumnModularizeType.Property), + new ColumnDefinition("Attributes", ColumnType.Number, 2, primaryKey: false, nullable: true, ColumnCategory.Unknown, possibilities: "1;4;5", description: "Attributes for extension: 1 == Script, 4 == Check Path Info"), + }, + symbolIdIsPrimaryKey: false + ); + + public static readonly TableDefinition IIsFilter = new TableDefinition( + "IIsFilter", + IisSymbolDefinitions.IIsFilter, + new[] + { + new ColumnDefinition("Filter", ColumnType.String, 72, primaryKey: true, nullable: false, ColumnCategory.Identifier, description: "Primary key, non-localized token", modularizeType: ColumnModularizeType.Column), + new ColumnDefinition("Name", ColumnType.String, 72, primaryKey: false, nullable: false, ColumnCategory.Unknown, description: "Name of the ISAPI Filter in IIS"), + new ColumnDefinition("Component_", ColumnType.String, 72, primaryKey: false, nullable: false, ColumnCategory.Identifier, keyTable: "Component", keyColumn: 1, description: "Foreign key referencing Component that controls the filter", modularizeType: ColumnModularizeType.Column), + new ColumnDefinition("Path", ColumnType.String, 255, primaryKey: false, nullable: true, ColumnCategory.Formatted, description: "Path to filter (usually file property: [#file])", modularizeType: ColumnModularizeType.Property), + new ColumnDefinition("Web_", ColumnType.String, 72, primaryKey: false, nullable: true, ColumnCategory.Identifier, keyTable: "IIsWebSite", keyColumn: 1, description: "Foreign key referencing web site that loads the filter (NULL == global filter", modularizeType: ColumnModularizeType.Column), + new ColumnDefinition("Description", ColumnType.String, 255, primaryKey: false, nullable: true, ColumnCategory.Formatted, description: "Description displayed in IIS MMC applet"), + new ColumnDefinition("Flags", ColumnType.Number, 4, primaryKey: false, nullable: false, ColumnCategory.Unknown, minValue: 0, maxValue: 2147483647, description: "What do all these numbers mean?"), + new ColumnDefinition("LoadOrder", ColumnType.Number, 2, primaryKey: false, nullable: true, ColumnCategory.Unknown, description: "-1 == last in order, 0 == first in order, # == place in order"), + }, + symbolIdIsPrimaryKey: true + ); + + public static readonly TableDefinition IIsWebDir = new TableDefinition( + "IIsWebDir", + IisSymbolDefinitions.IIsWebDir, + new[] + { + new ColumnDefinition("WebDir", ColumnType.String, 72, primaryKey: true, nullable: false, ColumnCategory.Identifier, description: "Primary key, non-localized token", modularizeType: ColumnModularizeType.Column), + new ColumnDefinition("Component_", ColumnType.String, 72, primaryKey: false, nullable: false, ColumnCategory.Identifier, keyTable: "Component", keyColumn: 1, description: "Foreign key referencing Component that controls the virtual directory", modularizeType: ColumnModularizeType.Column), + new ColumnDefinition("Web_", ColumnType.String, 72, primaryKey: false, nullable: false, ColumnCategory.Identifier, keyTable: "IIsWebSite", keyColumn: 1, description: "Foreign key referencing web site that controls the virtual directory", modularizeType: ColumnModularizeType.Column), + new ColumnDefinition("Path", ColumnType.String, 255, primaryKey: false, nullable: false, ColumnCategory.Formatted, description: "Name of web directory displayed in IIS MMC applet", modularizeType: ColumnModularizeType.Property), + new ColumnDefinition("DirProperties_", ColumnType.String, 72, primaryKey: false, nullable: true, ColumnCategory.Identifier, keyTable: "IIsWebDirProperties", keyColumn: 1, description: "Foreign key referencing possible security information for the virtual directory", modularizeType: ColumnModularizeType.Column), + new ColumnDefinition("Application_", ColumnType.String, 72, primaryKey: false, nullable: true, ColumnCategory.Identifier, keyTable: "IIsWebApplication", keyColumn: 1, description: "Foreign key referencing possible ASP application for the virtual directory. This column is currently unused, but maintained for compatibility reasons.", modularizeType: ColumnModularizeType.Column), + }, + symbolIdIsPrimaryKey: true + ); + + public static readonly TableDefinition IIsWebError = new TableDefinition( + "IIsWebError", + IisSymbolDefinitions.IIsWebError, + new[] + { + new ColumnDefinition("ErrorCode", ColumnType.Number, 2, primaryKey: true, nullable: false, ColumnCategory.Unknown, minValue: 400, maxValue: 599, description: "HTTP status code indicating error."), + new ColumnDefinition("SubCode", ColumnType.Number, 4, primaryKey: true, nullable: false, ColumnCategory.Unknown, description: "HTTP sub-status code indicating error."), + new ColumnDefinition("ParentType", ColumnType.Number, 2, primaryKey: true, nullable: false, ColumnCategory.Unknown, possibilities: "1;2", description: "Type of parent: 1=vdir, 2=web"), + new ColumnDefinition("ParentValue", ColumnType.String, 72, primaryKey: true, nullable: false, ColumnCategory.Identifier, description: "Name of the parent value.", modularizeType: ColumnModularizeType.Column), + new ColumnDefinition("File", ColumnType.String, 255, primaryKey: false, nullable: true, ColumnCategory.Formatted, description: "Path to file for this custom error (usually file property: [#file]). Must be null if URL is not null."), + new ColumnDefinition("URL", ColumnType.String, 255, primaryKey: false, nullable: true, ColumnCategory.Formatted, description: "URL for this custom error. Must be null if File is not null."), + }, + symbolIdIsPrimaryKey: false + ); + + public static readonly TableDefinition IIsHttpHeader = new TableDefinition( + "IIsHttpHeader", + IisSymbolDefinitions.IIsHttpHeader, + new[] + { + new ColumnDefinition("HttpHeader", ColumnType.String, 72, primaryKey: true, nullable: false, ColumnCategory.Identifier, description: "Primary key, non-localized token", modularizeType: ColumnModularizeType.Column), + new ColumnDefinition("ParentType", ColumnType.Number, 2, primaryKey: true, nullable: false, ColumnCategory.Unknown, possibilities: "1;2", description: "Type of parent: 1=vdir, 2=web"), + new ColumnDefinition("ParentValue", ColumnType.String, 72, primaryKey: true, nullable: false, ColumnCategory.Identifier, description: "Name of the parent value.", modularizeType: ColumnModularizeType.Column), + new ColumnDefinition("Name", ColumnType.String, 255, primaryKey: false, nullable: false, ColumnCategory.Text, description: "Name of the HTTP Header"), + new ColumnDefinition("Value", ColumnType.String, 255, primaryKey: false, nullable: true, ColumnCategory.Formatted, description: "URL for this custom error. Must be null if File is not null."), + new ColumnDefinition("Attributes", ColumnType.Number, 2, primaryKey: false, nullable: false, ColumnCategory.Unknown, minValue: 0, maxValue: 0, description: "Attributes for HTTP Header: none"), + new ColumnDefinition("Sequence", ColumnType.Number, 2, primaryKey: false, nullable: true, ColumnCategory.Unknown, description: "Order to add the HTTP Headers."), + }, + symbolIdIsPrimaryKey: false + ); + + public static readonly TableDefinition IIsWebServiceExtension = new TableDefinition( + "IIsWebServiceExtension", + IisSymbolDefinitions.IIsWebServiceExtension, + new[] + { + new ColumnDefinition("WebServiceExtension", ColumnType.String, 72, primaryKey: true, nullable: false, ColumnCategory.Identifier, description: "Primary key, non-localized token", modularizeType: ColumnModularizeType.Column), + new ColumnDefinition("Component_", ColumnType.String, 72, primaryKey: false, nullable: false, ColumnCategory.Identifier, keyTable: "Component", keyColumn: 1, description: "Foreign key referencing Component that controls the WebServiceExtension handler", modularizeType: ColumnModularizeType.Column), + new ColumnDefinition("File", ColumnType.String, 255, primaryKey: false, nullable: false, ColumnCategory.Formatted, description: "Path to handler (usually file property: [#file])", modularizeType: ColumnModularizeType.Property), + new ColumnDefinition("Description", ColumnType.Localized, 255, primaryKey: false, nullable: true, ColumnCategory.Formatted, description: "Description displayed in WebServiceExtension Wizard", modularizeType: ColumnModularizeType.Property), + new ColumnDefinition("Group", ColumnType.String, 255, primaryKey: false, nullable: true, ColumnCategory.Formatted, description: "String used to identify groups of extensions.", modularizeType: ColumnModularizeType.Property), + new ColumnDefinition("Attributes", ColumnType.Number, 1, primaryKey: false, nullable: false, ColumnCategory.Unknown, minValue: 0, maxValue: 3, description: "Attributes for WebServiceExtension: 1 = Allow, 2 = UIDeletable"), + }, + symbolIdIsPrimaryKey: true + ); + + public static readonly TableDefinition IIsWebVirtualDir = new TableDefinition( + "IIsWebVirtualDir", + IisSymbolDefinitions.IIsWebVirtualDir, + new[] + { + new ColumnDefinition("VirtualDir", ColumnType.String, 72, primaryKey: true, nullable: false, ColumnCategory.Identifier, description: "Primary key, non-localized token", modularizeType: ColumnModularizeType.Column), + new ColumnDefinition("Component_", ColumnType.String, 72, primaryKey: false, nullable: false, ColumnCategory.Identifier, keyTable: "Component", keyColumn: 1, description: "Foreign key referencing Component that controls the virtual directory", modularizeType: ColumnModularizeType.Column), + new ColumnDefinition("Web_", ColumnType.String, 72, primaryKey: false, nullable: false, ColumnCategory.Identifier, keyTable: "IIsWebSite", keyColumn: 1, description: "Foreign key referencing web site that controls the virtual directory", modularizeType: ColumnModularizeType.Column), + new ColumnDefinition("Alias", ColumnType.String, 255, primaryKey: false, nullable: false, ColumnCategory.Formatted, description: "Name of virtual directory displayed in IIS MMC applet", modularizeType: ColumnModularizeType.Property), + new ColumnDefinition("Directory_", ColumnType.String, 72, primaryKey: false, nullable: false, ColumnCategory.Identifier, keyTable: "Directory", keyColumn: 1, description: "Foreign key referencing directory that the virtual directory points at", modularizeType: ColumnModularizeType.Column), + new ColumnDefinition("DirProperties_", ColumnType.String, 72, primaryKey: false, nullable: true, ColumnCategory.Identifier, keyTable: "IIsWebDirProperties", keyColumn: 1, description: "Foreign key referencing possible security information for the virtual directory", modularizeType: ColumnModularizeType.Column), + new ColumnDefinition("Application_", ColumnType.String, 72, primaryKey: false, nullable: true, ColumnCategory.Identifier, keyTable: "IIsWebApplication", keyColumn: 1, description: "Foreign key referencing possible ASP application for the virtual directory", modularizeType: ColumnModularizeType.Column), + }, + symbolIdIsPrimaryKey: true + ); + + public static readonly TableDefinition IIsWebLog = new TableDefinition( + "IIsWebLog", + IisSymbolDefinitions.IIsWebLog, + new[] + { + new ColumnDefinition("Log", ColumnType.String, 72, primaryKey: true, nullable: false, ColumnCategory.Identifier, description: "Primary key, non-localized token", modularizeType: ColumnModularizeType.Column), + new ColumnDefinition("Format", ColumnType.String, 255, primaryKey: false, nullable: false, ColumnCategory.Text, description: "Type of log format"), + }, + symbolIdIsPrimaryKey: true + ); + + public static readonly TableDefinition[] All = new[] + { + Certificate, + CertificateHash, + IIsWebSiteCertificates, + IIsAppPool, + IIsMimeMap, + IIsProperty, + IIsWebDirProperties, + IIsWebAddress, + IIsWebSite, + IIsWebApplication, + IIsWebApplicationExtension, + IIsFilter, + IIsWebDir, + IIsWebError, + IIsHttpHeader, + IIsWebServiceExtension, + IIsWebVirtualDir, + IIsWebLog, + }; + } +} diff --git a/src/ext/Iis/wixext/IisWindowsInstallerBackendBinderExtension.cs b/src/ext/Iis/wixext/IisWindowsInstallerBackendBinderExtension.cs new file mode 100644 index 00000000..a61cbad6 --- /dev/null +++ b/src/ext/Iis/wixext/IisWindowsInstallerBackendBinderExtension.cs @@ -0,0 +1,13 @@ +// 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. + +namespace WixToolset.Iis +{ + using System.Collections.Generic; + using WixToolset.Data.WindowsInstaller; + using WixToolset.Extensibility; + + public class IisWindowsInstallerBackendBinderExtension : BaseWindowsInstallerBackendBinderExtension + { + public override IReadOnlyCollection TableDefinitions => IisTableDefinitions.All; + } +} diff --git a/src/ext/Iis/wixext/Symbols/CertificateHashSymbol.cs b/src/ext/Iis/wixext/Symbols/CertificateHashSymbol.cs new file mode 100644 index 00000000..866d474c --- /dev/null +++ b/src/ext/Iis/wixext/Symbols/CertificateHashSymbol.cs @@ -0,0 +1,55 @@ +// 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. + +namespace WixToolset.Iis +{ + using WixToolset.Data; + using WixToolset.Iis.Symbols; + + public static partial class IisSymbolDefinitions + { + public static readonly IntermediateSymbolDefinition CertificateHash = new IntermediateSymbolDefinition( + IisSymbolDefinitionType.CertificateHash.ToString(), + new[] + { + new IntermediateFieldDefinition(nameof(CertificateHashSymbolFields.CertificateRef), IntermediateFieldType.String), + new IntermediateFieldDefinition(nameof(CertificateHashSymbolFields.Hash), IntermediateFieldType.String), + }, + typeof(CertificateHashSymbol)); + } +} + +namespace WixToolset.Iis.Symbols +{ + using WixToolset.Data; + + public enum CertificateHashSymbolFields + { + CertificateRef, + Hash, + } + + public class CertificateHashSymbol : IntermediateSymbol + { + public CertificateHashSymbol() : base(IisSymbolDefinitions.CertificateHash, null, null) + { + } + + public CertificateHashSymbol(SourceLineNumber sourceLineNumber, Identifier id = null) : base(IisSymbolDefinitions.CertificateHash, sourceLineNumber, id) + { + } + + public IntermediateField this[CertificateHashSymbolFields index] => this.Fields[(int)index]; + + public string CertificateRef + { + get => this.Fields[(int)CertificateHashSymbolFields.CertificateRef].AsString(); + set => this.Set((int)CertificateHashSymbolFields.CertificateRef, value); + } + + public string Hash + { + get => this.Fields[(int)CertificateHashSymbolFields.Hash].AsString(); + set => this.Set((int)CertificateHashSymbolFields.Hash, value); + } + } +} \ No newline at end of file diff --git a/src/ext/Iis/wixext/Symbols/CertificateSymbol.cs b/src/ext/Iis/wixext/Symbols/CertificateSymbol.cs new file mode 100644 index 00000000..b80b6ba4 --- /dev/null +++ b/src/ext/Iis/wixext/Symbols/CertificateSymbol.cs @@ -0,0 +1,103 @@ +// 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. + +namespace WixToolset.Iis +{ + using WixToolset.Data; + using WixToolset.Iis.Symbols; + + public static partial class IisSymbolDefinitions + { + public static readonly IntermediateSymbolDefinition Certificate = new IntermediateSymbolDefinition( + IisSymbolDefinitionType.Certificate.ToString(), + new[] + { + new IntermediateFieldDefinition(nameof(CertificateSymbolFields.ComponentRef), IntermediateFieldType.String), + new IntermediateFieldDefinition(nameof(CertificateSymbolFields.Name), IntermediateFieldType.String), + new IntermediateFieldDefinition(nameof(CertificateSymbolFields.StoreLocation), IntermediateFieldType.Number), + new IntermediateFieldDefinition(nameof(CertificateSymbolFields.StoreName), IntermediateFieldType.String), + new IntermediateFieldDefinition(nameof(CertificateSymbolFields.Attributes), IntermediateFieldType.Number), + new IntermediateFieldDefinition(nameof(CertificateSymbolFields.BinaryRef), IntermediateFieldType.String), + new IntermediateFieldDefinition(nameof(CertificateSymbolFields.CertificatePath), IntermediateFieldType.String), + new IntermediateFieldDefinition(nameof(CertificateSymbolFields.PFXPassword), IntermediateFieldType.String), + }, + typeof(CertificateSymbol)); + } +} + +namespace WixToolset.Iis.Symbols +{ + using WixToolset.Data; + + public enum CertificateSymbolFields + { + ComponentRef, + Name, + StoreLocation, + StoreName, + Attributes, + BinaryRef, + CertificatePath, + PFXPassword, + } + + public class CertificateSymbol : IntermediateSymbol + { + public CertificateSymbol() : base(IisSymbolDefinitions.Certificate, null, null) + { + } + + public CertificateSymbol(SourceLineNumber sourceLineNumber, Identifier id = null) : base(IisSymbolDefinitions.Certificate, sourceLineNumber, id) + { + } + + public IntermediateField this[CertificateSymbolFields index] => this.Fields[(int)index]; + + public string ComponentRef + { + get => this.Fields[(int)CertificateSymbolFields.ComponentRef].AsString(); + set => this.Set((int)CertificateSymbolFields.ComponentRef, value); + } + + public string Name + { + get => this.Fields[(int)CertificateSymbolFields.Name].AsString(); + set => this.Set((int)CertificateSymbolFields.Name, value); + } + + public int StoreLocation + { + get => this.Fields[(int)CertificateSymbolFields.StoreLocation].AsNumber(); + set => this.Set((int)CertificateSymbolFields.StoreLocation, value); + } + + public string StoreName + { + get => this.Fields[(int)CertificateSymbolFields.StoreName].AsString(); + set => this.Set((int)CertificateSymbolFields.StoreName, value); + } + + public int Attributes + { + get => this.Fields[(int)CertificateSymbolFields.Attributes].AsNumber(); + set => this.Set((int)CertificateSymbolFields.Attributes, value); + } + + public string BinaryRef + { + get => this.Fields[(int)CertificateSymbolFields.BinaryRef].AsString(); + set => this.Set((int)CertificateSymbolFields.BinaryRef, value); + } + + public string CertificatePath + { + get => this.Fields[(int)CertificateSymbolFields.CertificatePath].AsString(); + set => this.Set((int)CertificateSymbolFields.CertificatePath, value); + } + + public string PFXPassword + { + get => this.Fields[(int)CertificateSymbolFields.PFXPassword].AsString(); + set => this.Set((int)CertificateSymbolFields.PFXPassword, value); + } + } +} \ No newline at end of file diff --git a/src/ext/Iis/wixext/Symbols/IIsAppPoolSymbol.cs b/src/ext/Iis/wixext/Symbols/IIsAppPoolSymbol.cs new file mode 100644 index 00000000..a6fab136 --- /dev/null +++ b/src/ext/Iis/wixext/Symbols/IIsAppPoolSymbol.cs @@ -0,0 +1,159 @@ +// 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. + +namespace WixToolset.Iis +{ + using WixToolset.Data; + using WixToolset.Iis.Symbols; + + public static partial class IisSymbolDefinitions + { + public static readonly IntermediateSymbolDefinition IIsAppPool = new IntermediateSymbolDefinition( + IisSymbolDefinitionType.IIsAppPool.ToString(), + new[] + { + new IntermediateFieldDefinition(nameof(IIsAppPoolSymbolFields.Name), IntermediateFieldType.String), + new IntermediateFieldDefinition(nameof(IIsAppPoolSymbolFields.ComponentRef), IntermediateFieldType.String), + new IntermediateFieldDefinition(nameof(IIsAppPoolSymbolFields.Attributes), IntermediateFieldType.Number), + new IntermediateFieldDefinition(nameof(IIsAppPoolSymbolFields.UserRef), IntermediateFieldType.String), + new IntermediateFieldDefinition(nameof(IIsAppPoolSymbolFields.RecycleMinutes), IntermediateFieldType.Number), + new IntermediateFieldDefinition(nameof(IIsAppPoolSymbolFields.RecycleRequests), IntermediateFieldType.Number), + new IntermediateFieldDefinition(nameof(IIsAppPoolSymbolFields.RecycleTimes), IntermediateFieldType.String), + new IntermediateFieldDefinition(nameof(IIsAppPoolSymbolFields.IdleTimeout), IntermediateFieldType.Number), + new IntermediateFieldDefinition(nameof(IIsAppPoolSymbolFields.QueueLimit), IntermediateFieldType.Number), + new IntermediateFieldDefinition(nameof(IIsAppPoolSymbolFields.CPUMon), IntermediateFieldType.String), + new IntermediateFieldDefinition(nameof(IIsAppPoolSymbolFields.MaxProc), IntermediateFieldType.Number), + new IntermediateFieldDefinition(nameof(IIsAppPoolSymbolFields.VirtualMemory), IntermediateFieldType.Number), + new IntermediateFieldDefinition(nameof(IIsAppPoolSymbolFields.PrivateMemory), IntermediateFieldType.Number), + new IntermediateFieldDefinition(nameof(IIsAppPoolSymbolFields.ManagedRuntimeVersion), IntermediateFieldType.String), + new IntermediateFieldDefinition(nameof(IIsAppPoolSymbolFields.ManagedPipelineMode), IntermediateFieldType.String), + }, + typeof(IIsAppPoolSymbol)); + } +} + +namespace WixToolset.Iis.Symbols +{ + using WixToolset.Data; + + public enum IIsAppPoolSymbolFields + { + Name, + ComponentRef, + Attributes, + UserRef, + RecycleMinutes, + RecycleRequests, + RecycleTimes, + IdleTimeout, + QueueLimit, + CPUMon, + MaxProc, + VirtualMemory, + PrivateMemory, + ManagedRuntimeVersion, + ManagedPipelineMode, + } + + public class IIsAppPoolSymbol : IntermediateSymbol + { + public IIsAppPoolSymbol() : base(IisSymbolDefinitions.IIsAppPool, null, null) + { + } + + public IIsAppPoolSymbol(SourceLineNumber sourceLineNumber, Identifier id = null) : base(IisSymbolDefinitions.IIsAppPool, sourceLineNumber, id) + { + } + + public IntermediateField this[IIsAppPoolSymbolFields index] => this.Fields[(int)index]; + + public string Name + { + get => this.Fields[(int)IIsAppPoolSymbolFields.Name].AsString(); + set => this.Set((int)IIsAppPoolSymbolFields.Name, value); + } + + public string ComponentRef + { + get => this.Fields[(int)IIsAppPoolSymbolFields.ComponentRef].AsString(); + set => this.Set((int)IIsAppPoolSymbolFields.ComponentRef, value); + } + + public int Attributes + { + get => this.Fields[(int)IIsAppPoolSymbolFields.Attributes].AsNumber(); + set => this.Set((int)IIsAppPoolSymbolFields.Attributes, value); + } + + public string UserRef + { + get => this.Fields[(int)IIsAppPoolSymbolFields.UserRef].AsString(); + set => this.Set((int)IIsAppPoolSymbolFields.UserRef, value); + } + + public int? RecycleMinutes + { + get => this.Fields[(int)IIsAppPoolSymbolFields.RecycleMinutes].AsNullableNumber(); + set => this.Set((int)IIsAppPoolSymbolFields.RecycleMinutes, value); + } + + public int? RecycleRequests + { + get => this.Fields[(int)IIsAppPoolSymbolFields.RecycleRequests].AsNullableNumber(); + set => this.Set((int)IIsAppPoolSymbolFields.RecycleRequests, value); + } + + public string RecycleTimes + { + get => this.Fields[(int)IIsAppPoolSymbolFields.RecycleTimes].AsString(); + set => this.Set((int)IIsAppPoolSymbolFields.RecycleTimes, value); + } + + public int? IdleTimeout + { + get => this.Fields[(int)IIsAppPoolSymbolFields.IdleTimeout].AsNullableNumber(); + set => this.Set((int)IIsAppPoolSymbolFields.IdleTimeout, value); + } + + public int? QueueLimit + { + get => this.Fields[(int)IIsAppPoolSymbolFields.QueueLimit].AsNullableNumber(); + set => this.Set((int)IIsAppPoolSymbolFields.QueueLimit, value); + } + + public string CPUMon + { + get => this.Fields[(int)IIsAppPoolSymbolFields.CPUMon].AsString(); + set => this.Set((int)IIsAppPoolSymbolFields.CPUMon, value); + } + + public int? MaxProc + { + get => this.Fields[(int)IIsAppPoolSymbolFields.MaxProc].AsNullableNumber(); + set => this.Set((int)IIsAppPoolSymbolFields.MaxProc, value); + } + + public int? VirtualMemory + { + get => this.Fields[(int)IIsAppPoolSymbolFields.VirtualMemory].AsNullableNumber(); + set => this.Set((int)IIsAppPoolSymbolFields.VirtualMemory, value); + } + + public int? PrivateMemory + { + get => this.Fields[(int)IIsAppPoolSymbolFields.PrivateMemory].AsNullableNumber(); + set => this.Set((int)IIsAppPoolSymbolFields.PrivateMemory, value); + } + + public string ManagedRuntimeVersion + { + get => this.Fields[(int)IIsAppPoolSymbolFields.ManagedRuntimeVersion].AsString(); + set => this.Set((int)IIsAppPoolSymbolFields.ManagedRuntimeVersion, value); + } + + public string ManagedPipelineMode + { + get => this.Fields[(int)IIsAppPoolSymbolFields.ManagedPipelineMode].AsString(); + set => this.Set((int)IIsAppPoolSymbolFields.ManagedPipelineMode, value); + } + } +} \ No newline at end of file diff --git a/src/ext/Iis/wixext/Symbols/IIsFilterSymbol.cs b/src/ext/Iis/wixext/Symbols/IIsFilterSymbol.cs new file mode 100644 index 00000000..618730bf --- /dev/null +++ b/src/ext/Iis/wixext/Symbols/IIsFilterSymbol.cs @@ -0,0 +1,95 @@ +// 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. + +namespace WixToolset.Iis +{ + using WixToolset.Data; + using WixToolset.Iis.Symbols; + + public static partial class IisSymbolDefinitions + { + public static readonly IntermediateSymbolDefinition IIsFilter = new IntermediateSymbolDefinition( + IisSymbolDefinitionType.IIsFilter.ToString(), + new[] + { + new IntermediateFieldDefinition(nameof(IIsFilterSymbolFields.Name), IntermediateFieldType.String), + new IntermediateFieldDefinition(nameof(IIsFilterSymbolFields.ComponentRef), IntermediateFieldType.String), + new IntermediateFieldDefinition(nameof(IIsFilterSymbolFields.Path), IntermediateFieldType.String), + new IntermediateFieldDefinition(nameof(IIsFilterSymbolFields.WebRef), IntermediateFieldType.String), + new IntermediateFieldDefinition(nameof(IIsFilterSymbolFields.Description), IntermediateFieldType.String), + new IntermediateFieldDefinition(nameof(IIsFilterSymbolFields.Flags), IntermediateFieldType.Number), + new IntermediateFieldDefinition(nameof(IIsFilterSymbolFields.LoadOrder), IntermediateFieldType.Number), + }, + typeof(IIsFilterSymbol)); + } +} + +namespace WixToolset.Iis.Symbols +{ + using WixToolset.Data; + + public enum IIsFilterSymbolFields + { + Name, + ComponentRef, + Path, + WebRef, + Description, + Flags, + LoadOrder, + } + + public class IIsFilterSymbol : IntermediateSymbol + { + public IIsFilterSymbol() : base(IisSymbolDefinitions.IIsFilter, null, null) + { + } + + public IIsFilterSymbol(SourceLineNumber sourceLineNumber, Identifier id = null) : base(IisSymbolDefinitions.IIsFilter, sourceLineNumber, id) + { + } + + public IntermediateField this[IIsFilterSymbolFields index] => this.Fields[(int)index]; + + public string Name + { + get => this.Fields[(int)IIsFilterSymbolFields.Name].AsString(); + set => this.Set((int)IIsFilterSymbolFields.Name, value); + } + + public string ComponentRef + { + get => this.Fields[(int)IIsFilterSymbolFields.ComponentRef].AsString(); + set => this.Set((int)IIsFilterSymbolFields.ComponentRef, value); + } + + public string Path + { + get => this.Fields[(int)IIsFilterSymbolFields.Path].AsString(); + set => this.Set((int)IIsFilterSymbolFields.Path, value); + } + + public string WebRef + { + get => this.Fields[(int)IIsFilterSymbolFields.WebRef].AsString(); + set => this.Set((int)IIsFilterSymbolFields.WebRef, value); + } + + public string Description + { + get => this.Fields[(int)IIsFilterSymbolFields.Description].AsString(); + set => this.Set((int)IIsFilterSymbolFields.Description, value); + } + + public int Flags + { + get => this.Fields[(int)IIsFilterSymbolFields.Flags].AsNumber(); + set => this.Set((int)IIsFilterSymbolFields.Flags, value); + } + + public int? LoadOrder + { + get => this.Fields[(int)IIsFilterSymbolFields.LoadOrder].AsNullableNumber(); + set => this.Set((int)IIsFilterSymbolFields.LoadOrder, value); + } + } +} \ No newline at end of file diff --git a/src/ext/Iis/wixext/Symbols/IIsHttpHeaderSymbol.cs b/src/ext/Iis/wixext/Symbols/IIsHttpHeaderSymbol.cs new file mode 100644 index 00000000..3ab2bf59 --- /dev/null +++ b/src/ext/Iis/wixext/Symbols/IIsHttpHeaderSymbol.cs @@ -0,0 +1,95 @@ +// 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. + +namespace WixToolset.Iis +{ + using WixToolset.Data; + using WixToolset.Iis.Symbols; + + public static partial class IisSymbolDefinitions + { + public static readonly IntermediateSymbolDefinition IIsHttpHeader = new IntermediateSymbolDefinition( + IisSymbolDefinitionType.IIsHttpHeader.ToString(), + new[] + { + new IntermediateFieldDefinition(nameof(IIsHttpHeaderSymbolFields.HttpHeader), IntermediateFieldType.String), + new IntermediateFieldDefinition(nameof(IIsHttpHeaderSymbolFields.ParentType), IntermediateFieldType.Number), + new IntermediateFieldDefinition(nameof(IIsHttpHeaderSymbolFields.ParentValue), IntermediateFieldType.String), + new IntermediateFieldDefinition(nameof(IIsHttpHeaderSymbolFields.Name), IntermediateFieldType.String), + new IntermediateFieldDefinition(nameof(IIsHttpHeaderSymbolFields.Value), IntermediateFieldType.String), + new IntermediateFieldDefinition(nameof(IIsHttpHeaderSymbolFields.Attributes), IntermediateFieldType.Number), + new IntermediateFieldDefinition(nameof(IIsHttpHeaderSymbolFields.Sequence), IntermediateFieldType.Number), + }, + typeof(IIsHttpHeaderSymbol)); + } +} + +namespace WixToolset.Iis.Symbols +{ + using WixToolset.Data; + + public enum IIsHttpHeaderSymbolFields + { + HttpHeader, + ParentType, + ParentValue, + Name, + Value, + Attributes, + Sequence, + } + + public class IIsHttpHeaderSymbol : IntermediateSymbol + { + public IIsHttpHeaderSymbol() : base(IisSymbolDefinitions.IIsHttpHeader, null, null) + { + } + + public IIsHttpHeaderSymbol(SourceLineNumber sourceLineNumber, Identifier id = null) : base(IisSymbolDefinitions.IIsHttpHeader, sourceLineNumber, id) + { + } + + public IntermediateField this[IIsHttpHeaderSymbolFields index] => this.Fields[(int)index]; + + public string HttpHeader + { + get => this.Fields[(int)IIsHttpHeaderSymbolFields.HttpHeader].AsString(); + set => this.Set((int)IIsHttpHeaderSymbolFields.HttpHeader, value); + } + + public int ParentType + { + get => this.Fields[(int)IIsHttpHeaderSymbolFields.ParentType].AsNumber(); + set => this.Set((int)IIsHttpHeaderSymbolFields.ParentType, value); + } + + public string ParentValue + { + get => this.Fields[(int)IIsHttpHeaderSymbolFields.ParentValue].AsString(); + set => this.Set((int)IIsHttpHeaderSymbolFields.ParentValue, value); + } + + public string Name + { + get => this.Fields[(int)IIsHttpHeaderSymbolFields.Name].AsString(); + set => this.Set((int)IIsHttpHeaderSymbolFields.Name, value); + } + + public string Value + { + get => this.Fields[(int)IIsHttpHeaderSymbolFields.Value].AsString(); + set => this.Set((int)IIsHttpHeaderSymbolFields.Value, value); + } + + public int Attributes + { + get => this.Fields[(int)IIsHttpHeaderSymbolFields.Attributes].AsNumber(); + set => this.Set((int)IIsHttpHeaderSymbolFields.Attributes, value); + } + + public int? Sequence + { + get => this.Fields[(int)IIsHttpHeaderSymbolFields.Sequence].AsNullableNumber(); + set => this.Set((int)IIsHttpHeaderSymbolFields.Sequence, value); + } + } +} \ No newline at end of file diff --git a/src/ext/Iis/wixext/Symbols/IIsMimeMapSymbol.cs b/src/ext/Iis/wixext/Symbols/IIsMimeMapSymbol.cs new file mode 100644 index 00000000..4af6f81c --- /dev/null +++ b/src/ext/Iis/wixext/Symbols/IIsMimeMapSymbol.cs @@ -0,0 +1,71 @@ +// 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. + +namespace WixToolset.Iis +{ + using WixToolset.Data; + using WixToolset.Iis.Symbols; + + public static partial class IisSymbolDefinitions + { + public static readonly IntermediateSymbolDefinition IIsMimeMap = new IntermediateSymbolDefinition( + IisSymbolDefinitionType.IIsMimeMap.ToString(), + new[] + { + new IntermediateFieldDefinition(nameof(IIsMimeMapSymbolFields.ParentType), IntermediateFieldType.Number), + new IntermediateFieldDefinition(nameof(IIsMimeMapSymbolFields.ParentValue), IntermediateFieldType.String), + new IntermediateFieldDefinition(nameof(IIsMimeMapSymbolFields.MimeType), IntermediateFieldType.String), + new IntermediateFieldDefinition(nameof(IIsMimeMapSymbolFields.Extension), IntermediateFieldType.String), + }, + typeof(IIsMimeMapSymbol)); + } +} + +namespace WixToolset.Iis.Symbols +{ + using WixToolset.Data; + + public enum IIsMimeMapSymbolFields + { + ParentType, + ParentValue, + MimeType, + Extension, + } + + public class IIsMimeMapSymbol : IntermediateSymbol + { + public IIsMimeMapSymbol() : base(IisSymbolDefinitions.IIsMimeMap, null, null) + { + } + + public IIsMimeMapSymbol(SourceLineNumber sourceLineNumber, Identifier id = null) : base(IisSymbolDefinitions.IIsMimeMap, sourceLineNumber, id) + { + } + + public IntermediateField this[IIsMimeMapSymbolFields index] => this.Fields[(int)index]; + + public int ParentType + { + get => this.Fields[(int)IIsMimeMapSymbolFields.ParentType].AsNumber(); + set => this.Set((int)IIsMimeMapSymbolFields.ParentType, value); + } + + public string ParentValue + { + get => this.Fields[(int)IIsMimeMapSymbolFields.ParentValue].AsString(); + set => this.Set((int)IIsMimeMapSymbolFields.ParentValue, value); + } + + public string MimeType + { + get => this.Fields[(int)IIsMimeMapSymbolFields.MimeType].AsString(); + set => this.Set((int)IIsMimeMapSymbolFields.MimeType, value); + } + + public string Extension + { + get => this.Fields[(int)IIsMimeMapSymbolFields.Extension].AsString(); + set => this.Set((int)IIsMimeMapSymbolFields.Extension, value); + } + } +} \ No newline at end of file diff --git a/src/ext/Iis/wixext/Symbols/IIsPropertySymbol.cs b/src/ext/Iis/wixext/Symbols/IIsPropertySymbol.cs new file mode 100644 index 00000000..9cf67014 --- /dev/null +++ b/src/ext/Iis/wixext/Symbols/IIsPropertySymbol.cs @@ -0,0 +1,63 @@ +// 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. + +namespace WixToolset.Iis +{ + using WixToolset.Data; + using WixToolset.Iis.Symbols; + + public static partial class IisSymbolDefinitions + { + public static readonly IntermediateSymbolDefinition IIsProperty = new IntermediateSymbolDefinition( + IisSymbolDefinitionType.IIsProperty.ToString(), + new[] + { + new IntermediateFieldDefinition(nameof(IIsPropertySymbolFields.ComponentRef), IntermediateFieldType.String), + new IntermediateFieldDefinition(nameof(IIsPropertySymbolFields.Attributes), IntermediateFieldType.Number), + new IntermediateFieldDefinition(nameof(IIsPropertySymbolFields.Value), IntermediateFieldType.String), + }, + typeof(IIsPropertySymbol)); + } +} + +namespace WixToolset.Iis.Symbols +{ + using WixToolset.Data; + + public enum IIsPropertySymbolFields + { + ComponentRef, + Attributes, + Value, + } + + public class IIsPropertySymbol : IntermediateSymbol + { + public IIsPropertySymbol() : base(IisSymbolDefinitions.IIsProperty, null, null) + { + } + + public IIsPropertySymbol(SourceLineNumber sourceLineNumber, Identifier id = null) : base(IisSymbolDefinitions.IIsProperty, sourceLineNumber, id) + { + } + + public IntermediateField this[IIsPropertySymbolFields index] => this.Fields[(int)index]; + + public string ComponentRef + { + get => this.Fields[(int)IIsPropertySymbolFields.ComponentRef].AsString(); + set => this.Set((int)IIsPropertySymbolFields.ComponentRef, value); + } + + public int Attributes + { + get => this.Fields[(int)IIsPropertySymbolFields.Attributes].AsNumber(); + set => this.Set((int)IIsPropertySymbolFields.Attributes, value); + } + + public string Value + { + get => this.Fields[(int)IIsPropertySymbolFields.Value].AsString(); + set => this.Set((int)IIsPropertySymbolFields.Value, value); + } + } +} \ No newline at end of file diff --git a/src/ext/Iis/wixext/Symbols/IIsWebAddressSymbol.cs b/src/ext/Iis/wixext/Symbols/IIsWebAddressSymbol.cs new file mode 100644 index 00000000..7111718a --- /dev/null +++ b/src/ext/Iis/wixext/Symbols/IIsWebAddressSymbol.cs @@ -0,0 +1,79 @@ +// 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. + +namespace WixToolset.Iis +{ + using WixToolset.Data; + using WixToolset.Iis.Symbols; + + public static partial class IisSymbolDefinitions + { + public static readonly IntermediateSymbolDefinition IIsWebAddress = new IntermediateSymbolDefinition( + IisSymbolDefinitionType.IIsWebAddress.ToString(), + new[] + { + new IntermediateFieldDefinition(nameof(IIsWebAddressSymbolFields.WebRef), IntermediateFieldType.String), + new IntermediateFieldDefinition(nameof(IIsWebAddressSymbolFields.IP), IntermediateFieldType.String), + new IntermediateFieldDefinition(nameof(IIsWebAddressSymbolFields.Port), IntermediateFieldType.String), + new IntermediateFieldDefinition(nameof(IIsWebAddressSymbolFields.Header), IntermediateFieldType.String), + new IntermediateFieldDefinition(nameof(IIsWebAddressSymbolFields.Secure), IntermediateFieldType.Number), + }, + typeof(IIsWebAddressSymbol)); + } +} + +namespace WixToolset.Iis.Symbols +{ + using WixToolset.Data; + + public enum IIsWebAddressSymbolFields + { + WebRef, + IP, + Port, + Header, + Secure, + } + + public class IIsWebAddressSymbol : IntermediateSymbol + { + public IIsWebAddressSymbol() : base(IisSymbolDefinitions.IIsWebAddress, null, null) + { + } + + public IIsWebAddressSymbol(SourceLineNumber sourceLineNumber, Identifier id = null) : base(IisSymbolDefinitions.IIsWebAddress, sourceLineNumber, id) + { + } + + public IntermediateField this[IIsWebAddressSymbolFields index] => this.Fields[(int)index]; + + public string WebRef + { + get => this.Fields[(int)IIsWebAddressSymbolFields.WebRef].AsString(); + set => this.Set((int)IIsWebAddressSymbolFields.WebRef, value); + } + + public string IP + { + get => this.Fields[(int)IIsWebAddressSymbolFields.IP].AsString(); + set => this.Set((int)IIsWebAddressSymbolFields.IP, value); + } + + public string Port + { + get => this.Fields[(int)IIsWebAddressSymbolFields.Port].AsString(); + set => this.Set((int)IIsWebAddressSymbolFields.Port, value); + } + + public string Header + { + get => this.Fields[(int)IIsWebAddressSymbolFields.Header].AsString(); + set => this.Set((int)IIsWebAddressSymbolFields.Header, value); + } + + public int? Secure + { + get => this.Fields[(int)IIsWebAddressSymbolFields.Secure].AsNullableNumber(); + set => this.Set((int)IIsWebAddressSymbolFields.Secure, value); + } + } +} \ No newline at end of file diff --git a/src/ext/Iis/wixext/Symbols/IIsWebApplicationExtensionSymbol.cs b/src/ext/Iis/wixext/Symbols/IIsWebApplicationExtensionSymbol.cs new file mode 100644 index 00000000..4283d702 --- /dev/null +++ b/src/ext/Iis/wixext/Symbols/IIsWebApplicationExtensionSymbol.cs @@ -0,0 +1,79 @@ +// 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. + +namespace WixToolset.Iis +{ + using WixToolset.Data; + using WixToolset.Iis.Symbols; + + public static partial class IisSymbolDefinitions + { + public static readonly IntermediateSymbolDefinition IIsWebApplicationExtension = new IntermediateSymbolDefinition( + IisSymbolDefinitionType.IIsWebApplicationExtension.ToString(), + new[] + { + new IntermediateFieldDefinition(nameof(IIsWebApplicationExtensionSymbolFields.ApplicationRef), IntermediateFieldType.String), + new IntermediateFieldDefinition(nameof(IIsWebApplicationExtensionSymbolFields.Extension), IntermediateFieldType.String), + new IntermediateFieldDefinition(nameof(IIsWebApplicationExtensionSymbolFields.Verbs), IntermediateFieldType.String), + new IntermediateFieldDefinition(nameof(IIsWebApplicationExtensionSymbolFields.Executable), IntermediateFieldType.String), + new IntermediateFieldDefinition(nameof(IIsWebApplicationExtensionSymbolFields.Attributes), IntermediateFieldType.Number), + }, + typeof(IIsWebApplicationExtensionSymbol)); + } +} + +namespace WixToolset.Iis.Symbols +{ + using WixToolset.Data; + + public enum IIsWebApplicationExtensionSymbolFields + { + ApplicationRef, + Extension, + Verbs, + Executable, + Attributes, + } + + public class IIsWebApplicationExtensionSymbol : IntermediateSymbol + { + public IIsWebApplicationExtensionSymbol() : base(IisSymbolDefinitions.IIsWebApplicationExtension, null, null) + { + } + + public IIsWebApplicationExtensionSymbol(SourceLineNumber sourceLineNumber, Identifier id = null) : base(IisSymbolDefinitions.IIsWebApplicationExtension, sourceLineNumber, id) + { + } + + public IntermediateField this[IIsWebApplicationExtensionSymbolFields index] => this.Fields[(int)index]; + + public string ApplicationRef + { + get => this.Fields[(int)IIsWebApplicationExtensionSymbolFields.ApplicationRef].AsString(); + set => this.Set((int)IIsWebApplicationExtensionSymbolFields.ApplicationRef, value); + } + + public string Extension + { + get => this.Fields[(int)IIsWebApplicationExtensionSymbolFields.Extension].AsString(); + set => this.Set((int)IIsWebApplicationExtensionSymbolFields.Extension, value); + } + + public string Verbs + { + get => this.Fields[(int)IIsWebApplicationExtensionSymbolFields.Verbs].AsString(); + set => this.Set((int)IIsWebApplicationExtensionSymbolFields.Verbs, value); + } + + public string Executable + { + get => this.Fields[(int)IIsWebApplicationExtensionSymbolFields.Executable].AsString(); + set => this.Set((int)IIsWebApplicationExtensionSymbolFields.Executable, value); + } + + public int Attributes + { + get => this.Fields[(int)IIsWebApplicationExtensionSymbolFields.Attributes].AsNumber(); + set => this.Set((int)IIsWebApplicationExtensionSymbolFields.Attributes, value); + } + } +} \ No newline at end of file diff --git a/src/ext/Iis/wixext/Symbols/IIsWebApplicationSymbol.cs b/src/ext/Iis/wixext/Symbols/IIsWebApplicationSymbol.cs new file mode 100644 index 00000000..2f6f87de --- /dev/null +++ b/src/ext/Iis/wixext/Symbols/IIsWebApplicationSymbol.cs @@ -0,0 +1,127 @@ +// 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. + +namespace WixToolset.Iis +{ + using WixToolset.Data; + using WixToolset.Iis.Symbols; + + public static partial class IisSymbolDefinitions + { + public static readonly IntermediateSymbolDefinition IIsWebApplication = new IntermediateSymbolDefinition( + IisSymbolDefinitionType.IIsWebApplication.ToString(), + new[] + { + new IntermediateFieldDefinition(nameof(IIsWebApplicationSymbolFields.Name), IntermediateFieldType.String), + new IntermediateFieldDefinition(nameof(IIsWebApplicationSymbolFields.Isolation), IntermediateFieldType.Number), + new IntermediateFieldDefinition(nameof(IIsWebApplicationSymbolFields.AllowSessions), IntermediateFieldType.Number), + new IntermediateFieldDefinition(nameof(IIsWebApplicationSymbolFields.SessionTimeout), IntermediateFieldType.Number), + new IntermediateFieldDefinition(nameof(IIsWebApplicationSymbolFields.Buffer), IntermediateFieldType.Number), + new IntermediateFieldDefinition(nameof(IIsWebApplicationSymbolFields.ParentPaths), IntermediateFieldType.Number), + new IntermediateFieldDefinition(nameof(IIsWebApplicationSymbolFields.DefaultScript), IntermediateFieldType.String), + new IntermediateFieldDefinition(nameof(IIsWebApplicationSymbolFields.ScriptTimeout), IntermediateFieldType.Number), + new IntermediateFieldDefinition(nameof(IIsWebApplicationSymbolFields.ServerDebugging), IntermediateFieldType.Number), + new IntermediateFieldDefinition(nameof(IIsWebApplicationSymbolFields.ClientDebugging), IntermediateFieldType.Number), + new IntermediateFieldDefinition(nameof(IIsWebApplicationSymbolFields.AppPoolRef), IntermediateFieldType.String), + }, + typeof(IIsWebApplicationSymbol)); + } +} + +namespace WixToolset.Iis.Symbols +{ + using WixToolset.Data; + + public enum IIsWebApplicationSymbolFields + { + Name, + Isolation, + AllowSessions, + SessionTimeout, + Buffer, + ParentPaths, + DefaultScript, + ScriptTimeout, + ServerDebugging, + ClientDebugging, + AppPoolRef, + } + + public class IIsWebApplicationSymbol : IntermediateSymbol + { + public IIsWebApplicationSymbol() : base(IisSymbolDefinitions.IIsWebApplication, null, null) + { + } + + public IIsWebApplicationSymbol(SourceLineNumber sourceLineNumber, Identifier id = null) : base(IisSymbolDefinitions.IIsWebApplication, sourceLineNumber, id) + { + } + + public IntermediateField this[IIsWebApplicationSymbolFields index] => this.Fields[(int)index]; + + public string Name + { + get => this.Fields[(int)IIsWebApplicationSymbolFields.Name].AsString(); + set => this.Set((int)IIsWebApplicationSymbolFields.Name, value); + } + + public int Isolation + { + get => this.Fields[(int)IIsWebApplicationSymbolFields.Isolation].AsNumber(); + set => this.Set((int)IIsWebApplicationSymbolFields.Isolation, value); + } + + public int? AllowSessions + { + get => this.Fields[(int)IIsWebApplicationSymbolFields.AllowSessions].AsNullableNumber(); + set => this.Set((int)IIsWebApplicationSymbolFields.AllowSessions, value); + } + + public int? SessionTimeout + { + get => this.Fields[(int)IIsWebApplicationSymbolFields.SessionTimeout].AsNullableNumber(); + set => this.Set((int)IIsWebApplicationSymbolFields.SessionTimeout, value); + } + + public int? Buffer + { + get => this.Fields[(int)IIsWebApplicationSymbolFields.Buffer].AsNullableNumber(); + set => this.Set((int)IIsWebApplicationSymbolFields.Buffer, value); + } + + public int? ParentPaths + { + get => this.Fields[(int)IIsWebApplicationSymbolFields.ParentPaths].AsNullableNumber(); + set => this.Set((int)IIsWebApplicationSymbolFields.ParentPaths, value); + } + + public string DefaultScript + { + get => this.Fields[(int)IIsWebApplicationSymbolFields.DefaultScript].AsString(); + set => this.Set((int)IIsWebApplicationSymbolFields.DefaultScript, value); + } + + public int? ScriptTimeout + { + get => this.Fields[(int)IIsWebApplicationSymbolFields.ScriptTimeout].AsNullableNumber(); + set => this.Set((int)IIsWebApplicationSymbolFields.ScriptTimeout, value); + } + + public int? ServerDebugging + { + get => this.Fields[(int)IIsWebApplicationSymbolFields.ServerDebugging].AsNullableNumber(); + set => this.Set((int)IIsWebApplicationSymbolFields.ServerDebugging, value); + } + + public int? ClientDebugging + { + get => this.Fields[(int)IIsWebApplicationSymbolFields.ClientDebugging].AsNullableNumber(); + set => this.Set((int)IIsWebApplicationSymbolFields.ClientDebugging, value); + } + + public string AppPoolRef + { + get => this.Fields[(int)IIsWebApplicationSymbolFields.AppPoolRef].AsString(); + set => this.Set((int)IIsWebApplicationSymbolFields.AppPoolRef, value); + } + } +} \ No newline at end of file diff --git a/src/ext/Iis/wixext/Symbols/IIsWebDirPropertiesSymbol.cs b/src/ext/Iis/wixext/Symbols/IIsWebDirPropertiesSymbol.cs new file mode 100644 index 00000000..42d2dead --- /dev/null +++ b/src/ext/Iis/wixext/Symbols/IIsWebDirPropertiesSymbol.cs @@ -0,0 +1,151 @@ +// 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. + +namespace WixToolset.Iis +{ + using WixToolset.Data; + using WixToolset.Iis.Symbols; + + public static partial class IisSymbolDefinitions + { + public static readonly IntermediateSymbolDefinition IIsWebDirProperties = new IntermediateSymbolDefinition( + IisSymbolDefinitionType.IIsWebDirProperties.ToString(), + new[] + { + new IntermediateFieldDefinition(nameof(IIsWebDirPropertiesSymbolFields.Access), IntermediateFieldType.Number), + new IntermediateFieldDefinition(nameof(IIsWebDirPropertiesSymbolFields.Authorization), IntermediateFieldType.Number), + new IntermediateFieldDefinition(nameof(IIsWebDirPropertiesSymbolFields.AnonymousUserRef), IntermediateFieldType.String), + new IntermediateFieldDefinition(nameof(IIsWebDirPropertiesSymbolFields.IIsControlledPassword), IntermediateFieldType.Number), + new IntermediateFieldDefinition(nameof(IIsWebDirPropertiesSymbolFields.LogVisits), IntermediateFieldType.Number), + new IntermediateFieldDefinition(nameof(IIsWebDirPropertiesSymbolFields.Index), IntermediateFieldType.Number), + new IntermediateFieldDefinition(nameof(IIsWebDirPropertiesSymbolFields.DefaultDoc), IntermediateFieldType.String), + new IntermediateFieldDefinition(nameof(IIsWebDirPropertiesSymbolFields.AspDetailedError), IntermediateFieldType.Number), + new IntermediateFieldDefinition(nameof(IIsWebDirPropertiesSymbolFields.HttpExpires), IntermediateFieldType.String), + new IntermediateFieldDefinition(nameof(IIsWebDirPropertiesSymbolFields.CacheControlMaxAge), IntermediateFieldType.Number), + new IntermediateFieldDefinition(nameof(IIsWebDirPropertiesSymbolFields.CacheControlCustom), IntermediateFieldType.String), + new IntermediateFieldDefinition(nameof(IIsWebDirPropertiesSymbolFields.NoCustomError), IntermediateFieldType.Number), + new IntermediateFieldDefinition(nameof(IIsWebDirPropertiesSymbolFields.AccessSSLFlags), IntermediateFieldType.Number), + new IntermediateFieldDefinition(nameof(IIsWebDirPropertiesSymbolFields.AuthenticationProviders), IntermediateFieldType.String), + }, + typeof(IIsWebDirPropertiesSymbol)); + } +} + +namespace WixToolset.Iis.Symbols +{ + using WixToolset.Data; + + public enum IIsWebDirPropertiesSymbolFields + { + Access, + Authorization, + AnonymousUserRef, + IIsControlledPassword, + LogVisits, + Index, + DefaultDoc, + AspDetailedError, + HttpExpires, + CacheControlMaxAge, + CacheControlCustom, + NoCustomError, + AccessSSLFlags, + AuthenticationProviders, + } + + public class IIsWebDirPropertiesSymbol : IntermediateSymbol + { + public IIsWebDirPropertiesSymbol() : base(IisSymbolDefinitions.IIsWebDirProperties, null, null) + { + } + + public IIsWebDirPropertiesSymbol(SourceLineNumber sourceLineNumber, Identifier id = null) : base(IisSymbolDefinitions.IIsWebDirProperties, sourceLineNumber, id) + { + } + + public IntermediateField this[IIsWebDirPropertiesSymbolFields index] => this.Fields[(int)index]; + + public int? Access + { + get => this.Fields[(int)IIsWebDirPropertiesSymbolFields.Access].AsNullableNumber(); + set => this.Set((int)IIsWebDirPropertiesSymbolFields.Access, value); + } + + public int? Authorization + { + get => this.Fields[(int)IIsWebDirPropertiesSymbolFields.Authorization].AsNullableNumber(); + set => this.Set((int)IIsWebDirPropertiesSymbolFields.Authorization, value); + } + + public string AnonymousUserRef + { + get => this.Fields[(int)IIsWebDirPropertiesSymbolFields.AnonymousUserRef].AsString(); + set => this.Set((int)IIsWebDirPropertiesSymbolFields.AnonymousUserRef, value); + } + + public int? IIsControlledPassword + { + get => this.Fields[(int)IIsWebDirPropertiesSymbolFields.IIsControlledPassword].AsNullableNumber(); + set => this.Set((int)IIsWebDirPropertiesSymbolFields.IIsControlledPassword, value); + } + + public int? LogVisits + { + get => this.Fields[(int)IIsWebDirPropertiesSymbolFields.LogVisits].AsNullableNumber(); + set => this.Set((int)IIsWebDirPropertiesSymbolFields.LogVisits, value); + } + + public int? Index + { + get => this.Fields[(int)IIsWebDirPropertiesSymbolFields.Index].AsNullableNumber(); + set => this.Set((int)IIsWebDirPropertiesSymbolFields.Index, value); + } + + public string DefaultDoc + { + get => this.Fields[(int)IIsWebDirPropertiesSymbolFields.DefaultDoc].AsString(); + set => this.Set((int)IIsWebDirPropertiesSymbolFields.DefaultDoc, value); + } + + public int? AspDetailedError + { + get => this.Fields[(int)IIsWebDirPropertiesSymbolFields.AspDetailedError].AsNullableNumber(); + set => this.Set((int)IIsWebDirPropertiesSymbolFields.AspDetailedError, value); + } + + public string HttpExpires + { + get => this.Fields[(int)IIsWebDirPropertiesSymbolFields.HttpExpires].AsString(); + set => this.Set((int)IIsWebDirPropertiesSymbolFields.HttpExpires, value); + } + + public int? CacheControlMaxAge + { + get => this.Fields[(int)IIsWebDirPropertiesSymbolFields.CacheControlMaxAge].AsNullableNumber(); + set => this.Set((int)IIsWebDirPropertiesSymbolFields.CacheControlMaxAge, value); + } + + public string CacheControlCustom + { + get => this.Fields[(int)IIsWebDirPropertiesSymbolFields.CacheControlCustom].AsString(); + set => this.Set((int)IIsWebDirPropertiesSymbolFields.CacheControlCustom, value); + } + + public int? NoCustomError + { + get => this.Fields[(int)IIsWebDirPropertiesSymbolFields.NoCustomError].AsNullableNumber(); + set => this.Set((int)IIsWebDirPropertiesSymbolFields.NoCustomError, value); + } + + public int? AccessSSLFlags + { + get => this.Fields[(int)IIsWebDirPropertiesSymbolFields.AccessSSLFlags].AsNullableNumber(); + set => this.Set((int)IIsWebDirPropertiesSymbolFields.AccessSSLFlags, value); + } + + public string AuthenticationProviders + { + get => this.Fields[(int)IIsWebDirPropertiesSymbolFields.AuthenticationProviders].AsString(); + set => this.Set((int)IIsWebDirPropertiesSymbolFields.AuthenticationProviders, value); + } + } +} \ No newline at end of file diff --git a/src/ext/Iis/wixext/Symbols/IIsWebDirSymbol.cs b/src/ext/Iis/wixext/Symbols/IIsWebDirSymbol.cs new file mode 100644 index 00000000..7f257f14 --- /dev/null +++ b/src/ext/Iis/wixext/Symbols/IIsWebDirSymbol.cs @@ -0,0 +1,79 @@ +// 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. + +namespace WixToolset.Iis +{ + using WixToolset.Data; + using WixToolset.Iis.Symbols; + + public static partial class IisSymbolDefinitions + { + public static readonly IntermediateSymbolDefinition IIsWebDir = new IntermediateSymbolDefinition( + IisSymbolDefinitionType.IIsWebDir.ToString(), + new[] + { + new IntermediateFieldDefinition(nameof(IIsWebDirSymbolFields.ComponentRef), IntermediateFieldType.String), + new IntermediateFieldDefinition(nameof(IIsWebDirSymbolFields.WebRef), IntermediateFieldType.String), + new IntermediateFieldDefinition(nameof(IIsWebDirSymbolFields.Path), IntermediateFieldType.String), + new IntermediateFieldDefinition(nameof(IIsWebDirSymbolFields.DirPropertiesRef), IntermediateFieldType.String), + new IntermediateFieldDefinition(nameof(IIsWebDirSymbolFields.ApplicationRef), IntermediateFieldType.String), + }, + typeof(IIsWebDirSymbol)); + } +} + +namespace WixToolset.Iis.Symbols +{ + using WixToolset.Data; + + public enum IIsWebDirSymbolFields + { + ComponentRef, + WebRef, + Path, + DirPropertiesRef, + ApplicationRef, + } + + public class IIsWebDirSymbol : IntermediateSymbol + { + public IIsWebDirSymbol() : base(IisSymbolDefinitions.IIsWebDir, null, null) + { + } + + public IIsWebDirSymbol(SourceLineNumber sourceLineNumber, Identifier id = null) : base(IisSymbolDefinitions.IIsWebDir, sourceLineNumber, id) + { + } + + public IntermediateField this[IIsWebDirSymbolFields index] => this.Fields[(int)index]; + + public string ComponentRef + { + get => this.Fields[(int)IIsWebDirSymbolFields.ComponentRef].AsString(); + set => this.Set((int)IIsWebDirSymbolFields.ComponentRef, value); + } + + public string WebRef + { + get => this.Fields[(int)IIsWebDirSymbolFields.WebRef].AsString(); + set => this.Set((int)IIsWebDirSymbolFields.WebRef, value); + } + + public string Path + { + get => this.Fields[(int)IIsWebDirSymbolFields.Path].AsString(); + set => this.Set((int)IIsWebDirSymbolFields.Path, value); + } + + public string DirPropertiesRef + { + get => this.Fields[(int)IIsWebDirSymbolFields.DirPropertiesRef].AsString(); + set => this.Set((int)IIsWebDirSymbolFields.DirPropertiesRef, value); + } + + public string ApplicationRef + { + get => this.Fields[(int)IIsWebDirSymbolFields.ApplicationRef].AsString(); + set => this.Set((int)IIsWebDirSymbolFields.ApplicationRef, value); + } + } +} \ No newline at end of file diff --git a/src/ext/Iis/wixext/Symbols/IIsWebErrorSymbol.cs b/src/ext/Iis/wixext/Symbols/IIsWebErrorSymbol.cs new file mode 100644 index 00000000..f8488fed --- /dev/null +++ b/src/ext/Iis/wixext/Symbols/IIsWebErrorSymbol.cs @@ -0,0 +1,87 @@ +// 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. + +namespace WixToolset.Iis +{ + using WixToolset.Data; + using WixToolset.Iis.Symbols; + + public static partial class IisSymbolDefinitions + { + public static readonly IntermediateSymbolDefinition IIsWebError = new IntermediateSymbolDefinition( + IisSymbolDefinitionType.IIsWebError.ToString(), + new[] + { + new IntermediateFieldDefinition(nameof(IIsWebErrorSymbolFields.ErrorCode), IntermediateFieldType.Number), + new IntermediateFieldDefinition(nameof(IIsWebErrorSymbolFields.SubCode), IntermediateFieldType.Number), + new IntermediateFieldDefinition(nameof(IIsWebErrorSymbolFields.ParentType), IntermediateFieldType.Number), + new IntermediateFieldDefinition(nameof(IIsWebErrorSymbolFields.ParentValue), IntermediateFieldType.String), + new IntermediateFieldDefinition(nameof(IIsWebErrorSymbolFields.File), IntermediateFieldType.String), + new IntermediateFieldDefinition(nameof(IIsWebErrorSymbolFields.URL), IntermediateFieldType.String), + }, + typeof(IIsWebErrorSymbol)); + } +} + +namespace WixToolset.Iis.Symbols +{ + using WixToolset.Data; + + public enum IIsWebErrorSymbolFields + { + ErrorCode, + SubCode, + ParentType, + ParentValue, + File, + URL, + } + + public class IIsWebErrorSymbol : IntermediateSymbol + { + public IIsWebErrorSymbol() : base(IisSymbolDefinitions.IIsWebError, null, null) + { + } + + public IIsWebErrorSymbol(SourceLineNumber sourceLineNumber, Identifier id = null) : base(IisSymbolDefinitions.IIsWebError, sourceLineNumber, id) + { + } + + public IntermediateField this[IIsWebErrorSymbolFields index] => this.Fields[(int)index]; + + public int ErrorCode + { + get => this.Fields[(int)IIsWebErrorSymbolFields.ErrorCode].AsNumber(); + set => this.Set((int)IIsWebErrorSymbolFields.ErrorCode, value); + } + + public int SubCode + { + get => this.Fields[(int)IIsWebErrorSymbolFields.SubCode].AsNumber(); + set => this.Set((int)IIsWebErrorSymbolFields.SubCode, value); + } + + public int ParentType + { + get => this.Fields[(int)IIsWebErrorSymbolFields.ParentType].AsNumber(); + set => this.Set((int)IIsWebErrorSymbolFields.ParentType, value); + } + + public string ParentValue + { + get => this.Fields[(int)IIsWebErrorSymbolFields.ParentValue].AsString(); + set => this.Set((int)IIsWebErrorSymbolFields.ParentValue, value); + } + + public string File + { + get => this.Fields[(int)IIsWebErrorSymbolFields.File].AsString(); + set => this.Set((int)IIsWebErrorSymbolFields.File, value); + } + + public string URL + { + get => this.Fields[(int)IIsWebErrorSymbolFields.URL].AsString(); + set => this.Set((int)IIsWebErrorSymbolFields.URL, value); + } + } +} \ No newline at end of file diff --git a/src/ext/Iis/wixext/Symbols/IIsWebLogSymbol.cs b/src/ext/Iis/wixext/Symbols/IIsWebLogSymbol.cs new file mode 100644 index 00000000..409dc673 --- /dev/null +++ b/src/ext/Iis/wixext/Symbols/IIsWebLogSymbol.cs @@ -0,0 +1,47 @@ +// 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. + +namespace WixToolset.Iis +{ + using WixToolset.Data; + using WixToolset.Iis.Symbols; + + public static partial class IisSymbolDefinitions + { + public static readonly IntermediateSymbolDefinition IIsWebLog = new IntermediateSymbolDefinition( + IisSymbolDefinitionType.IIsWebLog.ToString(), + new[] + { + new IntermediateFieldDefinition(nameof(IIsWebLogSymbolFields.Format), IntermediateFieldType.String), + }, + typeof(IIsWebLogSymbol)); + } +} + +namespace WixToolset.Iis.Symbols +{ + using WixToolset.Data; + + public enum IIsWebLogSymbolFields + { + Format, + } + + public class IIsWebLogSymbol : IntermediateSymbol + { + public IIsWebLogSymbol() : base(IisSymbolDefinitions.IIsWebLog, null, null) + { + } + + public IIsWebLogSymbol(SourceLineNumber sourceLineNumber, Identifier id = null) : base(IisSymbolDefinitions.IIsWebLog, sourceLineNumber, id) + { + } + + public IntermediateField this[IIsWebLogSymbolFields index] => this.Fields[(int)index]; + + public string Format + { + get => this.Fields[(int)IIsWebLogSymbolFields.Format].AsString(); + set => this.Set((int)IIsWebLogSymbolFields.Format, value); + } + } +} \ No newline at end of file diff --git a/src/ext/Iis/wixext/Symbols/IIsWebServiceExtensionSymbol.cs b/src/ext/Iis/wixext/Symbols/IIsWebServiceExtensionSymbol.cs new file mode 100644 index 00000000..44922357 --- /dev/null +++ b/src/ext/Iis/wixext/Symbols/IIsWebServiceExtensionSymbol.cs @@ -0,0 +1,79 @@ +// 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. + +namespace WixToolset.Iis +{ + using WixToolset.Data; + using WixToolset.Iis.Symbols; + + public static partial class IisSymbolDefinitions + { + public static readonly IntermediateSymbolDefinition IIsWebServiceExtension = new IntermediateSymbolDefinition( + IisSymbolDefinitionType.IIsWebServiceExtension.ToString(), + new[] + { + new IntermediateFieldDefinition(nameof(IIsWebServiceExtensionSymbolFields.ComponentRef), IntermediateFieldType.String), + new IntermediateFieldDefinition(nameof(IIsWebServiceExtensionSymbolFields.File), IntermediateFieldType.String), + new IntermediateFieldDefinition(nameof(IIsWebServiceExtensionSymbolFields.Description), IntermediateFieldType.String), + new IntermediateFieldDefinition(nameof(IIsWebServiceExtensionSymbolFields.Group), IntermediateFieldType.String), + new IntermediateFieldDefinition(nameof(IIsWebServiceExtensionSymbolFields.Attributes), IntermediateFieldType.Number), + }, + typeof(IIsWebServiceExtensionSymbol)); + } +} + +namespace WixToolset.Iis.Symbols +{ + using WixToolset.Data; + + public enum IIsWebServiceExtensionSymbolFields + { + ComponentRef, + File, + Description, + Group, + Attributes, + } + + public class IIsWebServiceExtensionSymbol : IntermediateSymbol + { + public IIsWebServiceExtensionSymbol() : base(IisSymbolDefinitions.IIsWebServiceExtension, null, null) + { + } + + public IIsWebServiceExtensionSymbol(SourceLineNumber sourceLineNumber, Identifier id = null) : base(IisSymbolDefinitions.IIsWebServiceExtension, sourceLineNumber, id) + { + } + + public IntermediateField this[IIsWebServiceExtensionSymbolFields index] => this.Fields[(int)index]; + + public string ComponentRef + { + get => this.Fields[(int)IIsWebServiceExtensionSymbolFields.ComponentRef].AsString(); + set => this.Set((int)IIsWebServiceExtensionSymbolFields.ComponentRef, value); + } + + public string File + { + get => this.Fields[(int)IIsWebServiceExtensionSymbolFields.File].AsString(); + set => this.Set((int)IIsWebServiceExtensionSymbolFields.File, value); + } + + public string Description + { + get => this.Fields[(int)IIsWebServiceExtensionSymbolFields.Description].AsString(); + set => this.Set((int)IIsWebServiceExtensionSymbolFields.Description, value); + } + + public string Group + { + get => this.Fields[(int)IIsWebServiceExtensionSymbolFields.Group].AsString(); + set => this.Set((int)IIsWebServiceExtensionSymbolFields.Group, value); + } + + public int Attributes + { + get => this.Fields[(int)IIsWebServiceExtensionSymbolFields.Attributes].AsNumber(); + set => this.Set((int)IIsWebServiceExtensionSymbolFields.Attributes, value); + } + } +} \ No newline at end of file diff --git a/src/ext/Iis/wixext/Symbols/IIsWebSiteCertificatesSymbol.cs b/src/ext/Iis/wixext/Symbols/IIsWebSiteCertificatesSymbol.cs new file mode 100644 index 00000000..851ce556 --- /dev/null +++ b/src/ext/Iis/wixext/Symbols/IIsWebSiteCertificatesSymbol.cs @@ -0,0 +1,55 @@ +// 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. + +namespace WixToolset.Iis +{ + using WixToolset.Data; + using WixToolset.Iis.Symbols; + + public static partial class IisSymbolDefinitions + { + public static readonly IntermediateSymbolDefinition IIsWebSiteCertificates = new IntermediateSymbolDefinition( + IisSymbolDefinitionType.IIsWebSiteCertificates.ToString(), + new[] + { + new IntermediateFieldDefinition(nameof(IIsWebSiteCertificatesSymbolFields.WebRef), IntermediateFieldType.String), + new IntermediateFieldDefinition(nameof(IIsWebSiteCertificatesSymbolFields.CertificateRef), IntermediateFieldType.String), + }, + typeof(IIsWebSiteCertificatesSymbol)); + } +} + +namespace WixToolset.Iis.Symbols +{ + using WixToolset.Data; + + public enum IIsWebSiteCertificatesSymbolFields + { + WebRef, + CertificateRef, + } + + public class IIsWebSiteCertificatesSymbol : IntermediateSymbol + { + public IIsWebSiteCertificatesSymbol() : base(IisSymbolDefinitions.IIsWebSiteCertificates, null, null) + { + } + + public IIsWebSiteCertificatesSymbol(SourceLineNumber sourceLineNumber, Identifier id = null) : base(IisSymbolDefinitions.IIsWebSiteCertificates, sourceLineNumber, id) + { + } + + public IntermediateField this[IIsWebSiteCertificatesSymbolFields index] => this.Fields[(int)index]; + + public string WebRef + { + get => this.Fields[(int)IIsWebSiteCertificatesSymbolFields.WebRef].AsString(); + set => this.Set((int)IIsWebSiteCertificatesSymbolFields.WebRef, value); + } + + public string CertificateRef + { + get => this.Fields[(int)IIsWebSiteCertificatesSymbolFields.CertificateRef].AsString(); + set => this.Set((int)IIsWebSiteCertificatesSymbolFields.CertificateRef, value); + } + } +} \ No newline at end of file diff --git a/src/ext/Iis/wixext/Symbols/IIsWebSiteSymbol.cs b/src/ext/Iis/wixext/Symbols/IIsWebSiteSymbol.cs new file mode 100644 index 00000000..ceba2ea0 --- /dev/null +++ b/src/ext/Iis/wixext/Symbols/IIsWebSiteSymbol.cs @@ -0,0 +1,135 @@ +// 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. + +namespace WixToolset.Iis +{ + using WixToolset.Data; + using WixToolset.Iis.Symbols; + + public static partial class IisSymbolDefinitions + { + public static readonly IntermediateSymbolDefinition IIsWebSite = new IntermediateSymbolDefinition( + IisSymbolDefinitionType.IIsWebSite.ToString(), + new[] + { + new IntermediateFieldDefinition(nameof(IIsWebSiteSymbolFields.ComponentRef), IntermediateFieldType.String), + new IntermediateFieldDefinition(nameof(IIsWebSiteSymbolFields.Description), IntermediateFieldType.String), + new IntermediateFieldDefinition(nameof(IIsWebSiteSymbolFields.ConnectionTimeout), IntermediateFieldType.Number), + new IntermediateFieldDefinition(nameof(IIsWebSiteSymbolFields.DirectoryRef), IntermediateFieldType.String), + new IntermediateFieldDefinition(nameof(IIsWebSiteSymbolFields.State), IntermediateFieldType.Number), + new IntermediateFieldDefinition(nameof(IIsWebSiteSymbolFields.Attributes), IntermediateFieldType.Number), + new IntermediateFieldDefinition(nameof(IIsWebSiteSymbolFields.KeyAddressRef), IntermediateFieldType.String), + new IntermediateFieldDefinition(nameof(IIsWebSiteSymbolFields.DirPropertiesRef), IntermediateFieldType.String), + new IntermediateFieldDefinition(nameof(IIsWebSiteSymbolFields.ApplicationRef), IntermediateFieldType.String), + new IntermediateFieldDefinition(nameof(IIsWebSiteSymbolFields.Sequence), IntermediateFieldType.Number), + new IntermediateFieldDefinition(nameof(IIsWebSiteSymbolFields.LogRef), IntermediateFieldType.String), + new IntermediateFieldDefinition(nameof(IIsWebSiteSymbolFields.WebsiteId), IntermediateFieldType.String), + }, + typeof(IIsWebSiteSymbol)); + } +} + +namespace WixToolset.Iis.Symbols +{ + using WixToolset.Data; + + public enum IIsWebSiteSymbolFields + { + ComponentRef, + Description, + ConnectionTimeout, + DirectoryRef, + State, + Attributes, + KeyAddressRef, + DirPropertiesRef, + ApplicationRef, + Sequence, + LogRef, + WebsiteId, + } + + public class IIsWebSiteSymbol : IntermediateSymbol + { + public IIsWebSiteSymbol() : base(IisSymbolDefinitions.IIsWebSite, null, null) + { + } + + public IIsWebSiteSymbol(SourceLineNumber sourceLineNumber, Identifier id = null) : base(IisSymbolDefinitions.IIsWebSite, sourceLineNumber, id) + { + } + + public IntermediateField this[IIsWebSiteSymbolFields index] => this.Fields[(int)index]; + + public string ComponentRef + { + get => this.Fields[(int)IIsWebSiteSymbolFields.ComponentRef].AsString(); + set => this.Set((int)IIsWebSiteSymbolFields.ComponentRef, value); + } + + public string Description + { + get => this.Fields[(int)IIsWebSiteSymbolFields.Description].AsString(); + set => this.Set((int)IIsWebSiteSymbolFields.Description, value); + } + + public int? ConnectionTimeout + { + get => this.Fields[(int)IIsWebSiteSymbolFields.ConnectionTimeout].AsNullableNumber(); + set => this.Set((int)IIsWebSiteSymbolFields.ConnectionTimeout, value); + } + + public string DirectoryRef + { + get => this.Fields[(int)IIsWebSiteSymbolFields.DirectoryRef].AsString(); + set => this.Set((int)IIsWebSiteSymbolFields.DirectoryRef, value); + } + + public int? State + { + get => this.Fields[(int)IIsWebSiteSymbolFields.State].AsNullableNumber(); + set => this.Set((int)IIsWebSiteSymbolFields.State, value); + } + + public int Attributes + { + get => this.Fields[(int)IIsWebSiteSymbolFields.Attributes].AsNumber(); + set => this.Set((int)IIsWebSiteSymbolFields.Attributes, value); + } + + public string KeyAddressRef + { + get => this.Fields[(int)IIsWebSiteSymbolFields.KeyAddressRef].AsString(); + set => this.Set((int)IIsWebSiteSymbolFields.KeyAddressRef, value); + } + + public string DirPropertiesRef + { + get => this.Fields[(int)IIsWebSiteSymbolFields.DirPropertiesRef].AsString(); + set => this.Set((int)IIsWebSiteSymbolFields.DirPropertiesRef, value); + } + + public string ApplicationRef + { + get => this.Fields[(int)IIsWebSiteSymbolFields.ApplicationRef].AsString(); + set => this.Set((int)IIsWebSiteSymbolFields.ApplicationRef, value); + } + + public int? Sequence + { + get => this.Fields[(int)IIsWebSiteSymbolFields.Sequence].AsNullableNumber(); + set => this.Set((int)IIsWebSiteSymbolFields.Sequence, value); + } + + public string LogRef + { + get => this.Fields[(int)IIsWebSiteSymbolFields.LogRef].AsString(); + set => this.Set((int)IIsWebSiteSymbolFields.LogRef, value); + } + + public string WebsiteId + { + get => this.Fields[(int)IIsWebSiteSymbolFields.WebsiteId].AsString(); + set => this.Set((int)IIsWebSiteSymbolFields.WebsiteId, value); + } + } +} \ No newline at end of file diff --git a/src/ext/Iis/wixext/Symbols/IIsWebVirtualDirSymbol.cs b/src/ext/Iis/wixext/Symbols/IIsWebVirtualDirSymbol.cs new file mode 100644 index 00000000..bfc29e84 --- /dev/null +++ b/src/ext/Iis/wixext/Symbols/IIsWebVirtualDirSymbol.cs @@ -0,0 +1,87 @@ +// 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. + +namespace WixToolset.Iis +{ + using WixToolset.Data; + using WixToolset.Iis.Symbols; + + public static partial class IisSymbolDefinitions + { + public static readonly IntermediateSymbolDefinition IIsWebVirtualDir = new IntermediateSymbolDefinition( + IisSymbolDefinitionType.IIsWebVirtualDir.ToString(), + new[] + { + new IntermediateFieldDefinition(nameof(IIsWebVirtualDirSymbolFields.ComponentRef), IntermediateFieldType.String), + new IntermediateFieldDefinition(nameof(IIsWebVirtualDirSymbolFields.WebRef), IntermediateFieldType.String), + new IntermediateFieldDefinition(nameof(IIsWebVirtualDirSymbolFields.Alias), IntermediateFieldType.String), + new IntermediateFieldDefinition(nameof(IIsWebVirtualDirSymbolFields.DirectoryRef), IntermediateFieldType.String), + new IntermediateFieldDefinition(nameof(IIsWebVirtualDirSymbolFields.DirPropertiesRef), IntermediateFieldType.String), + new IntermediateFieldDefinition(nameof(IIsWebVirtualDirSymbolFields.ApplicationRef), IntermediateFieldType.String), + }, + typeof(IIsWebVirtualDirSymbol)); + } +} + +namespace WixToolset.Iis.Symbols +{ + using WixToolset.Data; + + public enum IIsWebVirtualDirSymbolFields + { + ComponentRef, + WebRef, + Alias, + DirectoryRef, + DirPropertiesRef, + ApplicationRef, + } + + public class IIsWebVirtualDirSymbol : IntermediateSymbol + { + public IIsWebVirtualDirSymbol() : base(IisSymbolDefinitions.IIsWebVirtualDir, null, null) + { + } + + public IIsWebVirtualDirSymbol(SourceLineNumber sourceLineNumber, Identifier id = null) : base(IisSymbolDefinitions.IIsWebVirtualDir, sourceLineNumber, id) + { + } + + public IntermediateField this[IIsWebVirtualDirSymbolFields index] => this.Fields[(int)index]; + + public string ComponentRef + { + get => this.Fields[(int)IIsWebVirtualDirSymbolFields.ComponentRef].AsString(); + set => this.Set((int)IIsWebVirtualDirSymbolFields.ComponentRef, value); + } + + public string WebRef + { + get => this.Fields[(int)IIsWebVirtualDirSymbolFields.WebRef].AsString(); + set => this.Set((int)IIsWebVirtualDirSymbolFields.WebRef, value); + } + + public string Alias + { + get => this.Fields[(int)IIsWebVirtualDirSymbolFields.Alias].AsString(); + set => this.Set((int)IIsWebVirtualDirSymbolFields.Alias, value); + } + + public string DirectoryRef + { + get => this.Fields[(int)IIsWebVirtualDirSymbolFields.DirectoryRef].AsString(); + set => this.Set((int)IIsWebVirtualDirSymbolFields.DirectoryRef, value); + } + + public string DirPropertiesRef + { + get => this.Fields[(int)IIsWebVirtualDirSymbolFields.DirPropertiesRef].AsString(); + set => this.Set((int)IIsWebVirtualDirSymbolFields.DirPropertiesRef, value); + } + + public string ApplicationRef + { + get => this.Fields[(int)IIsWebVirtualDirSymbolFields.ApplicationRef].AsString(); + set => this.Set((int)IIsWebVirtualDirSymbolFields.ApplicationRef, value); + } + } +} \ No newline at end of file diff --git a/src/ext/Iis/wixext/Symbols/IisSymbolDefinitions.cs b/src/ext/Iis/wixext/Symbols/IisSymbolDefinitions.cs new file mode 100644 index 00000000..d6ed80a2 --- /dev/null +++ b/src/ext/Iis/wixext/Symbols/IisSymbolDefinitions.cs @@ -0,0 +1,107 @@ +// 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. + +namespace WixToolset.Iis +{ + using System; + using WixToolset.Data; + + public enum IisSymbolDefinitionType + { + Certificate, + CertificateHash, + IIsAppPool, + IIsFilter, + IIsHttpHeader, + IIsMimeMap, + IIsProperty, + IIsWebAddress, + IIsWebApplication, + IIsWebApplicationExtension, + IIsWebDir, + IIsWebDirProperties, + IIsWebError, + IIsWebLog, + IIsWebServiceExtension, + IIsWebSite, + IIsWebSiteCertificates, + IIsWebVirtualDir, + } + + public static partial class IisSymbolDefinitions + { + public static readonly Version Version = new Version("4.0.0"); + + public static IntermediateSymbolDefinition ByName(string name) + { + if (!Enum.TryParse(name, out IisSymbolDefinitionType type)) + { + return null; + } + + return ByType(type); + } + + public static IntermediateSymbolDefinition ByType(IisSymbolDefinitionType type) + { + switch (type) + { + case IisSymbolDefinitionType.Certificate: + return IisSymbolDefinitions.Certificate; + + case IisSymbolDefinitionType.CertificateHash: + return IisSymbolDefinitions.CertificateHash; + + case IisSymbolDefinitionType.IIsAppPool: + return IisSymbolDefinitions.IIsAppPool; + + case IisSymbolDefinitionType.IIsFilter: + return IisSymbolDefinitions.IIsFilter; + + case IisSymbolDefinitionType.IIsHttpHeader: + return IisSymbolDefinitions.IIsHttpHeader; + + case IisSymbolDefinitionType.IIsMimeMap: + return IisSymbolDefinitions.IIsMimeMap; + + case IisSymbolDefinitionType.IIsProperty: + return IisSymbolDefinitions.IIsProperty; + + case IisSymbolDefinitionType.IIsWebAddress: + return IisSymbolDefinitions.IIsWebAddress; + + case IisSymbolDefinitionType.IIsWebApplication: + return IisSymbolDefinitions.IIsWebApplication; + + case IisSymbolDefinitionType.IIsWebApplicationExtension: + return IisSymbolDefinitions.IIsWebApplicationExtension; + + case IisSymbolDefinitionType.IIsWebDir: + return IisSymbolDefinitions.IIsWebDir; + + case IisSymbolDefinitionType.IIsWebDirProperties: + return IisSymbolDefinitions.IIsWebDirProperties; + + case IisSymbolDefinitionType.IIsWebError: + return IisSymbolDefinitions.IIsWebError; + + case IisSymbolDefinitionType.IIsWebLog: + return IisSymbolDefinitions.IIsWebLog; + + case IisSymbolDefinitionType.IIsWebServiceExtension: + return IisSymbolDefinitions.IIsWebServiceExtension; + + case IisSymbolDefinitionType.IIsWebSite: + return IisSymbolDefinitions.IIsWebSite; + + case IisSymbolDefinitionType.IIsWebSiteCertificates: + return IisSymbolDefinitions.IIsWebSiteCertificates; + + case IisSymbolDefinitionType.IIsWebVirtualDir: + return IisSymbolDefinitions.IIsWebVirtualDir; + + default: + throw new ArgumentOutOfRangeException(nameof(type)); + } + } + } +} diff --git a/src/ext/Iis/wixext/WixToolset.Iis.wixext.csproj b/src/ext/Iis/wixext/WixToolset.Iis.wixext.csproj new file mode 100644 index 00000000..81d41e77 --- /dev/null +++ b/src/ext/Iis/wixext/WixToolset.Iis.wixext.csproj @@ -0,0 +1,31 @@ + + + + + + netstandard2.0 + WixToolset.Iis + WiX Toolset Iis Extension + WiX Toolset Iis Extension + true + build + + + + + + + + + + + + + + + + + + + + diff --git a/src/ext/Iis/wixext/WixToolset.Iis.wixext.targets b/src/ext/Iis/wixext/WixToolset.Iis.wixext.targets new file mode 100644 index 00000000..6398fce1 --- /dev/null +++ b/src/ext/Iis/wixext/WixToolset.Iis.wixext.targets @@ -0,0 +1,11 @@ + + + + + + $(MSBuildThisFileDirectory)..\tools\WixToolset.Iis.wixext.dll + + + + + diff --git a/src/ext/Iis/wixlib/IIsExtension.wxs b/src/ext/Iis/wixlib/IIsExtension.wxs new file mode 100644 index 00000000..18fc34ff --- /dev/null +++ b/src/ext/Iis/wixlib/IIsExtension.wxs @@ -0,0 +1,58 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/ext/Iis/wixlib/IIsExtension_Platform.wxi b/src/ext/Iis/wixlib/IIsExtension_Platform.wxi new file mode 100644 index 00000000..09562a69 --- /dev/null +++ b/src/ext/Iis/wixlib/IIsExtension_Platform.wxi @@ -0,0 +1,66 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/ext/Iis/wixlib/IIsExtension_arm64.wxs b/src/ext/Iis/wixlib/IIsExtension_arm64.wxs new file mode 100644 index 00000000..39ac048f --- /dev/null +++ b/src/ext/Iis/wixlib/IIsExtension_arm64.wxs @@ -0,0 +1,7 @@ + + + + + + + diff --git a/src/ext/Iis/wixlib/IIsExtension_x64.wxs b/src/ext/Iis/wixlib/IIsExtension_x64.wxs new file mode 100644 index 00000000..553953b3 --- /dev/null +++ b/src/ext/Iis/wixlib/IIsExtension_x64.wxs @@ -0,0 +1,7 @@ + + + + + + + diff --git a/src/ext/Iis/wixlib/IIsExtension_x86.wxs b/src/ext/Iis/wixlib/IIsExtension_x86.wxs new file mode 100644 index 00000000..c99a243b --- /dev/null +++ b/src/ext/Iis/wixlib/IIsExtension_x86.wxs @@ -0,0 +1,7 @@ + + + + + + + diff --git a/src/ext/Iis/wixlib/caDecor.wxi b/src/ext/Iis/wixlib/caDecor.wxi new file mode 100644 index 00000000..b1711518 --- /dev/null +++ b/src/ext/Iis/wixlib/caDecor.wxi @@ -0,0 +1,39 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/ext/Iis/wixlib/caerr.wxi b/src/ext/Iis/wixlib/caerr.wxi new file mode 100644 index 00000000..ff7ec121 --- /dev/null +++ b/src/ext/Iis/wixlib/caerr.wxi @@ -0,0 +1,96 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/ext/Iis/wixlib/de-de.wxl b/src/ext/Iis/wixlib/de-de.wxl new file mode 100644 index 00000000..5929f258 --- /dev/null +++ b/src/ext/Iis/wixlib/de-de.wxl @@ -0,0 +1,55 @@ + + + + + Konnte keine Verbindung mit dem Internet Information Server herstellen. ([2] [3] [4] [5]) + Fehler bei der Verarbeitung der WebSites. ([2] [3] [4] [5]) + Fehler bei der Verarbeitung der WebDirs. ([2] [3] [4] [5]) + Fehler bei der Verarbeitung der WebVirtualDirs. ([2] [3] [4] [5]) + Fehler bei der Verarbeitung der WebFilters. ([2] [3] [4] [5]) + Fehler bei der Verarbeitung der MimeMaps. ([2] [3] [4] [5]) + Fehler bei der Verarbeitung der WebAppPools. ([2] [3] [4] [5]) + Fehler bei der Verarbeitung der WebProperties. ([2] [3] [4] [5]) + Fehler bei der Verarbeitung der WebServiceExtensions. ([2] [3] [4] [5]) + Fehler bei der Verarbeitung der WebErrors. ([2] [3] [4] [5]) + Fehler bei der Verarbeitung der HttpHeader. ([2] [3] [4] [5]) + + Die Transaktion für Änderungen am IIS konnte nicht geschedulded werden. ([2] [3] [4] [5]) + Die Installation von IIS-Websites konnte nicht geschedulded werden. ([2] [3] [4] [5]) + Die Installation von IIS Web Directories konnte nicht geschedulded werden. ([2] [3] [4] [5]) + Die Installation von IIS Virtual Directories konnte nicht geschedulded werden. ([2] [3] [4] [5]) + Die Installation von IIS Filters konnte nicht geschedulded werden. ([2] [3] [4] [5]) + Die Installation von IIS AppPools konnte nicht geschedulded werden. ([2] [3] [4] [5]) + Die Installation von IIS Properties konnte nicht geschedulded werden. ([2] [3] [4] [5]) + Die Installation von IIS Web Service Extensions konnte nicht geschedulded werden. ([2] [3] [4] [5]) + + Die Deinstallation von IIS Web Sites konnte nicht geschedulded werden. ([2] [3] [4] [5]) + Die Deinstallation von IIS Web Directories konnte nicht geschedulded werden. ([2] [3] [4] [5]) + Die Deinstallation von IIS Virtual Directories konnte nicht geschedulded werden. ([2] [3] [4] [5]) + Die Deinstallation von IIS Filters konnte nicht geschedulded werden. ([2] [3] [4] [5]) + Die Deinstallation von IIS AppPools konnte nicht geschedulded werden. ([2] [3] [4] [5]) + Die Deinstallation der IIS Properties konnte nicht geschedulded werden. ([2] [3] [4] [5]) + Die Deinstallation von IIS Web Service Extensions konnte nicht geschedulded werden. ([2] [3] [4] [5]) + + Die IIS-Transaktion konnte nicht gestartet werden. ([2] [3] [4] [5]) + Der Metabase Key konnte nicht geöffnet werden. ([2] [3] [4] [5]) + Der Metabase Key konnte nicht erstellt werden. ([2] [3] [4] [5]) + Daten konnten nicht in den Metabase Key geschrieben werden. ([2] [3] [4] [5]) + Webanwendung konnte nicht erstellt werden. ([2] [3] [4] [5]) + Der Metabase Key konnte nicht gelöscht werden. ([2] [3] [4] [5]) + Der Metabase Value konnte nicht gelöscht werden. ([2] [3] [4] [5]) + Die IIS Transaktion konnte aufgrund einer Freigabeverletzung nicht committed werden. Möglicherweise konfiguriert eine andere Anwendung derzeit IIS. + + Konfiguriere IIS + Führe IIS Konfiguration aus + Starte IIS Metabase Transaktion + Mache IIS Metabase Transaktion rückgängig + Committe IIS Metabase Transaktion + Installiere Metabase Keys und Values + + Konfiguriere IIS + Starte IIS Konfigurationstransaktion + Mache IIS Konfigurationstransaktion rückgängig + Committe IIS Konfigurationstransaktion + Installiere Konfigurationskeys and -values + diff --git a/src/ext/Iis/wixlib/en-us.wxl b/src/ext/Iis/wixlib/en-us.wxl new file mode 100644 index 00000000..44949095 --- /dev/null +++ b/src/ext/Iis/wixlib/en-us.wxl @@ -0,0 +1,55 @@ + + + + + Cannot connect to Internet Information Server. ([2] [3] [4] [5]) + Failed while processing WebSites. ([2] [3] [4] [5]) + Failed while processing WebDirs. ([2] [3] [4] [5]) + Failed while processing WebVirtualDirs. ([2] [3] [4] [5]) + Failed while processing WebFilters. ([2] [3] [4] [5]) + Failed while processing MimeMaps. ([2] [3] [4] [5]) + Failed while processing WebAppPools. ([2] [3] [4] [5]) + Failed while processing WebProperties. ([2] [3] [4] [5]) + Failed while processing WebServiceExtensions. ([2] [3] [4] [5]) + Failed while processing WebErrors. ([2] [3] [4] [5]) + Failed while processing HttpHeaders. ([2] [3] [4] [5]) + + Failed to schedule transaction for changes to IIS. ([2] [3] [4] [5]) + Failed to schedule install of IIS Web Sites. ([2] [3] [4] [5]) + Failed to schedule install of IIS Web Directories. ([2] [3] [4] [5]) + Failed to schedule install of IIS Virtual Directories. ([2] [3] [4] [5]) + Failed to schedule install of IIS Filters. ([2] [3] [4] [5]) + Failed to schedule install of IIS AppPools. ([2] [3] [4] [5]) + Failed to schedule install of IIS Properties. ([2] [3] [4] [5]) + Failed to schedule install of IIS Web Service Extensions. ([2] [3] [4] [5]) + + Failed to schedule uninstall of IIS Web Sites. ([2] [3] [4] [5]) + Failed to schedule uninstall of IIS Web Directories. ([2] [3] [4] [5]) + Failed to schedule uninstall of IIS Virtual Directories. ([2] [3] [4] [5]) + Failed to schedule uninstall of IIS Filters. ([2] [3] [4] [5]) + Failed to schedule uninstall of IIS AppPools. ([2] [3] [4] [5]) + Failed to schedule uninstall of IIS Properties. ([2] [3] [4] [5]) + Failed to schedule uninstall of IIS Web Service Extensions. ([2] [3] [4] [5]) + + Failed to start IIS transaction. ([2] [3] [4] [5]) + Failed to open metabase key. ([2] [3] [4] [5]) + Failed to create metabase key. ([2] [3] [4] [5]) + Failed to write data to metabase key. ([2] [3] [4] [5]) + Failed to create web application. ([2] [3] [4] [5]) + Failed to delete metabase key. ([2] [3] [4] [5]) + Failed to delete metabase value. ([2] [3] [4] [5]) + Failed to commit IIS transaction due to a sharing violation. Some other application may be configuring IIS. + + Configuring IIS + Executing IIS Configuration + Starting IIS Metabase Transaction + Rolling back IIS Metabase Transaction + Committing IIS Metabase Transaction + Installing Metabase Keys and Values + + Configuring IIS + Starting IIS Config Transaction + Rolling back IIS Config Transaction + Committing IIS Config Transaction + Installing Config Keys and Values + diff --git a/src/ext/Iis/wixlib/iis.v3.ncrunchproject b/src/ext/Iis/wixlib/iis.v3.ncrunchproject new file mode 100644 index 00000000..319cd523 --- /dev/null +++ b/src/ext/Iis/wixlib/iis.v3.ncrunchproject @@ -0,0 +1,5 @@ + + + True + + \ No newline at end of file diff --git a/src/ext/Iis/wixlib/iis.wixproj b/src/ext/Iis/wixlib/iis.wixproj new file mode 100644 index 00000000..89f9608f --- /dev/null +++ b/src/ext/Iis/wixlib/iis.wixproj @@ -0,0 +1,26 @@ + + + + + Library + true + en-us + + + + + + + + + + + + + + + + + + + diff --git a/src/ext/Iis/wixlib/ja-jp.wxl b/src/ext/Iis/wixlib/ja-jp.wxl new file mode 100644 index 00000000..7fd6978a --- /dev/null +++ b/src/ext/Iis/wixlib/ja-jp.wxl @@ -0,0 +1,47 @@ + + + + + IISへ接続できません。 ([2] [3] [4] [5]) + ウェブ サイト処理中に失敗しました。 ([2] [3] [4] [5]) + ウェブ ディレクトリ処理中に失敗しました。 ([2] [3] [4] [5]) + ウェブ仮想ディレクトリ処理中に失敗しました。 ([2] [3] [4] [5]) + ウェブ フィルタ処理中に失敗しました。 ([2] [3] [4] [5]) + MIME マップ処理中に失敗しました。 ([2] [3] [4] [5]) + ウェブ アプリケーション プール処理中に失敗しました。 ([2] [3] [4] [5]) + ウェブ プロパティ処理中に失敗しました。 ([2] [3] [4] [5]) + ウェブ サービス拡張処理中に失敗しました。 ([2] [3] [4] [5]) + ウェブ エラー処理中に失敗しました。 ([2] [3] [4] [5]) + HTTP ヘッダ処理中に失敗しました。 ([2] [3] [4] [5]) + + IIS 変更トランザクションのスケジューリングに失敗しました。 ([2] [3] [4] [5]) + IIS ウェブ サイト インストールのスケジューリングに失敗しました。 ([2] [3] [4] [5]) + IIS ウェブ ディレクトリ インストールのスケジューリングに失敗しました。 ([2] [3] [4] [5]) + IIS 仮想ディレクトリ インストールのスケジューリングに失敗しました。 ([2] [3] [4] [5]) + IIS フィルタ インストールのスケジューリングに失敗しました。 ([2] [3] [4] [5]) + IIS アプリケーション プール インストールのスケジューリングに失敗しました。 ([2] [3] [4] [5]) + IIS プロパティ インストールのスケジューリングに失敗しました。 ([2] [3] [4] [5]) + IIS ウェブ サービス拡張インストールのスケジューリングに失敗しました。 ([2] [3] [4] [5]) + + IISウェブ サイト アンインストールのスケジューリングに失敗しました。 ([2] [3] [4] [5]) + IIS ウェブ ディレクトリ アンインストールのスケジューリングに失敗しました。 ([2] [3] [4] [5]) + IIS 仮想ディレクトリ アンインストールのスケジューリングに失敗しました。 ([2] [3] [4] [5]) + IIS フィルタ アンインストールのスケジューリングに失敗しました。 ([2] [3] [4] [5]) + IIS アプリケーション プール アンインストールのスケジューリングに失敗しました。 ([2] [3] [4] [5]) + IIS プロパティ アンインストールのスケジューリングに失敗しました。 ([2] [3] [4] [5]) + IIS ウェブ サービス拡張アンインストールのスケジューリングに失敗しました。 ([2] [3] [4] [5]) + + IIS トランザクション開始に失敗しました。 ([2] [3] [4] [5]) + メタベース キーのオープンに失敗しました。 ([2] [3] [4] [5]) + メタベース キーの作成に失敗しました。 ([2] [3] [4] [5]) + メタベース キーの書き込みに失敗しました。 ([2] [3] [4] [5]) + ウェブ アプリケーションの作成に失敗しました。 ([2] [3] [4] [5]) + メタベース キーの削除に失敗しました。 ([2] [3] [4] [5]) + メタベース値の削除に失敗しました。 ([2] [3] [4] [5]) + + IIS を構成しています + IIS メタベース トランザクションを開始しています + IIS メタベース トランザクションをロールバックしています + IIS メタベース トランザクションを確定しています + IIS メタベース キーと値をインストールしています + diff --git a/src/ext/Iis/wixlib/pt-br.wxl b/src/ext/Iis/wixlib/pt-br.wxl new file mode 100644 index 00000000..1fc0d768 --- /dev/null +++ b/src/ext/Iis/wixlib/pt-br.wxl @@ -0,0 +1,50 @@ + + + + + Não foi possível conectar no Internet Information Server. ([2] [3] [4] [5]) + Erro ao processar WebSites. ([2] [3] [4] [5]) + Erro ao processar WebDirs. ([2] [3] [4] [5]) + Erro ao processar WebVirtualDirs. ([2] [3] [4] [5]) + Erro ao processar WebFilters. ([2] [3] [4] [5]) + Erro ao processar MimeMaps. ([2] [3] [4] [5]) + Erro ao processar WebAppPools. ([2] [3] [4] [5]) + Erro ao processar WebProperties. ([2] [3] [4] [5]) + Erro ao processar WebServiceExtensions. ([2] [3] [4] [5]) + Erro ao processar WebErrors. ([2] [3] [4] [5]) + Erro ao processar HttpHeaders. ([2] [3] [4] [5]) + Erro ao agendar transação para alterações no IIS. ([2] [3] [4] [5]) + Erro ao agendar instalação de IIS Web Sites. ([2] [3] [4] [5]) + Erro ao agendar instalação de IIS Web Directories. ([2] [3] [4] [5]) + Erro ao agendar instalação de IIS Virtual Directories. ([2] [3] [4] [5]) + Erro ao agendar instalação de IIS Filters. ([2] [3] [4] [5]) + Erro ao agendar instalação de IIS AppPools. ([2] [3] [4] [5]) + Erro ao agendar instalação de IIS Properties. ([2] [3] [4] [5]) + Erro ao agendar instalação de IIS Web Service Extensions. ([2] [3] [4] [5]) + Erro ao agendar desinstalação de IIS Web Sites. ([2] [3] [4] [5]) + Erro ao agendar desinstalação de IIS Web Directories. ([2] [3] [4] [5]) + Erro ao agendar desinstalação de IIS Virtual Directories. ([2] [3] [4] [5]) + Erro ao agendar desinstalação de IIS Filters. ([2] [3] [4] [5]) + Erro ao agendar desinstalação de IIS AppPools. ([2] [3] [4] [5]) + Erro ao agendar desinstalação de IIS Properties. ([2] [3] [4] [5]) + Erro ao agendar desinstalação de IIS Web Service Extensions. ([2] [3] [4] [5]) + Erro ao iniciar transação do IIS . ([2] [3] [4] [5]) + Erro ao abrir metabase key. ([2] [3] [4] [5]) + Erro ao criar metabase key. ([2] [3] [4] [5]) + Erro ao escrever dados na metabase key. ([2] [3] [4] [5]) + Erro ao criar aplicação Web. ([2] [3] [4] [5]) + Erro ao excluir metabase key. ([2] [3] [4] [5]) + Erro ao excluir valor da metabase. ([2] [3] [4] [5]) + Erro ao fazer commit de transação IIS por violação de compartilhamento. Alguma outra aplicação pode estar tentando configurar o ISS ao mesmo tempo. + Configurando IIS + Executando Configurações do IIS + Iniciando Transação de Metabase do IIS + Cancelando Transação de Metabase do IIS + Efetivando Transação de Metabase do IIS + Instalando Chaves e Valores de Metabase do IIS + Configurando IIS + Iniciando Transação de Configuração do IIS + Cancelando Transação de Configuração do IIS + Efetivando Transação de Configuração do IIS + Instalando Chaves e Valores de Configurações do IIS + diff --git a/src/ext/global.json b/src/ext/global.json new file mode 100644 index 00000000..23dd3fa6 --- /dev/null +++ b/src/ext/global.json @@ -0,0 +1,5 @@ +{ + "msbuild-sdks": { + "WixToolset.Sdk": "4.0.0-build-0211" + } +} -- cgit v1.2.3-55-g6feb