Microsoft.VSCode.Dsc.psm1
# Copyright (c) Microsoft Corporation. All rights reserved. # Licensed under the MIT License. $ErrorActionPreference = 'Stop' Set-StrictMode -Version Latest #region Functions function Get-VSCodeRegistryKey { [CmdletBinding()] param ( [string] $Architecture ) switch ($architecture) { 'X64' { return @('{771FD6B0-FA20-440A-A002-3B3BAC16DC50}_is1', '{EA457B21-F73E-494C-ACAB-524FDE069978}_is1') } 'X86' { return @('{D628A17A-9713-46BF-8D57-E671B46A741E}_is1', '{F8A2A208-72B3-4D61-95FC-8A65D340689B}_is1') } 'Arm64' { return @('{D9E514E7-1A56-452D-9337-2990C0DC4310}_is1', '{A5270FC5-65AD-483E-AC30-2C276B63D0AC}_is1') } default { throw 'Could not determine architecture.' } } } function Get-VSCodeInsidersRegistryKey { param ( [string] $Architecture ) switch ($Architecture) { 'X64' { return @('{217B4C08-948D-4276-BFBB-BEE930AE5A2C}_is1', '{1287CAD5-7C8D-410D-88B9-0D1EE4A83FF2}_is1') } 'X86' { return @('{C26E74D1-022E-4238-8B9D-1E7564A36CC9}_is1', '{26F4A15E-E392-4887-8C09-7BC55712FD5B}_is1') } 'Arm64' { return @('{69BD8F7B-65EB-4C6F-A14E-44CFA83712C0}_is1', '{0AEDB616-9614-463B-97D7-119DD86CCA64}_is1') } default { throw 'Could not determine architecture.' } } } function Get-OSArchitectureRegistryKey { [CmdletBinding()] param ( [ValidateSet('X64', 'X86', 'Arm64')] [string] $Architecture, [switch] $Insiders ) $registryKey = if ($Insiders.IsPresent) { Get-VSCodeInsidersRegistryKey -Architecture $architecture } else { Get-VSCodeRegistryKey -Architecture $architecture } return $registryKey } function Get-VSCodeCLIPath { param ( [switch]$Insiders ) $architecture = [System.Runtime.InteropServices.RuntimeInformation]::OSArchitecture # Get the available keys $registryKeys = Get-OSArchitectureRegistryKey -Insiders:$Insiders.IsPresent -Architecture $architecture $registryHive = @('HKCU:\Software\Microsoft\Windows\CurrentVersion\Uninstall', 'HKLM:\Software\Microsoft\Windows\CurrentVersion\Uninstall', 'HKLM:\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall') if ($IsLinux) { $command = 'code' if ($Insiders.IsPresent) { $command = 'code-insiders' } # Using Get-Command to find the path of the command instead of PATH environment variable because both can be installed $commandPath = Get-Command -Name $command -ErrorAction SilentlyContinue if ($commandPath) { return $commandPath.Source } } if ($IsWindows) { foreach ($hive in $registryHive) { foreach ($key in $registryKeys) { Write-Verbose -Message ("Searching path '{0}' with key '{1}'" -f $hive, $key) $installLocation = TryGetRegistryValue -Key "$hive\$key" -Property 'InstallLocation' if ($installLocation) { $cmdPath = $Insiders ? 'bin\code-insiders.cmd' : 'bin\code.cmd' return Join-Path $installLocation $cmdPath } } } } throw 'VSCode is not installed.' } function Install-VSCodeExtension { [CmdletBinding()] param ( [Parameter(Mandatory, ValueFromPipelineByPropertyName)] [string]$Name, [Parameter(ValueFromPipelineByPropertyName)] [string]$Version, [Parameter()] [bool]$PreRelease ) begin { function Get-VSCodeExtensionInstallArgument { param([string]$Name, [string]$Version) if ([string]::IsNullOrEmpty($Version)) { return $Name } return @( $Name $Version ) -join '@' } } process { $installArgument = Get-VSCodeExtensionInstallArgument @PSBoundParameters # Always add the --force parameter to support switching between release and prerelease version $command = "--install-extension $installArgument --force" if ($PreRelease) { $command += ' --pre-release' } Invoke-VSCode -Command $command } } function Uninstall-VSCodeExtension { [CmdletBinding()] param ( [Parameter(Mandatory, ValueFromPipelineByPropertyName)] [string]$Name ) Invoke-VSCode -Command "--uninstall-extension $($this.Name)" } function Invoke-VSCode { param ( [Parameter(Mandatory = $true)] [string]$Command ) $stdErrTempFile = Join-Path -Path ([System.IO.Path]::GetTempPath()) -ChildPath (New-Guid).Guid $stdOutTempFile = Join-Path -Path ([System.IO.Path]::GetTempPath()) -ChildPath (New-Guid).Guid $invocationSuccess = $true $processParams = @{ FilePath = $VSCodeCLIPath ArgumentList = "$Command" RedirectStandardError = $stdErrTempFile RedirectStandardOutput = $stdOutTempFile Wait = $true PassThru = $true NoNewWindow = $true } $invocation = Start-Process @processParams $invocationErrors = Get-Content $stdErrTempFile -Raw -ErrorAction SilentlyContinue $invocationErrors = $invocationErrors -replace '\n', '\n ' $invocationOutput = Get-Content $stdOutTempFile -ErrorAction SilentlyContinue Remove-Item -Path $stdErrTempFile -ErrorAction Ignore Remove-Item -Path $stdOutTempFile -ErrorAction Ignore if (![string]::IsNullOrWhiteSpace($invocationErrors)) { $invocationSuccess = $false } if ($invocation.ExitCode) { $invocationSuccess = $false } if (!$invocationSuccess) { throw [System.Configuration.ConfigurationException]::new("Executing '$VSCodeCLIPath $Command' failed. Command Output: '$invocationErrors'") } return $invocationOutput } function TryGetRegistryValue { param ( [Parameter(Mandatory = $true)] [string]$Key, [Parameter(Mandatory = $true)] [string]$Property ) if (Test-Path -Path $Key) { try { return (Get-ItemProperty -Path $Key | Select-Object -ExpandProperty $Property) } catch { Write-Verbose "Property `"$($Property)`" could not be found." } } else { Write-Verbose 'Registry key does not exist.' } } function Get-PreReleaseFlag { [CmdletBinding()] param ( [Parameter()] [AllowNull()] [string] $Name, [Parameter()] [AllowNull()] [string] $Version, [Parameter()] [switch] $Insiders ) if ($IsWindows) { $packageName = [System.String]::Concat($Name, '-', $Version) if ($Insiders) { $extensionPath = Join-Path $env:USERPROFILE '.vscode-insiders' 'extensions' $packageName '.vsixmanifest' } else { $extensionPath = Join-Path $env:USERPROFILE '.vscode' 'extensions' $packageName '.vsixmanifest' } if (Test-Path $extensionPath -ErrorAction SilentlyContinue) { [xml]$manifest = Get-Content $extensionPath -ErrorAction SilentlyContinue # If it does not contain the property, it is not a pre-release extension if ($manifest.PackageManifest.Metadata.Properties.Property.Id -contains 'Microsoft.VisualStudio.Code.PreRelease') { return $true } else { return $false } } } } function Get-VsixManifestInfo { [CmdletBinding()] [OutputType([System.Collections.Hashtable])] param ( [Parameter(Mandatory = $true)] [string] $Path ) [System.IO.Compression.ZipFile]::OpenRead($Path) | ForEach-Object { $zipArchive = $_ $zipEntry = $zipArchive.Entries | Where-Object { $_.FullName -eq 'extension.vsixmanifest' } if ($zipEntry) { $reader = [System.IO.StreamReader]::new($zipEntry.Open()) [xml]$fileContent = $reader.ReadToEnd() $reader.Close() $packageId = [System.String]::Concat($fileContent.PackageManifest.Metadata.Identity.Publisher, '.', $fileContent.PackageManifest.Metadata.Identity.Id) return @{ Name = $packageId Version = $fileContent.PackageManifest.Metadata.Identity.Version PreRelease = ($fileContent.PackageManifest.Metadata.Properties.Property.Id -contains 'Microsoft.VisualStudio.Code.PreRelease') } } else { throw "VSIX manifest not found. Ensure the VSIX file contains a 'extension.vsixmanifest' file." } # Close the zip archive $zipArchive.Dispose() } } function Test-VsixFilePath { param ( [Parameter(Mandatory = $false)] [AllowNull()] [string] $InputString ) $extension = [System.IO.Path]::GetExtension($InputString) if ($extension -eq '.vsix') { if (Test-Path -Path $InputString -ErrorAction SilentlyContinue) { return $true } else { return $false } } else { return $false } } #endregion Functions #region DSCResources <# .SYNOPSIS The `VSCodeExtension` DSC Resource allows you to install, update, and remove Visual Studio Code extensions. This resource ensures that the specified Visual Studio Code extension is in the desired state. .PARAMETER Name The name of the Visual Studio Code extension to manage. This is a required parameter. .PARAMETER Version The version of the Visual Studio Code extension to install. If not specified, the latest version will be installed. .PARAMETER Exist Indicates whether the extension should exist. The default value is `$true`. .PARAMETER PreRelease Indicates whether to install the pre-release version of the extension. The default value is `$false`. When PreRelease is set to `$true`, the extension will be installed from the Visual Studio Code marketplace. If the extension is already installed, it will be updated to the pre-release version. If there is no prerelease version available, the extension will be installed. .PARAMETER Insiders Indicates whether to manage the extension for the Insiders version of Visual Studio Code. The default value is `$false`. .EXAMPLE PS C:\> $params = @{ Name = 'ms-python.python' } PS C:\> Invoke-DscResource -Name VSCodeExtension -Method Set -Property $params -ModuleName Microsoft.VSCode.Dsc This installs the latest version of the Visual Studio Code extension 'ms-python.python' .EXAMPLE # Install a specific version of the Visual Studio Code extension 'ms-python.python' PS C:\> $params = @{ Name = 'ms-python.python' Version = '2021.5.842923320' } PS C:\> Invoke-DscResource -Name VSCodeExtension -Method Set -Property $params -ModuleName Microsoft.VSCode.Dsc This installs a specific version of the Visual Studio Code extension 'ms-python.python' .EXAMPLE PS C:\> $params = @{ Name = 'ms-python.python' Exist = $false } PS C:\> Invoke-DscResource -Name VSCodeExtension -Method Set -Property $params -ModuleName Microsoft.VSCode.Dsc This removes the Visual Studio Code extension 'ms-python.python' .EXAMPLE PS C:\> $params = @{ Name = 'ms-python.python' Insiders = $true } PS C:\> Invoke-DscResource -Name VSCodeExtension -Method Set -Property $params -ModuleName Microsoft.VSCode.Dsc This installs the latest version of the Visual Studio Code extension 'ms-python.python' for the Insiders version of Visual Studio Code .EXAMPLE PS C:\> $params = @{ Name = 'dbaeumer.vscode-eslint' PreRelease = $true } PS C:\> Invoke-DscResource -Name VSCodeExtension -Method Set -Property $params -ModuleName Microsoft.VSCode.Dsc This installs the latest pre-release version of the Visual Studio Code extension 'dbaeumer.vscode-eslint' .EXAMPLE PS C:\> $params = @{ Name = 'C:\SharedExtensions\ms-python.python-2021.5.842923320.vsix' } PS C:\> Invoke-DscResource -Name VSCodeExtension -Method Set -Property $params -ModuleName Microsoft.VSCode.Dsc This installs the Visual Studio Code extension 'ms-python.python' from the specified VSIX file path. #> [DSCResource()] class VSCodeExtension { [DscProperty(Key)] [string] $Name [DscProperty()] [string] $Version [DscProperty()] [bool] $Exist = $true [DscProperty()] [bool] $PreRelease = $false [DscProperty()] [bool] $Insiders = $false static [hashtable] $InstalledExtensions VSCodeExtension() { } VSCodeExtension([string]$Name, [string]$Version) { $this.Name = $Name $this.Version = $Version } VSCodeExtension([string]$Name, [string]$Version, [bool]$Insiders) { $this.Name = $Name $this.Version = $Version $this.Insiders = $Insiders } [VSCodeExtension[]] Export([bool]$Insiders) { if ($Insiders) { $script:VSCodeCLIPath = Get-VSCodeCLIPath -Insiders } else { $script:VSCodeCLIPath = Get-VSCodeCLIPath } $extensionList = (Invoke-VSCode -Command '--list-extensions --show-versions') -split [Environment]::NewLine $results = [VSCodeExtension[]]::new($extensionList.length) for ($i = 0; $i -lt $extensionList.length; $i++) { $extensionName, $extensionVersion = $extensionList[$i] -split '@' $initialize = @{ Name = $extensionName Version = $extensionVersion PreRelease = (Get-PreReleaseFlag -Name $extensionName -Version $extensionVersion -Insiders:$Insiders) } if ($Insiders) { $initialize.Insiders = $true } $results[$i] = [VSCodeExtension]$initialize } return $results } [VSCodeExtension] Get() { if (Test-VsixFilePath -InputString $this.Name) { $manifestInfo = Get-VsixManifestInfo -Path $this.Name $this.Name = $manifestInfo.Name $this.Version = $manifestInfo.Version $this.PreRelease = $manifestInfo.PreRelease } [VSCodeExtension]::GetInstalledExtensions($this.Insiders) $currentState = [VSCodeExtension]::InstalledExtensions[$this.Name] if ($null -ne $currentState) { if ($null -ne $this.Version) { $versionState = $currentState | Where-Object { $_.Version -eq $this.Version } if ($versionState) { $finalState = [VSCodeExtension]::InstalledExtensions[$this.Name] } else { $currentState.Exist = $false $finalState = $currentState } } else { $finalState = [VSCodeExtension]::InstalledExtensions[$this.Name] } if ($currentState.PreRelease -ne $this.PreRelease) { $currentState.Exist = $false $finalState = $currentState } return $finalState } Write-Verbose -Message "Extension '$($this.Name)' with version '$($this.Version)' and pre-release '$($this.PreRelease)' does not exist." -Verbose return [VSCodeExtension]@{ Name = $this.Name Version = $this.Version Exist = $false PreRelease = $this.PreRelease Insiders = $this.Insiders } } [bool] Test() { $currentState = $this.Get() if ($currentState.Exist -ne $this.Exist) { return $false } if ($null -ne $this.Version -and $this.Version -ne $currentState.Version) { return $false } if ($null -ne $this.PreRelease -and $this.PreRelease -ne $currentState.PreRelease) { return $false } return $true } [void] Set() { if ($this.Test()) { return } if ($this.Exist) { $this.Install($false) } else { $this.Uninstall($false) } } #region VSCodeExtension helper functions static [void] GetInstalledExtensions([bool]$Insiders) { [VSCodeExtension]::InstalledExtensions = @{} $extension = [VSCodeExtension]::new() foreach ($extension in $extension.Export($Insiders)) { [VSCodeExtension]::InstalledExtensions[$extension.Name] = $extension } } [void] Install([bool] $preTest) { if ($preTest -and $this.Test()) { return } Install-VSCodeExtension -Name $this.Name -Version $this.Version -PreRelease $this.PreRelease [VSCodeExtension]::GetInstalledExtensions($this.Insiders) } [void] Install() { $this.Install($true) } [void] Uninstall([bool] $preTest) { Uninstall-VSCodeExtension -Name $this.Name [VSCodeExtension]::GetInstalledExtensions($this.Insiders) } [void] Uninstall() { $this.Uninstall($true) } #endregion VSCodeExtension helper functions } #endregion DSCResources # SIG # Begin signature block # MIIoKgYJKoZIhvcNAQcCoIIoGzCCKBcCAQExDzANBglghkgBZQMEAgEFADB5Bgor # BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG # KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCAji+wWHktYWR63 # c5FPDg/RlPNSAw5xsUQs4tIWPeqyaqCCDXYwggX0MIID3KADAgECAhMzAAAEhV6Z # 7A5ZL83XAAAAAASFMA0GCSqGSIb3DQEBCwUAMH4xCzAJBgNVBAYTAlVTMRMwEQYD # VQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNy # b3NvZnQgQ29ycG9yYXRpb24xKDAmBgNVBAMTH01pY3Jvc29mdCBDb2RlIFNpZ25p # bmcgUENBIDIwMTEwHhcNMjUwNjE5MTgyMTM3WhcNMjYwNjE3MTgyMTM3WjB0MQsw # CQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9u # ZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMR4wHAYDVQQDExVNaWNy # b3NvZnQgQ29ycG9yYXRpb24wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB # AQDASkh1cpvuUqfbqxele7LCSHEamVNBfFE4uY1FkGsAdUF/vnjpE1dnAD9vMOqy # 5ZO49ILhP4jiP/P2Pn9ao+5TDtKmcQ+pZdzbG7t43yRXJC3nXvTGQroodPi9USQi # 9rI+0gwuXRKBII7L+k3kMkKLmFrsWUjzgXVCLYa6ZH7BCALAcJWZTwWPoiT4HpqQ # hJcYLB7pfetAVCeBEVZD8itKQ6QA5/LQR+9X6dlSj4Vxta4JnpxvgSrkjXCz+tlJ # 67ABZ551lw23RWU1uyfgCfEFhBfiyPR2WSjskPl9ap6qrf8fNQ1sGYun2p4JdXxe # UAKf1hVa/3TQXjvPTiRXCnJPAgMBAAGjggFzMIIBbzAfBgNVHSUEGDAWBgorBgEE # AYI3TAgBBggrBgEFBQcDAzAdBgNVHQ4EFgQUuCZyGiCuLYE0aU7j5TFqY05kko0w # RQYDVR0RBD4wPKQ6MDgxHjAcBgNVBAsTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEW # MBQGA1UEBRMNMjMwMDEyKzUwNTM1OTAfBgNVHSMEGDAWgBRIbmTlUAXTgqoXNzci # tW2oynUClTBUBgNVHR8ETTBLMEmgR6BFhkNodHRwOi8vd3d3Lm1pY3Jvc29mdC5j # b20vcGtpb3BzL2NybC9NaWNDb2RTaWdQQ0EyMDExXzIwMTEtMDctMDguY3JsMGEG # CCsGAQUFBwEBBFUwUzBRBggrBgEFBQcwAoZFaHR0cDovL3d3dy5taWNyb3NvZnQu # Y29tL3BraW9wcy9jZXJ0cy9NaWNDb2RTaWdQQ0EyMDExXzIwMTEtMDctMDguY3J0 # MAwGA1UdEwEB/wQCMAAwDQYJKoZIhvcNAQELBQADggIBACjmqAp2Ci4sTHZci+qk # tEAKsFk5HNVGKyWR2rFGXsd7cggZ04H5U4SV0fAL6fOE9dLvt4I7HBHLhpGdE5Uj # Ly4NxLTG2bDAkeAVmxmd2uKWVGKym1aarDxXfv3GCN4mRX+Pn4c+py3S/6Kkt5eS # DAIIsrzKw3Kh2SW1hCwXX/k1v4b+NH1Fjl+i/xPJspXCFuZB4aC5FLT5fgbRKqns # WeAdn8DsrYQhT3QXLt6Nv3/dMzv7G/Cdpbdcoul8FYl+t3dmXM+SIClC3l2ae0wO # lNrQ42yQEycuPU5OoqLT85jsZ7+4CaScfFINlO7l7Y7r/xauqHbSPQ1r3oIC+e71 # 5s2G3ClZa3y99aYx2lnXYe1srcrIx8NAXTViiypXVn9ZGmEkfNcfDiqGQwkml5z9 # nm3pWiBZ69adaBBbAFEjyJG4y0a76bel/4sDCVvaZzLM3TFbxVO9BQrjZRtbJZbk # C3XArpLqZSfx53SuYdddxPX8pvcqFuEu8wcUeD05t9xNbJ4TtdAECJlEi0vvBxlm # M5tzFXy2qZeqPMXHSQYqPgZ9jvScZ6NwznFD0+33kbzyhOSz/WuGbAu4cHZG8gKn # lQVT4uA2Diex9DMs2WHiokNknYlLoUeWXW1QrJLpqO82TLyKTbBM/oZHAdIc0kzo # STro9b3+vjn2809D0+SOOCVZMIIHejCCBWKgAwIBAgIKYQ6Q0gAAAAAAAzANBgkq # hkiG9w0BAQsFADCBiDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24x # EDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlv # bjEyMDAGA1UEAxMpTWljcm9zb2Z0IFJvb3QgQ2VydGlmaWNhdGUgQXV0aG9yaXR5 # IDIwMTEwHhcNMTEwNzA4MjA1OTA5WhcNMjYwNzA4MjEwOTA5WjB+MQswCQYDVQQG # EwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwG # A1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSgwJgYDVQQDEx9NaWNyb3NvZnQg # Q29kZSBTaWduaW5nIFBDQSAyMDExMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIIC # CgKCAgEAq/D6chAcLq3YbqqCEE00uvK2WCGfQhsqa+laUKq4BjgaBEm6f8MMHt03 # a8YS2AvwOMKZBrDIOdUBFDFC04kNeWSHfpRgJGyvnkmc6Whe0t+bU7IKLMOv2akr # rnoJr9eWWcpgGgXpZnboMlImEi/nqwhQz7NEt13YxC4Ddato88tt8zpcoRb0Rrrg # OGSsbmQ1eKagYw8t00CT+OPeBw3VXHmlSSnnDb6gE3e+lD3v++MrWhAfTVYoonpy # 4BI6t0le2O3tQ5GD2Xuye4Yb2T6xjF3oiU+EGvKhL1nkkDstrjNYxbc+/jLTswM9 # sbKvkjh+0p2ALPVOVpEhNSXDOW5kf1O6nA+tGSOEy/S6A4aN91/w0FK/jJSHvMAh # dCVfGCi2zCcoOCWYOUo2z3yxkq4cI6epZuxhH2rhKEmdX4jiJV3TIUs+UsS1Vz8k # A/DRelsv1SPjcF0PUUZ3s/gA4bysAoJf28AVs70b1FVL5zmhD+kjSbwYuER8ReTB # w3J64HLnJN+/RpnF78IcV9uDjexNSTCnq47f7Fufr/zdsGbiwZeBe+3W7UvnSSmn # Eyimp31ngOaKYnhfsi+E11ecXL93KCjx7W3DKI8sj0A3T8HhhUSJxAlMxdSlQy90 # lfdu+HggWCwTXWCVmj5PM4TasIgX3p5O9JawvEagbJjS4NaIjAsCAwEAAaOCAe0w # ggHpMBAGCSsGAQQBgjcVAQQDAgEAMB0GA1UdDgQWBBRIbmTlUAXTgqoXNzcitW2o # ynUClTAZBgkrBgEEAYI3FAIEDB4KAFMAdQBiAEMAQTALBgNVHQ8EBAMCAYYwDwYD # VR0TAQH/BAUwAwEB/zAfBgNVHSMEGDAWgBRyLToCMZBDuRQFTuHqp8cx0SOJNDBa # BgNVHR8EUzBRME+gTaBLhklodHRwOi8vY3JsLm1pY3Jvc29mdC5jb20vcGtpL2Ny # bC9wcm9kdWN0cy9NaWNSb29DZXJBdXQyMDExXzIwMTFfMDNfMjIuY3JsMF4GCCsG # AQUFBwEBBFIwUDBOBggrBgEFBQcwAoZCaHR0cDovL3d3dy5taWNyb3NvZnQuY29t # L3BraS9jZXJ0cy9NaWNSb29DZXJBdXQyMDExXzIwMTFfMDNfMjIuY3J0MIGfBgNV # HSAEgZcwgZQwgZEGCSsGAQQBgjcuAzCBgzA/BggrBgEFBQcCARYzaHR0cDovL3d3 # dy5taWNyb3NvZnQuY29tL3BraW9wcy9kb2NzL3ByaW1hcnljcHMuaHRtMEAGCCsG # AQUFBwICMDQeMiAdAEwAZQBnAGEAbABfAHAAbwBsAGkAYwB5AF8AcwB0AGEAdABl # AG0AZQBuAHQALiAdMA0GCSqGSIb3DQEBCwUAA4ICAQBn8oalmOBUeRou09h0ZyKb # C5YR4WOSmUKWfdJ5DJDBZV8uLD74w3LRbYP+vj/oCso7v0epo/Np22O/IjWll11l # hJB9i0ZQVdgMknzSGksc8zxCi1LQsP1r4z4HLimb5j0bpdS1HXeUOeLpZMlEPXh6 # I/MTfaaQdION9MsmAkYqwooQu6SpBQyb7Wj6aC6VoCo/KmtYSWMfCWluWpiW5IP0 # wI/zRive/DvQvTXvbiWu5a8n7dDd8w6vmSiXmE0OPQvyCInWH8MyGOLwxS3OW560 # STkKxgrCxq2u5bLZ2xWIUUVYODJxJxp/sfQn+N4sOiBpmLJZiWhub6e3dMNABQam # ASooPoI/E01mC8CzTfXhj38cbxV9Rad25UAqZaPDXVJihsMdYzaXht/a8/jyFqGa # J+HNpZfQ7l1jQeNbB5yHPgZ3BtEGsXUfFL5hYbXw3MYbBL7fQccOKO7eZS/sl/ah # XJbYANahRr1Z85elCUtIEJmAH9AAKcWxm6U/RXceNcbSoqKfenoi+kiVH6v7RyOA # 9Z74v2u3S5fi63V4GuzqN5l5GEv/1rMjaHXmr/r8i+sLgOppO6/8MO0ETI7f33Vt # Y5E90Z1WTk+/gFcioXgRMiF670EKsT/7qMykXcGhiJtXcVZOSEXAQsmbdlsKgEhr # /Xmfwb1tbWrJUnMTDXpQzTGCGgowghoGAgEBMIGVMH4xCzAJBgNVBAYTAlVTMRMw # EQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVN # aWNyb3NvZnQgQ29ycG9yYXRpb24xKDAmBgNVBAMTH01pY3Jvc29mdCBDb2RlIFNp # Z25pbmcgUENBIDIwMTECEzMAAASFXpnsDlkvzdcAAAAABIUwDQYJYIZIAWUDBAIB # BQCgga4wGQYJKoZIhvcNAQkDMQwGCisGAQQBgjcCAQQwHAYKKwYBBAGCNwIBCzEO # MAwGCisGAQQBgjcCARUwLwYJKoZIhvcNAQkEMSIEIAHgJAuJQyDNxgt6CZU2FvHD # 0E/q2YLGmCqv3ZnbaXtcMEIGCisGAQQBgjcCAQwxNDAyoBSAEgBNAGkAYwByAG8A # cwBvAGYAdKEagBhodHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20wDQYJKoZIhvcNAQEB # BQAEggEADSrJOQWcfu4fdOHaxsSAVEGgG4fh2tmMNQWGqgzbZyh6+owOmWIIJ9a2 # RVhAVATD3Es5B1ROw0/BmWjnOolfgsxRoVVCY9ui9N2G3FAaKQQPht+KMfZnG7+6 # dtHM9NJnQ87Ljz5fFnZNUNOl69I1rQ7fGT71XKvBH12GycSsR2j49CosLI8mDA8z # VJplbIabiDPoOGrNmgVIk3+dvWY48KD9J9FM31O6iUlFPoHQBvCH6I5xtzTBg3vl # aiac5z0xWkTCy4p6dPF3AOHOXBVq8rzJHYxwzMA5MiJHQ5XV6pjPr0DcJ56TLmFk # kkToc1kQ4nN/Um77JhdMZVcPYcdgkKGCF5QwgheQBgorBgEEAYI3AwMBMYIXgDCC # F3wGCSqGSIb3DQEHAqCCF20wghdpAgEDMQ8wDQYJYIZIAWUDBAIBBQAwggFSBgsq # hkiG9w0BCRABBKCCAUEEggE9MIIBOQIBAQYKKwYBBAGEWQoDATAxMA0GCWCGSAFl # AwQCAQUABCC7QSLNIN9J84SJzXf2z+1R2TwCtJ0oLdaLDBpi0mf74AIGaEsfxa8q # GBMyMDI1MDcyNjIyMzUyOS43MDZaMASAAgH0oIHRpIHOMIHLMQswCQYDVQQGEwJV # UzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UE # ChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSUwIwYDVQQLExxNaWNyb3NvZnQgQW1l # cmljYSBPcGVyYXRpb25zMScwJQYDVQQLEx5uU2hpZWxkIFRTUyBFU046ODkwMC0w # NUUwLUQ5NDcxJTAjBgNVBAMTHE1pY3Jvc29mdCBUaW1lLVN0YW1wIFNlcnZpY2Wg # ghHqMIIHIDCCBQigAwIBAgITMwAAAg4syyh9lSB1YwABAAACDjANBgkqhkiG9w0B # AQsFADB8MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UE # BxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSYwJAYD # VQQDEx1NaWNyb3NvZnQgVGltZS1TdGFtcCBQQ0EgMjAxMDAeFw0yNTAxMzAxOTQz # MDNaFw0yNjA0MjIxOTQzMDNaMIHLMQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2Fz # aGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENv # cnBvcmF0aW9uMSUwIwYDVQQLExxNaWNyb3NvZnQgQW1lcmljYSBPcGVyYXRpb25z # MScwJQYDVQQLEx5uU2hpZWxkIFRTUyBFU046ODkwMC0wNUUwLUQ5NDcxJTAjBgNV # BAMTHE1pY3Jvc29mdCBUaW1lLVN0YW1wIFNlcnZpY2UwggIiMA0GCSqGSIb3DQEB # AQUAA4ICDwAwggIKAoICAQCs5t7iRtXt0hbeo9ME78ZYjIo3saQuWMBFQ7X4s9vo # oYRABTOf2poTHatx+EwnBUGB1V2t/E6MwsQNmY5XpM/75aCrZdxAnrV9o4Tu5sBe # pbbfehsrOWRBIGoJE6PtWod1CrFehm1diz3jY3H8iFrh7nqefniZ1SnbcWPMyNIx # uGFzpQiDA+E5YS33meMqaXwhdb01Cluymh/3EKvknj4dIpQZEWOPM3jxbRVAYN5J # 2tOrYkJcdDx0l02V/NYd1qkvUBgPxrKviq5kz7E6AbOifCDSMBgcn/X7RQw630Qk # zqhp0kDU2qei/ao9IHmuuReXEjnjpgTsr4Ab33ICAKMYxOQe+n5wqEVcE9OTyhmW # ZJS5AnWUTniok4mgwONBWQ1DLOGFkZwXT334IPCqd4/3/Ld/ItizistyUZYsml/C # 4ZhdALbvfYwzv31Oxf8NTmV5IGxWdHnk2Hhh4bnzTKosEaDrJvQMiQ+loojM7f5b # gdyBBnYQBm5+/iJsxw8k227zF2jbNI+Ows8HLeZGt8t6uJ2eVjND1B0YtgsBP0cs # BlnnI+4+dvLYRt0cAqw6PiYSz5FSZcbpi0xdAH/jd3dzyGArbyLuo69HugfGEEb/ # sM07rcoP1o3cZ8eWMb4+MIB8euOb5DVPDnEcFi4NDukYM91g1Dt/qIek+rtE88VS # 8QIDAQABo4IBSTCCAUUwHQYDVR0OBBYEFIVxRGlSEZE+1ESK6UGI7YNcEIjbMB8G # A1UdIwQYMBaAFJ+nFV0AXmJdg/Tl0mWnG1M1GelyMF8GA1UdHwRYMFYwVKBSoFCG # Tmh0dHA6Ly93d3cubWljcm9zb2Z0LmNvbS9wa2lvcHMvY3JsL01pY3Jvc29mdCUy # MFRpbWUtU3RhbXAlMjBQQ0ElMjAyMDEwKDEpLmNybDBsBggrBgEFBQcBAQRgMF4w # XAYIKwYBBQUHMAKGUGh0dHA6Ly93d3cubWljcm9zb2Z0LmNvbS9wa2lvcHMvY2Vy # dHMvTWljcm9zb2Z0JTIwVGltZS1TdGFtcCUyMFBDQSUyMDIwMTAoMSkuY3J0MAwG # A1UdEwEB/wQCMAAwFgYDVR0lAQH/BAwwCgYIKwYBBQUHAwgwDgYDVR0PAQH/BAQD # AgeAMA0GCSqGSIb3DQEBCwUAA4ICAQB14L2TL+L8OXLxnGSal2h30mZ7FsBFooiY # kUVOY05F9pnwPTVufEDGWEpNNy2OfaUHWIOoQ/9/rjwO0hS2SpB0BzMAk2gyz92N # GWOpWbpBdMvrrRDpiWZi/uLS4ZGdRn3P2DccYmlkNP+vaRAXvnv+mp27KgI79mJ9 # hGyCQbvtMIjkbYoLqK7sF7Wahn9rLjX1y5QJL4lvEy3QmA9KRBj56cEv/lAvzDq7 # eSiqRq/pCyqyc8uzmQ8SeKWyWu6DjUA9vi84QsmLjqPGCnH4cPyg+t95RpW+73sn # hew1iCV+wXu2RxMnWg7EsD5eLkJHLszUIPd+XClD+FTvV03GfrDDfk+45flH/eKR # Zc3MUZtnhLJjPwv3KoKDScW4iV6SbCRycYPkqoWBrHf7SvDA7GrH2UOtz1Wa1k27 # sdZgpG6/c9CqKI8CX5vgaa+A7oYHb4ZBj7S8u8sgxwWK7HgWDRByOH3CiJu4LJ8h # 3TiRkRArmHRp0lbNf1iAKuL886IKE912v0yq55t8jMxjBU7uoLsrYVIoKkzh+sAk # gkpGOoZL14+dlxVM91Bavza4kODTUlwzb+SpXsSqVx8nuB6qhUy7pqpgww1q4SNh # AxFnFxsxiTlaoL75GNxPR605lJ2WXehtEi7/+YfJqvH+vnqcpqCjyQ9hNaVzuOEH # X4MyuqcjwjCCB3EwggVZoAMCAQICEzMAAAAVxedrngKbSZkAAAAAABUwDQYJKoZI # hvcNAQELBQAwgYgxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAw # DgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24x # MjAwBgNVBAMTKU1pY3Jvc29mdCBSb290IENlcnRpZmljYXRlIEF1dGhvcml0eSAy # MDEwMB4XDTIxMDkzMDE4MjIyNVoXDTMwMDkzMDE4MzIyNVowfDELMAkGA1UEBhMC # VVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNV # BAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEmMCQGA1UEAxMdTWljcm9zb2Z0IFRp # bWUtU3RhbXAgUENBIDIwMTAwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoIC # AQDk4aZM57RyIQt5osvXJHm9DtWC0/3unAcH0qlsTnXIyjVX9gF/bErg4r25Phdg # M/9cT8dm95VTcVrifkpa/rg2Z4VGIwy1jRPPdzLAEBjoYH1qUoNEt6aORmsHFPPF # dvWGUNzBRMhxXFExN6AKOG6N7dcP2CZTfDlhAnrEqv1yaa8dq6z2Nr41JmTamDu6 # GnszrYBbfowQHJ1S/rboYiXcag/PXfT+jlPP1uyFVk3v3byNpOORj7I5LFGc6XBp # Dco2LXCOMcg1KL3jtIckw+DJj361VI/c+gVVmG1oO5pGve2krnopN6zL64NF50Zu # yjLVwIYwXE8s4mKyzbnijYjklqwBSru+cakXW2dg3viSkR4dPf0gz3N9QZpGdc3E # XzTdEonW/aUgfX782Z5F37ZyL9t9X4C626p+Nuw2TPYrbqgSUei/BQOj0XOmTTd0 # lBw0gg/wEPK3Rxjtp+iZfD9M269ewvPV2HM9Q07BMzlMjgK8QmguEOqEUUbi0b1q # GFphAXPKZ6Je1yh2AuIzGHLXpyDwwvoSCtdjbwzJNmSLW6CmgyFdXzB0kZSU2LlQ # +QuJYfM2BjUYhEfb3BvR/bLUHMVr9lxSUV0S2yW6r1AFemzFER1y7435UsSFF5PA # PBXbGjfHCBUYP3irRbb1Hode2o+eFnJpxq57t7c+auIurQIDAQABo4IB3TCCAdkw # EgYJKwYBBAGCNxUBBAUCAwEAATAjBgkrBgEEAYI3FQIEFgQUKqdS/mTEmr6CkTxG # NSnPEP8vBO4wHQYDVR0OBBYEFJ+nFV0AXmJdg/Tl0mWnG1M1GelyMFwGA1UdIARV # MFMwUQYMKwYBBAGCN0yDfQEBMEEwPwYIKwYBBQUHAgEWM2h0dHA6Ly93d3cubWlj # cm9zb2Z0LmNvbS9wa2lvcHMvRG9jcy9SZXBvc2l0b3J5Lmh0bTATBgNVHSUEDDAK # BggrBgEFBQcDCDAZBgkrBgEEAYI3FAIEDB4KAFMAdQBiAEMAQTALBgNVHQ8EBAMC # AYYwDwYDVR0TAQH/BAUwAwEB/zAfBgNVHSMEGDAWgBTV9lbLj+iiXGJo0T2UkFvX # zpoYxDBWBgNVHR8ETzBNMEugSaBHhkVodHRwOi8vY3JsLm1pY3Jvc29mdC5jb20v # cGtpL2NybC9wcm9kdWN0cy9NaWNSb29DZXJBdXRfMjAxMC0wNi0yMy5jcmwwWgYI # KwYBBQUHAQEETjBMMEoGCCsGAQUFBzAChj5odHRwOi8vd3d3Lm1pY3Jvc29mdC5j # b20vcGtpL2NlcnRzL01pY1Jvb0NlckF1dF8yMDEwLTA2LTIzLmNydDANBgkqhkiG # 9w0BAQsFAAOCAgEAnVV9/Cqt4SwfZwExJFvhnnJL/Klv6lwUtj5OR2R4sQaTlz0x # M7U518JxNj/aZGx80HU5bbsPMeTCj/ts0aGUGCLu6WZnOlNN3Zi6th542DYunKmC # VgADsAW+iehp4LoJ7nvfam++Kctu2D9IdQHZGN5tggz1bSNU5HhTdSRXud2f8449 # xvNo32X2pFaq95W2KFUn0CS9QKC/GbYSEhFdPSfgQJY4rPf5KYnDvBewVIVCs/wM # nosZiefwC2qBwoEZQhlSdYo2wh3DYXMuLGt7bj8sCXgU6ZGyqVvfSaN0DLzskYDS # PeZKPmY7T7uG+jIa2Zb0j/aRAfbOxnT99kxybxCrdTDFNLB62FD+CljdQDzHVG2d # Y3RILLFORy3BFARxv2T5JL5zbcqOCb2zAVdJVGTZc9d/HltEAY5aGZFrDZ+kKNxn # GSgkujhLmm77IVRrakURR6nxt67I6IleT53S0Ex2tVdUCbFpAUR+fKFhbHP+Crvs # QWY9af3LwUFJfn6Tvsv4O+S3Fb+0zj6lMVGEvL8CwYKiexcdFYmNcP7ntdAoGokL # jzbaukz5m/8K6TT4JDVnK+ANuOaMmdbhIurwJ0I9JZTmdHRbatGePu1+oDEzfbzL # 6Xu/OHBE0ZDxyKs6ijoIYn/ZcGNTTY3ugm2lBRDBcQZqELQdVTNYs6FwZvKhggNN # MIICNQIBATCB+aGB0aSBzjCByzELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hp # bmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jw # b3JhdGlvbjElMCMGA1UECxMcTWljcm9zb2Z0IEFtZXJpY2EgT3BlcmF0aW9uczEn # MCUGA1UECxMeblNoaWVsZCBUU1MgRVNOOjg5MDAtMDVFMC1EOTQ3MSUwIwYDVQQD # ExxNaWNyb3NvZnQgVGltZS1TdGFtcCBTZXJ2aWNloiMKAQEwBwYFKw4DAhoDFQBK # 6HY/ZWLnOcMEQsjkDAoB/JZWCKCBgzCBgKR+MHwxCzAJBgNVBAYTAlVTMRMwEQYD # VQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNy # b3NvZnQgQ29ycG9yYXRpb24xJjAkBgNVBAMTHU1pY3Jvc29mdCBUaW1lLVN0YW1w # IFBDQSAyMDEwMA0GCSqGSIb3DQEBCwUAAgUA7C+cUDAiGA8yMDI1MDcyNjE4MjYy # NFoYDzIwMjUwNzI3MTgyNjI0WjB0MDoGCisGAQQBhFkKBAExLDAqMAoCBQDsL5xQ # AgEAMAcCAQACAhjBMAcCAQACAhKhMAoCBQDsMO3QAgEAMDYGCisGAQQBhFkKBAIx # KDAmMAwGCisGAQQBhFkKAwKgCjAIAgEAAgMHoSChCjAIAgEAAgMBhqAwDQYJKoZI # hvcNAQELBQADggEBAAHPtMk62DYbEAxQpOoLG7hGJtlU/iQjjUrYT3uENJganL+M # lwzygFOW9d34TCmVKX/QezaCwj+lRU+6/Af2sZBM7C7jjMFBjdULbFrFc1K2ZdHy # vFAo+gw6oUH9Lyo+ikqh3ZuohgwlB8FcajP0kWvo78NWnAXRhxjsjvDLAPmR3xv8 # 0dZ4LDW3wZmthe89ZoAYgFc5m6GkW8AZITZZNie/ttx1IM44uswjaED2DdbNEb/1 # YQjwobQVxgWLr0NLJ1MEnLzQCQOUUXjYVKmTenrUqt9Dyp66iKCwzX9cSZyE2OW/ # y4IW6rOyEago0bKRBJwo7YqQoOY0w7jvr0vwqwUxggQNMIIECQIBATCBkzB8MQsw # CQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9u # ZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSYwJAYDVQQDEx1NaWNy # b3NvZnQgVGltZS1TdGFtcCBQQ0EgMjAxMAITMwAAAg4syyh9lSB1YwABAAACDjAN # BglghkgBZQMEAgEFAKCCAUowGgYJKoZIhvcNAQkDMQ0GCyqGSIb3DQEJEAEEMC8G # CSqGSIb3DQEJBDEiBCBR2MBasGxiwSJdqRXDpz/mjge8HnImZYimEjIw88ha4DCB # +gYLKoZIhvcNAQkQAi8xgeowgecwgeQwgb0EIAF0HXMl8OmBkK267mxobKSihwOd # P0eUNXQMypPzTxKGMIGYMIGApH4wfDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldh # c2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBD # b3Jwb3JhdGlvbjEmMCQGA1UEAxMdTWljcm9zb2Z0IFRpbWUtU3RhbXAgUENBIDIw # MTACEzMAAAIOLMsofZUgdWMAAQAAAg4wIgQgsWXCuFC79RJKDSHTWi/OxcTbKqjh # NHkiCkguVhmqoIIwDQYJKoZIhvcNAQELBQAEggIAnjmHIAIjG2fkbs9xXt5FSUCz # +RZNwwh1b0IXPRoFaCHg32BSjsnb2ZybBMLDT7S5JTassRnZWGRbuBYS98N07C92 # Ox+A1IPAyqnZ+HMHivCP9TCdUP45kSN+EpZhZu9ujBQhhCSsM8FALnNADzbTaJPe # u2pcq12iwPM0eiH4Qs1JcvNcBjarsSAZb5g7zavUZXWcopTFfm1kVtEUIHxaP200 # jpysRQQBKa2kL6fAAT98kSA9STECuCQ7HJWFRyRDpVl74Od8IwhwQqdgN8KbOAJC # NCnOLCFsV6vW1BsOzeKmz910aA4qT0j4Ps6ONHMdXvPaJJHW4KKyrnAsrqOHs2SG # PxLa2emT44AN9HZcPWg5HGBYeoR5YPXhTDkObulxlhReLNfTErRAVvdjfOPl5mms # RLKl1JaC/Y1WBQFU3PQBhdeBjhLsnJPWD43TAeBNe+0dSf8G8xIavEYkSAugNEeE # wTA4oKAQyPqfavXkWKlyhLj1gJnQs7nq5qiGdonbmUvC3NdZJcDJRKc/Q46i9S3U # cZpkxicFbX5a1ueKFlYv90PsWkEMp8yUDVPzFSaQBrIP2P6QIX8klOXbUzlSElcC # CIaCAkbwoP1PVaBmBVoP3m6gfmvn34E5b4c+9eCauVbIscX66ORWXMzkpM7r6myq # ZNpd66fYAggklFw33Qc= # SIG # End signature block |