AppvProvider.ps1
<#
Copyright (c) Virtual Engine Limited. All rights reserved. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. #> Import-LocalizedData -BindingVariable localized -FileName Resources.psd1; $providerName = 'AppV'; $packageSourceRootName = 'AppvPackageSourceRoot'; $persistencePath = Join-Path -Path $env:LOCALAPPDATA -ChildPath ('PackageManagement\{0}' -f $providerName); $registeredPackageSourcesPath = Join-Path -Path $persistencePath -ChildPath 'PackageSources.json'; $registeredPackageSources = New-Object -TypeName System.Collections.ArrayList -ArgumentList @(); function Resolve-PackageSource { <# Returns all registered provider package sources. This is called by the Get-PackageSource cmdlet. #> param () Write-Debug ('In {0} Provider - ''Resolve-PackageSource''.' -f $providerName); Get-RegisteredPackageSources; # if that's null or empty, they're asking for the whole list. if (-not ($request.PackageSources)) { # if there is nothing passed in, just return all the known package sources foreach($registeredPackageSource in $registeredPackageSources) { Write-Debug ('In {0} Provider - ''Resolve-PackageSource'' - Returning package source ''{1}''.' -f $providerName, $registeredPackageSource.Name); Write-Output $registeredPackageSource; } return; } ##TODO: Support $request.Options['Location'] # otherwise, they are requesing one or more sources back. foreach ($source in ($request.PackageSources)) { Write-Debug ('In {0} Provider - ''Resolve-PackageSource'' - Requesting package source ''{1}''.' -f $providerName, $source); $isFound = $false; # otherwise, for each item, check if we have a source by that name and return it. foreach ($packageSource in $registeredPackageSources) { Write-Debug ('In {0} Provider - ''Resolve-PackageSource'' - Checking package source ''{1}''.' -f $providerName, $packageSource.Name); if ($packageSource.Name -eq $source) { Write-Debug ('In {0} Provider - ''Resolve-PackageSource'' - Returning package source ''{1}''.' -f $providerName, $packageSource.Name); Write-Output $packageSource; $isFound = $true; break; } } if ($isFound) { continue; } # if it's not a valid source location send a warning back. Write-Warning ($localized.InvalidSourcePathWarning -f $source); } #end foreach source Write-Debug ('Done In {0} Provider - ''Resolve-PackageSource''.' -f $providerName); } #end function Resolve-PackageSources function Dump-RequestObjectOptions { param () if ($request.Options) { foreach ($optionKey in $request.Options.Keys) { Write-Debug ('In {0} Provider - Dynamic Option ''{1}'' => ''{2}''.' -f $providerName, $optionKey, $request.Options[$optionKey]); } } } function Set-RegisteredPackageSources { <# Persists registered package sources to disk. #> param () Write-Debug ('In {0} Provider - ''Set-RegisteredPackageSources''.' -f $providerName); Write-Debug ('Current Package Source Count: ''{0}''.' -f $RegisteredPackageSources.Count); if (-not (Test-Path -Path $registeredPackageSourcesPath -PathType Leaf)) { [Ref] $null = New-Item -Path $registeredPackageSourcesPath -ItemType File -Force; } ## Cannot pipe $registeredPackageSources into ConvertTo-Json if the object is empty! ConvertTo-Json -InputObject $registeredPackageSources | Set-Content -Path $registeredPackageSourcesPath -Force; } #end function Set-RegisteredPackageSources function Get-RegisteredPackageSources { <# Loads registered package sources from disk. #> param () $script:registeredPackageSources = New-Object -TypeName System.Collections.ArrayList -ArgumentList @(); if (Test-Path -Path $registeredPackageSourcesPath -PathType Leaf) { $packageSources = Get-Content -Path $registeredPackageSourcesPath -Raw | ConvertFrom-Json; foreach ($source in $packageSources) { Write-Debug ('In {0} Provider - ''Get-RegisteredPackageSources'' - Loading package source ''{1}''.' -f $providerName, $source.Name); $packageSourceParam = @{ Name = $source.Name; Location = $source.Location; Trusted = $source.IsTrusted; Registered = $source.IsRegistered; Valid = $source.IsValidated; } $packageSource = New-PackageSource @packageSourceParam; $script:registeredPackageSources.Add($packageSource); } } [Ref] $null = Resolve-AppvPackageSourceRoot; } #end function Get-RegisteredPackageSources function Register-PackageSource { <# .SYNOPSIS Registers a new provider package source #> param ( [Parameter(Mandatory)] [System.String] $Name, [Parameter(Mandatory)] [System.String] $Location, [Parameter()] [System.Boolean] $Trusted ) Write-Debug ('In {0} Provider - ''Register-PackageSource'' - Name ''{1}''.' -f $providerName, $Name); if (-not (Test-Path -Path $Location -PathType Container)) { Write-Error -Message ($localized.InvalidDirectoryPathError -f $Location) -Category InvalidArgument; return; } # remove any existing object first. for ($i = $registeredPackageSources.Count; $i -gt 0; $i--) { $packageSource = $registeredPackageSources[$i -1]; Write-Debug ('In {0} Provider - ''Register-PackageSource'' - Checking existing package source ''{1}''.' -f $providerName, $packageSource.Name); if ($packageSource.Name -eq $Name) { Write-Debug ('In {0} Provider - ''Register-PackageSource'' - Removing existing package source ''{1}''.' -f $providerName, $packageSource.Name); $script:registeredPackageSources.Remove($packageSource); } } $location = Resolve-Path -Path $Location; $packageSource = New-PackageSource -Name $Name -Location $location -Trusted $trusted -Registered $true -Valid $true; $script:registeredPackageSources.Add($packageSource); Set-RegisteredPackageSources; Write-Output $packageSource; } #end function Register-PackageSource function Unregister-PackageSource { <# .SYNOPSIS Removes the specified provider package source by name or location. #> param ( [Parameter(Mandatory)] [System.String] $Name ) Write-Debug ('In {0} Provider - ''Unregister-PackageSource''.' -f $providerName); for ($i = $registeredPackageSources.Count; $i -gt 0; $i--) { $packageSource = $registeredPackageSources[$i -1]; Write-Debug ('In {0} Provider - ''Unregister-PackageSource'' - Checking existing package source ''{1}''.' -f $providerName, $packageSource.Name); if ($packageSource.Name -eq $name -or $packageSource.Location -eq $Name ) { Write-Debug ('In {0} Provider - ''Unregister-PackageSource'' - Removing source ''{1}'' location ''{2}''.' -f $providerName, $packageSource.Name, $packageSource.Location); Write-Output $packageSource; $script:registeredPackageSources.Remove($packageSource); Set-RegisteredPackageSources; } } #end foreach registeredPackageSources } #end function Unregister-PackageSource function Get-AppvAppxManifest { <# .SYNOPSIS Returns the contents of an App-V package AppxManifest file. #> param ( [Parameter(Mandatory)] [System.String] $Path ) try { Write-Debug ('In {0} Provider - ''Get-AppvAppxManifest'' - Loading .Net Framework assemblies.' -f $providerName); [Ref] $null = [System.Reflection.Assembly]::LoadWithPartialName('System.IO.Compression'); [Ref] $null = [System.Reflection.Assembly]::LoadWithPartialName('System.IO.Compression.FileSystem'); Write-Debug ('In {0} Provider - ''Get-AppvAppxManifest'' - Opening ''{1}'' .appv archive.' -f $providerName, $Path); $appvArchive = New-Object System.IO.Compression.ZipArchive(New-Object System.IO.FileStream($Path, [System.IO.FileMode]::Open)); $appvArchiveEntry = $appvArchive.GetEntry('AppxManifest.xml'); if ($appvArchiveEntry -ne $null) { $xmlDocument = New-Object System.Xml.XmlDocument; $xmlDocument.Load($appvArchiveEntry.Open()); Write-Output $xmlDocument; } } catch { Write-Error -Message ($localized.ReadAppvManifestError -f $Path) -Exception $_.Exception -Category InvalidOperation; } finally { if ($xmlDocument -ne $null) { $xmlDocument = $null; } if ($appvArchiveEntry -ne $null) { $appvArchive.Dispose(); } } } #end function Get-AppvAppxManifest function Find-Package { <# .SYNOPSIS Searches a registered provider package sources for packages #> param ( [Parameter()] [System.String[]] $Names, [Parameter()] [System.String] $RequiredVersion, [Parameter()] [System.String] $MinimumVersion, [Parameter()] [System.String] $MaximumVersion ) Write-Debug ('In {0} Provider - ''Find-Package''.' -f $providerName); $filterparam = $request.Options['filter']; ##TODO: What is the filter parameter? if ([System.String]::IsNullOrEmpty($RequiredVersion)) { if ([System.String]::IsNullOrEmpty($MinimumVersion)) { $minVersion = New-Object -TypeName System.Version -ArgumentList 0, 0, 0, 0; } else { $minVersion = New-Object -TypeName System.Version -ArgumentList $MinimumVersion; } if ([System.String]::IsNullOrEmpty($MaximumVersion)) {$maxVersion = (New-Object -TypeName System.Version -ArgumentList ([int]::MaxValue),([int]::MaxValue)).ToString(); } else { $maxVersion = New-Object -TypeName System.Version -ArgumentList $MaximumVersion; } } else { $minVersion = New-Object -TypeName System.Version -ArgumentList $RequiredVersion; $maxVersion = $minVersion; } Write-Debug ('In {0} Provider - ''Find-Package'' - Checking minimum ''{1}'' and maximum ''{2}''.' -f $providerName, $minVersion.ToString(), $maxVersion.ToString()); foreach ($registeredPackageSource in $registeredPackageSources) { Write-Debug ('In {0} Provider - ''Find-Package'' - Checking package source ''{1}''.' -f $providerName, $registeredPackageSource.Name); $shouldProcessPackageSource = $false; if ([System.String]::IsNullOrEmpty($request.Options['Source'])) { Write-Debug ('In {0} Provider - ''Find-Package'' - Source parameter not specified.' -f $providerName); $shouldProcessPackageSource = $true; } elseif ($registeredPackageSource.Name -eq $request.Options['Source']) { Write-Debug ('In {0} Provider - ''Find-Package'' - Source parameter ''{1}'' matches package source ''{2}''.' -f $providerName, $request.Options['Source'], $registeredPackageSource.Name); $shouldProcessPackageSource = $true; } else { Write-Debug ('In {0} Provider - ''Find-Package'' - Skipping package source ''{1}''.' -f $providerName, $registeredPackageSource); } if ($shouldProcessPackageSource) { Write-Debug ('In {0} Provider - ''Find-Package'' - Searching package source ''{1}''.' -f $providerName, $registeredPackageSource.Name); Get-ChildItem -Path $registeredPackageSource.Location -Include *.appv -Recurse | ForEach-Object { Write-Debug ('In {0} Provider - ''Find-Package'' - Reading package ''{1}''.' -f $providerName, $_.FullName); $appxManifest = Get-AppvAppxManifest -Path $_.FullName; $appxVersion = New-Object -TypeName System.Version -ArgumentList $appxManifest.Package.Identity.Version; Write-Debug ('In {0} Provider - ''Find-Package'' - Discovered Package Id ''{1}''.' -f $providerName, $appxManifest.Package.Identity.PackageId); foreach ($name in $Names) { if (-not $name.Contains('*')) { $name = '*{0}*' -f $name; } if ($appxManifest.Package.Properties.DisplayName -like $name) { if ($appxVersion -lt $minVersion) { Write-Debug ('In {0} Provider - ''Find-Package'' - Package version ''{1}'' is less than ''{2}''.' -f $providerName, $appxVersion, $minVersion); } elseif ($appxVersion -gt $maxVersion) { Write-Debug ('In {0} Provider - ''Find-Package'' - Package version ''{1}'' is greater than ''{2}''.' -f $providerName, $appxVersion, $maxVersion); } else { $softwareIdentityParam = @{ FastPackageReference = $_.FullName; Name = $appxManifest.Package.Properties.DisplayName; Version = $appxManifest.Package.Identity.Version; VersionScheme = 'semver'; Source = $registeredPackageSource.Name; Summary = $appxManifest.Package.Properties.AppVPackageDescription; SearchKey = $appxManifest.Package.Identity.PackageId; FullPath = $_.FullName; FromTrustedSource = $registeredPackageSource.IsTrusted; }; Write-Output (New-SoftwareIdentity @softwareIdentityParam); } } #end if DisplayName -like name } #end foreach name } #end foreach-object } #end if package source } #end foreach registeredPackageSource } #end function Find-Package function Get-InstalledPackage { <# .SYNOPSIS Returns all applications registered in the App-V client. #> param ( [Parameter()] [System.String] $Name, [Parameter()] [System.String] $RequiredVersion, [Parameter()] [System.String] $MinimumVersion, [Parameter()] [System.String] $MaximumVersion ##TODO: Implement version semantics ) Write-Debug ('In {0} Provider - ''Get-InstalledPackage'' {1} {2}.' -f $providerName, $InstalledPackages.Count, $Name); if ($Name -eq $null -or $Name -eq "") { foreach ($appvPackage in Get-AppvClientPackage -All) { $softwareIdentityParam = @{ FastPackageReference = $appvPackage.Path; Name = $appvPackage.Name; Version = $appvPackage.Version; VersionScheme = 'semver'; Source = $appvPackage.Path; SearchKey = $appvPackage.PackageId; }; New-SoftwareIdentity @softwareIdentityParam; } } else { # We're after a specific package if (-not $Name.Contains('*')) { $Name = '*{0}*' -f $Name; } foreach ($appvPackage in Get-AppvClientPackage -All) { if ($appvPackage.Name -like $Name) { $softwareIdentityParam = @{ FastPackageReference = $appvPackage.Path; Name = $appvPackage.Name; Version = $appvPackage.Version; VersionScheme = 'semver'; Source = $appvPackage.Path; SearchKey = $appvPackage.PackageId; }; New-SoftwareIdentity @softwareIdentityParam; } } #end foreach appvPackage } #end else } #end function Get-InstalledPackage function Get-PackageProviderName { <# .SYNOPSIS Returns the name of the PackageManagement provider. #> param () return $providerName; } #end function Get-PackageProviderName function Initialize-Provider { <# .SYNOPSIS Initializes the provider upon startup. #> param () Write-Debug ('In {0} Provider - ''Initialize-Provider''.' -f $providerName); Get-RegisteredPackageSources; } #end function Initialize-Provider function Resolve-AppvPackageSourceRoot { <# .SYNOPSIS Resolves the AppV client package source root from the registry and if valid, automatically adds it as a trusted Appv provider package source. #> param () Write-Debug ('In {0} Provider - ''Resolve-AppvPackageSourceRoot''.' -f $providerName); if ((Get-ItemProperty -Path 'HKLM:\SOFTWARE\Microsoft\AppV\Client\Streaming')) { $packageSourceRoot = (Get-ItemProperty -Path 'HKLM:\SOFTWARE\Microsoft\AppV\Client\Streaming').PackageSourceRoot; if ([System.String]::IsNullOrEmpty($packageSourceRoot) -or (-not (Test-Path -Path $packageSourceRoot -PathType Container))) { Write-Debug ('In {0} Provider - ''Resolve-AppvPackageSourceRoot'' - Appv Package Source Root ''{1}'' is invalid. ' -f $providerName, $packageSourceRoot); Unregister-PackageSource -Name $packageSourceRootName; } else { Write-Debug ('In {0} Provider - ''Resolve-AppvPackageSourceRoot'' - Adding Package Source Root ''{1}''.' -f $providerName, $packageSourceRoot); Register-PackageSource -Name $packageSourceRootName -Location $packageSourceRoot -Trusted $true; } } #end if packageSourceRoot else { Unregister-PackageSource -Name $packageSourceRootName; } } #end function Resolve-AppvPackageSourceRoot function Install-Package { <# .SYNOPSIS Registers the App-V application package located in the path specified. #> param ( [Parameter(Mandatory)] [System.String] $FastPackageReference ) Write-Debug ('In {0} Provider - ''Install-Package'' - Package reference ''{1}''.' -f $providerName, $FastPackageReference); if ($request.Options['Global'] -eq $true -and $request.Options['DynamicUserConfigurationPath'] -ne $null) { Write-Error -Message $localized.InvalidArgumentsError -Category InvalidArgument; } $publishAppvClientPackageParam = @{ }; $addAppvClientPackageParam = @{ Path = $FastPackageReference; }; if ($request.Options['DynamicDeploymentConfiguration']) { $dynamicDeploymentConfiguration = $request.Options['DynamicDeploymentConfiguration']; Write-Debug ('In {0} Provider - ''Install-Package'' - Setting Dynamic Deployment Configuration ''{1}''.' -f $providerName, $dynamicDeploymentConfiguration); $addAppvClientPackageParam['DynamicDeploymentConfiguration'] = $dynamicDeploymentConfiguration; } if ($request.Options['Global'] -ne $null -and $request.Options['Global'] -eq $true) { $publishAppvClientPackageParam['Global'] = $true; } if ($request.Options['DynamicUserConfigurationPath']) { $dynamicUserConfigurationPath = $request.Options['DynamicUserConfigurationPath']; Write-Debug ('In {0} Provider - ''Install-Package'' - Setting Dynamic User Configuration Path ''{1}''.' -f $providerName, $dynamicUserConfigurationPath); $publishAppvClientPackageParam['DynamicUserConfigurationPath'] = $dynamicUserConfigurationPath; } if ($request.Options['DynamicUserConfigurationType']) { $dynamicUserConfigurationType = $request.Options['DynamicUserConfigurationType']; Write-Debug ('In {0} Provider - ''Install-Package'' - Setting Dynamic User Configuration Type ''{1}''.' -f $providerName, $dynamicUserConfigurationType); $publishAppvClientPackageParam['DynamicUserConfigurationType'] = $dynamicUserConfigurationType; } try { $appvPackage = Add-AppvClientPackage @addAppvClientPackageParam -ErrorAction Stop | Publish-AppvClientPackage @publishAppvClientPackageParam; if ($request.Options['Mount'] -ne $null -and $request.Options['Mount'] -eq $true) { Write-Debug ('In {0} Provider - ''Install-Package'' - Mounting package ''{1}''.' -f $providerName, $appvPackage.Name); $appvPackage | Mount-AppvClientPackage; } $softwareIdentityParam = @{ FastPackageReference = $appvPackage.Path; Name = $appvPackage.Name; Version = $appvPackage.Version; VersionScheme = 'semver'; Source = $appvPackage.Path; SearchKey = $appvPackage.PackageId; } Write-Output (New-SoftwareIdentity @softwareIdentityParam); } catch { Remove-AppvClientPackage -Package $appvPackage -ErrorAction SilentlyContinue; Write-Error -Message $localized.InstallAppvPackageError -Exception $_.Exception -Category NotInstalled; } } #end function Install-Package function Uninstall-Package { <# .SYNOPSIS Registers the App-V application package located in the path specified. #> param ( [Parameter(Mandatory)] [System.String] $FastPackageReference ) Write-Debug ('In {0} Provider - ''Uninstall-Package'' - Reference ''{1}''.' -f $providerName, $FastPackageReference); foreach ($appvPackage in (Get-AppvClientPackage -All | Where-Object { $_.Path -eq $FastPackageReference })) { if ($appvPackage.InUse) { Write-Error -Message ($localized.AppvPackageIsInUseError -f $appvPackage.Name) -Category InvalidOperation; } else { Write-Debug ('Removing package ''{0}''.' -f $appvPackage.Name); $softwareIdentityParam = @{ FastPackageReference = $appvPackage.Path; Name = $appvPackage.Name; Version = $appvPackage.Version; VersionScheme = 'semver'; Source = $appvPackage.Path; SearchKey = $appvPackage.PackageId; } $package = New-SoftwareIdentity @softwareIdentityParam; Remove-AppvClientPackage -PackageId $appvPackage.PackageId -VersionId $appvPackage.VersionId; Write-Output $package; } #end if } #end foreach appvPackage } #end function Uninstall-Package function Get-Feature { <# .SYNOPSIS Returns metadata about what features are implemented by the Provider. #> param () Write-Debug ('In {0} Provider - ''Get-Feature''.' -f $providerName); Write-Output (New-feature 'extensions' @('appv')); } #end function Get-Feature function Get-DynamicOptions { <# .SYNOPSIS Provides dynamic parameters for Package, Source and/or Install operations. #> param ( [Microsoft.PackageManagement.MetaProvider.PowerShell.OptionCategory] $category ) Write-Debug ('In {0} Provider - ''Get-DynamicOption'' for category ''{1}''.' -f $providerName, $category); switch ($category) { Provider { # options when the user is trying to specify a provider } Package { # options when the user is trying to specify a package } Source { #options when the user is trying to specify a source } Install { #options for installation/uninstallation Write-Output (New-DynamicOption -Category $category -Name Global -ExpectedType ([Microsoft.PackageManagement.MetaProvider.PowerShell.OptionType]::Switch) -IsRequired $false); Write-Output (New-DynamicOption -Category $category -Name Mount -ExpectedType ([Microsoft.PackageManagement.MetaProvider.PowerShell.OptionType]::Switch) -IsRequired $false); Write-Output (New-DynamicOption -Category $category -Name DynamicDeploymentConfiguration -ExpectedType ([Microsoft.PackageManagement.MetaProvider.PowerShell.OptionType]::String) -IsRequired $false); Write-Output (New-DynamicOption -Category $category -Name DynamicUserConfigurationPath -ExpectedType ([Microsoft.PackageManagement.MetaProvider.PowerShell.OptionType]::String) -IsRequired $false); Write-Output (New-DynamicOption -Category $category -Name DynamicUserConfigurationType -ExpectedType ([Microsoft.PackageManagement.MetaProvider.PowerShell.OptionType]::String) -IsRequired $false -PermittedValues @('UseDeploymentConfiguration','UseExistingConfiguration','UseNoConfiguration')); } } #end switch category } #end function Get-DynamicOptions # SIG # Begin signature block # MIIaogYJKoZIhvcNAQcCoIIakzCCGo8CAQExCzAJBgUrDgMCGgUAMGkGCisGAQQB # gjcCAQSgWzBZMDQGCisGAQQBgjcCAR4wJgIDAQAABBAfzDtgWUsITrck0sYpfvNR # AgEAAgEAAgEAAgEAAgEAMCEwCQYFKw4DAhoFAAQUTvuppu3zxvY2XUiQJVsq5Ykm # nqqgghXYMIID7jCCA1egAwIBAgIQfpPr+3zGTlnqS5p31Ab8OzANBgkqhkiG9w0B # AQUFADCBizELMAkGA1UEBhMCWkExFTATBgNVBAgTDFdlc3Rlcm4gQ2FwZTEUMBIG # A1UEBxMLRHVyYmFudmlsbGUxDzANBgNVBAoTBlRoYXd0ZTEdMBsGA1UECxMUVGhh # d3RlIENlcnRpZmljYXRpb24xHzAdBgNVBAMTFlRoYXd0ZSBUaW1lc3RhbXBpbmcg # Q0EwHhcNMTIxMjIxMDAwMDAwWhcNMjAxMjMwMjM1OTU5WjBeMQswCQYDVQQGEwJV # UzEdMBsGA1UEChMUU3ltYW50ZWMgQ29ycG9yYXRpb24xMDAuBgNVBAMTJ1N5bWFu # dGVjIFRpbWUgU3RhbXBpbmcgU2VydmljZXMgQ0EgLSBHMjCCASIwDQYJKoZIhvcN # AQEBBQADggEPADCCAQoCggEBALGss0lUS5ccEgrYJXmRIlcqb9y4JsRDc2vCvy5Q # WvsUwnaOQwElQ7Sh4kX06Ld7w3TMIte0lAAC903tv7S3RCRrzV9FO9FEzkMScxeC # i2m0K8uZHqxyGyZNcR+xMd37UWECU6aq9UksBXhFpS+JzueZ5/6M4lc/PcaS3Er4 # ezPkeQr78HWIQZz/xQNRmarXbJ+TaYdlKYOFwmAUxMjJOxTawIHwHw103pIiq8r3 # +3R8J+b3Sht/p8OeLa6K6qbmqicWfWH3mHERvOJQoUvlXfrlDqcsn6plINPYlujI # fKVOSET/GeJEB5IL12iEgF1qeGRFzWBGflTBE3zFefHJwXECAwEAAaOB+jCB9zAd # BgNVHQ4EFgQUX5r1blzMzHSa1N197z/b7EyALt0wMgYIKwYBBQUHAQEEJjAkMCIG # CCsGAQUFBzABhhZodHRwOi8vb2NzcC50aGF3dGUuY29tMBIGA1UdEwEB/wQIMAYB # Af8CAQAwPwYDVR0fBDgwNjA0oDKgMIYuaHR0cDovL2NybC50aGF3dGUuY29tL1Ro # YXd0ZVRpbWVzdGFtcGluZ0NBLmNybDATBgNVHSUEDDAKBggrBgEFBQcDCDAOBgNV # HQ8BAf8EBAMCAQYwKAYDVR0RBCEwH6QdMBsxGTAXBgNVBAMTEFRpbWVTdGFtcC0y # MDQ4LTEwDQYJKoZIhvcNAQEFBQADgYEAAwmbj3nvf1kwqu9otfrjCR27T4IGXTdf # plKfFo3qHJIJRG71betYfDDo+WmNI3MLEm9Hqa45EfgqsZuwGsOO61mWAK3ODE2y # 0DGmCFwqevzieh1XTKhlGOl5QGIllm7HxzdqgyEIjkHq3dlXPx13SYcqFgZepjhq # IhKjURmDfrYwggSjMIIDi6ADAgECAhAOz/Q4yP6/NW4E2GqYGxpQMA0GCSqGSIb3 # DQEBBQUAMF4xCzAJBgNVBAYTAlVTMR0wGwYDVQQKExRTeW1hbnRlYyBDb3Jwb3Jh # dGlvbjEwMC4GA1UEAxMnU3ltYW50ZWMgVGltZSBTdGFtcGluZyBTZXJ2aWNlcyBD # QSAtIEcyMB4XDTEyMTAxODAwMDAwMFoXDTIwMTIyOTIzNTk1OVowYjELMAkGA1UE # BhMCVVMxHTAbBgNVBAoTFFN5bWFudGVjIENvcnBvcmF0aW9uMTQwMgYDVQQDEytT # eW1hbnRlYyBUaW1lIFN0YW1waW5nIFNlcnZpY2VzIFNpZ25lciAtIEc0MIIBIjAN # BgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAomMLOUS4uyOnREm7Dv+h8GEKU5Ow # mNutLA9KxW7/hjxTVQ8VzgQ/K/2plpbZvmF5C1vJTIZ25eBDSyKV7sIrQ8Gf2Gi0 # jkBP7oU4uRHFI/JkWPAVMm9OV6GuiKQC1yoezUvh3WPVF4kyW7BemVqonShQDhfu # ltthO0VRHc8SVguSR/yrrvZmPUescHLnkudfzRC5xINklBm9JYDh6NIipdC6Anqh # d5NbZcPuF3S8QYYq3AhMjJKMkS2ed0QfaNaodHfbDlsyi1aLM73ZY8hJnTrFxeoz # C9Lxoxv0i77Zs1eLO94Ep3oisiSuLsdwxb5OgyYI+wu9qU+ZCOEQKHKqzQIDAQAB # o4IBVzCCAVMwDAYDVR0TAQH/BAIwADAWBgNVHSUBAf8EDDAKBggrBgEFBQcDCDAO # BgNVHQ8BAf8EBAMCB4AwcwYIKwYBBQUHAQEEZzBlMCoGCCsGAQUFBzABhh5odHRw # Oi8vdHMtb2NzcC53cy5zeW1hbnRlYy5jb20wNwYIKwYBBQUHMAKGK2h0dHA6Ly90 # cy1haWEud3Muc3ltYW50ZWMuY29tL3Rzcy1jYS1nMi5jZXIwPAYDVR0fBDUwMzAx # oC+gLYYraHR0cDovL3RzLWNybC53cy5zeW1hbnRlYy5jb20vdHNzLWNhLWcyLmNy # bDAoBgNVHREEITAfpB0wGzEZMBcGA1UEAxMQVGltZVN0YW1wLTIwNDgtMjAdBgNV # HQ4EFgQURsZpow5KFB7VTNpSYxc/Xja8DeYwHwYDVR0jBBgwFoAUX5r1blzMzHSa # 1N197z/b7EyALt0wDQYJKoZIhvcNAQEFBQADggEBAHg7tJEqAEzwj2IwN3ijhCcH # bxiy3iXcoNSUA6qGTiWfmkADHN3O43nLIWgG2rYytG2/9CwmYzPkSWRtDebDZw73 # BaQ1bHyJFsbpst+y6d0gxnEPzZV03LZc3r03H0N45ni1zSgEIKOq8UvEiCmRDoDR # EfzdXHZuT14ORUZBbg2w6jiasTraCXEQ/Bx5tIB7rGn0/Zy2DBYr8X9bCT2bW+IW # yhOBbQAuOA2oKY8s4bL0WqkBrxWcLC9JG9siu8P+eJRRw4axgohd8D20UaF5Mysu # e7ncIAkTcetqGVvP6KUwVyyJST+5z3/Jvz4iaGNTmr1pdKzFHTx/kuDDvBzYBHUw # ggaUMIIFfKADAgECAhAG8BXYFUYj6XmzRgEaZJSVMA0GCSqGSIb3DQEBBQUAMG8x # CzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3 # dy5kaWdpY2VydC5jb20xLjAsBgNVBAMTJURpZ2lDZXJ0IEFzc3VyZWQgSUQgQ29k # ZSBTaWduaW5nIENBLTEwHhcNMTMwNDE3MDAwMDAwWhcNMTUwNzE2MTIwMDAwWjBg # MQswCQYDVQQGEwJHQjEPMA0GA1UEBxMGT3hmb3JkMR8wHQYDVQQKExZWaXJ0dWFs # IEVuZ2luZSBMaW1pdGVkMR8wHQYDVQQDExZWaXJ0dWFsIEVuZ2luZSBMaW1pdGVk # MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA1dxm3r1cUKp7rYZBDAeo # Lm0iLIgYuzeg7tC2mt7kEJfvGiSVx4/d3pYw2/GpDB08JjsoAYIfhWOuGtUf0RRy # 5QcyrfWDCmLfUApf83/GJZrATWs1OPzdYEsLzVrx7ZtvcCVvlEIyG4RJmhSG2mZS # 6P0D68a2/U4QmcNEGpnbTyszHds8BnVL1D3oQP+rcXN2jDP83/rECmGgYGexvRkV # K/+HHrporgkT4KRMbrWXMRPrLQazIFeg1mnm1UtjxTXN7IPaY97qwxhxPqwpL3DH # PdF/6+rC1ZQZ27akf5qporAlsftUe3URHFmmJ8NrLivANrwco9BY3If4iAvz9ipl # mQIDAQABo4IDOTCCAzUwHwYDVR0jBBgwFoAUe2jOKarAF75JeuHlP9an90WPNTIw # HQYDVR0OBBYEFNQ3nxxDKFobighYZExYqzXq8SQTMA4GA1UdDwEB/wQEAwIHgDAT # BgNVHSUEDDAKBggrBgEFBQcDAzBzBgNVHR8EbDBqMDOgMaAvhi1odHRwOi8vY3Js # My5kaWdpY2VydC5jb20vYXNzdXJlZC1jcy0yMDExYS5jcmwwM6AxoC+GLWh0dHA6 # Ly9jcmw0LmRpZ2ljZXJ0LmNvbS9hc3N1cmVkLWNzLTIwMTFhLmNybDCCAcQGA1Ud # IASCAbswggG3MIIBswYJYIZIAYb9bAMBMIIBpDA6BggrBgEFBQcCARYuaHR0cDov # L3d3dy5kaWdpY2VydC5jb20vc3NsLWNwcy1yZXBvc2l0b3J5Lmh0bTCCAWQGCCsG # AQUFBwICMIIBVh6CAVIAQQBuAHkAIAB1AHMAZQAgAG8AZgAgAHQAaABpAHMAIABD # AGUAcgB0AGkAZgBpAGMAYQB0AGUAIABjAG8AbgBzAHQAaQB0AHUAdABlAHMAIABh # AGMAYwBlAHAAdABhAG4AYwBlACAAbwBmACAAdABoAGUAIABEAGkAZwBpAEMAZQBy # AHQAIABDAFAALwBDAFAAUwAgAGEAbgBkACAAdABoAGUAIABSAGUAbAB5AGkAbgBn # ACAAUABhAHIAdAB5ACAAQQBnAHIAZQBlAG0AZQBuAHQAIAB3AGgAaQBjAGgAIABs # AGkAbQBpAHQAIABsAGkAYQBiAGkAbABpAHQAeQAgAGEAbgBkACAAYQByAGUAIABp # AG4AYwBvAHIAcABvAHIAYQB0AGUAZAAgAGgAZQByAGUAaQBuACAAYgB5ACAAcgBl # AGYAZQByAGUAbgBjAGUALjCBggYIKwYBBQUHAQEEdjB0MCQGCCsGAQUFBzABhhho # dHRwOi8vb2NzcC5kaWdpY2VydC5jb20wTAYIKwYBBQUHMAKGQGh0dHA6Ly9jYWNl # cnRzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydEFzc3VyZWRJRENvZGVTaWduaW5nQ0Et # MS5jcnQwDAYDVR0TAQH/BAIwADANBgkqhkiG9w0BAQUFAAOCAQEAPsyUuxkYkEGE # 1bl4g3Muy5QxQq8frp34BPf+Sm6E9J915eBizW72ofbm08O9NkQvszbT4GTZaO/o # SExSDbLIxHI98zi7AavVPuRpmVnfoF55yVomh/BYAU8vu0M7FvUeIhSAUfz0Q8PK # wT5U+SdNoE7+xgxd4zHjBA3kUo3TZ+R/+MDd2Hzv6vrgxUfGeQfBCwafdEjD4pHr # 0kvXcPq6VnQpsv92P3wvgsCrsTKIgtaNIfkGe5eCcTQ7pYTBauZl+XmyFvyiADKo # 6Dng4jyuxYRP3EdCGVlZK7sEmiz1Y2f3zh0xoF58B3xXDnRJxo8ArlEAG8KzXn6w # ryaA1vbgITCCBqMwggWLoAMCAQICEA+oSQYV1wCgviF2/cXsbb0wDQYJKoZIhvcN # AQEFBQAwZTELMAkGA1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZMBcG # A1UECxMQd3d3LmRpZ2ljZXJ0LmNvbTEkMCIGA1UEAxMbRGlnaUNlcnQgQXNzdXJl # ZCBJRCBSb290IENBMB4XDTExMDIxMTEyMDAwMFoXDTI2MDIxMDEyMDAwMFowbzEL # MAkGA1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZMBcGA1UECxMQd3d3 # LmRpZ2ljZXJ0LmNvbTEuMCwGA1UEAxMlRGlnaUNlcnQgQXNzdXJlZCBJRCBDb2Rl # IFNpZ25pbmcgQ0EtMTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAJx8 # +aCPCsqJS1OaPOwZIn8My/dIRNA/Im6aT/rO38bTJJH/qFKT53L48UaGlMWrF/R4 # f8t6vpAmHHxTL+WD57tqBSjMoBcRSxgg87e98tzLuIZARR9P+TmY0zvrb2mkXAEu # sWbpprjcBt6ujWL+RCeCqQPD/uYmC5NJceU4bU7+gFxnd7XVb2ZklGu7iElo2NH0 # fiHB5sUeyeCWuAmV+UuerswxvWpaQqfEBUd9YCvZoV29+1aT7xv8cvnfPjL93Sos # MkbaXmO80LjLTBA1/FBfrENEfP6ERFC0jCo9dAz0eotyS+BWtRO2Y+k/Tkkj5wYW # 8CWrAfgoQebH1GQ7XasCAwEAAaOCA0MwggM/MA4GA1UdDwEB/wQEAwIBhjATBgNV # HSUEDDAKBggrBgEFBQcDAzCCAcMGA1UdIASCAbowggG2MIIBsgYIYIZIAYb9bAMw # ggGkMDoGCCsGAQUFBwIBFi5odHRwOi8vd3d3LmRpZ2ljZXJ0LmNvbS9zc2wtY3Bz # LXJlcG9zaXRvcnkuaHRtMIIBZAYIKwYBBQUHAgIwggFWHoIBUgBBAG4AeQAgAHUA # cwBlACAAbwBmACAAdABoAGkAcwAgAEMAZQByAHQAaQBmAGkAYwBhAHQAZQAgAGMA # bwBuAHMAdABpAHQAdQB0AGUAcwAgAGEAYwBjAGUAcAB0AGEAbgBjAGUAIABvAGYA # IAB0AGgAZQAgAEQAaQBnAGkAQwBlAHIAdAAgAEMAUAAvAEMAUABTACAAYQBuAGQA # IAB0AGgAZQAgAFIAZQBsAHkAaQBuAGcAIABQAGEAcgB0AHkAIABBAGcAcgBlAGUA # bQBlAG4AdAAgAHcAaABpAGMAaAAgAGwAaQBtAGkAdAAgAGwAaQBhAGIAaQBsAGkA # dAB5ACAAYQBuAGQAIABhAHIAZQAgAGkAbgBjAG8AcgBwAG8AcgBhAHQAZQBkACAA # aABlAHIAZQBpAG4AIABiAHkAIAByAGUAZgBlAHIAZQBuAGMAZQAuMBIGA1UdEwEB # /wQIMAYBAf8CAQAweQYIKwYBBQUHAQEEbTBrMCQGCCsGAQUFBzABhhhodHRwOi8v # b2NzcC5kaWdpY2VydC5jb20wQwYIKwYBBQUHMAKGN2h0dHA6Ly9jYWNlcnRzLmRp # Z2ljZXJ0LmNvbS9EaWdpQ2VydEFzc3VyZWRJRFJvb3RDQS5jcnQwgYEGA1UdHwR6 # MHgwOqA4oDaGNGh0dHA6Ly9jcmwzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydEFzc3Vy # ZWRJRFJvb3RDQS5jcmwwOqA4oDaGNGh0dHA6Ly9jcmw0LmRpZ2ljZXJ0LmNvbS9E # aWdpQ2VydEFzc3VyZWRJRFJvb3RDQS5jcmwwHQYDVR0OBBYEFHtozimqwBe+SXrh # 5T/Wp/dFjzUyMB8GA1UdIwQYMBaAFEXroq/0ksuCMS1Ri6enIZ3zbcgPMA0GCSqG # SIb3DQEBBQUAA4IBAQB7ch1k/4jIOsG36eepxIe725SS15BZM/orh96oW4AlPxOP # m4MbfEPE5ozfOT7DFeyw2jshJXskwXJduEeRgRNG+pw/alE43rQly/Cr38UoAVR5 # EEYk0TgPJqFhkE26vSjmP/HEqpv22jVTT8nyPdNs3CPtqqBNZwnzOoA9PPs2TJDn # dqTd8jq/VjUvokxl6ODU2tHHyJFqLSNPNzsZlBjU1ZwQPNWxHBn/j8hrm574rpyZ # lnjRzZxRFVtCJnJajQpKI5JA6IbeIsKTOtSbaKbfKX8GuTwOvZ/EhpyCR0JxMoYJ # mXIJeUudcWn1Qf9/OXdk8YSNvosesn1oo6WQsQz/MYIENDCCBDACAQEwgYMwbzEL # MAkGA1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZMBcGA1UECxMQd3d3 # LmRpZ2ljZXJ0LmNvbTEuMCwGA1UEAxMlRGlnaUNlcnQgQXNzdXJlZCBJRCBDb2Rl # IFNpZ25pbmcgQ0EtMQIQBvAV2BVGI+l5s0YBGmSUlTAJBgUrDgMCGgUAoHgwGAYK # KwYBBAGCNwIBDDEKMAigAoAAoQKAADAZBgkqhkiG9w0BCQMxDAYKKwYBBAGCNwIB # BDAcBgorBgEEAYI3AgELMQ4wDAYKKwYBBAGCNwIBFTAjBgkqhkiG9w0BCQQxFgQU # QyJ1YiXgwBGdaDif9nJ6sNi08fUwDQYJKoZIhvcNAQEBBQAEggEAhqs0Y+AYdQim # MZs3IMtk7OTuVUkQ+aQTNRFE6YU71nyy94G5i2nMg1bXbVgDw3fmF71rGUd/3Y4Q # wQQt2WURmzWyGsXTxW4p4UTA0fVE2zw/l8U2G5PQ+VNrTeK8OHIo0rNnXvmFydvw # llew3Qt7RIKvKFcQK0PkNb09NloKuhMFF2ZfjgLg8UwMa++bTCceQAXTPS14kFYf # jrNhSw+9sSitVgX4dDlGlX9bfFscMbPgQpE2m+qCx+/vRhlsGrLcDm2UjdySDYlW # YZKc2yu3Kc5DXhvFvC+Vw7Du0CQQoP0hAOp3wW66d1QA0bcm0neT1a5dsYNl9Wjw # RUqBjF92vaGCAgswggIHBgkqhkiG9w0BCQYxggH4MIIB9AIBATByMF4xCzAJBgNV # BAYTAlVTMR0wGwYDVQQKExRTeW1hbnRlYyBDb3Jwb3JhdGlvbjEwMC4GA1UEAxMn # U3ltYW50ZWMgVGltZSBTdGFtcGluZyBTZXJ2aWNlcyBDQSAtIEcyAhAOz/Q4yP6/ # NW4E2GqYGxpQMAkGBSsOAwIaBQCgXTAYBgkqhkiG9w0BCQMxCwYJKoZIhvcNAQcB # MBwGCSqGSIb3DQEJBTEPFw0xNTA2MDMwODQ0NDdaMCMGCSqGSIb3DQEJBDEWBBSJ # mI9R6aY22ZZb1ojOjaQZo9oKUDANBgkqhkiG9w0BAQEFAASCAQAV585hpZNoePUq # JGrvDj18y0t44I3BoohJVVuyEfiY7+WS0xVMnEXG2mks5xw6afFecJbD+1ALDPm4 # sBgmB273DRvozYbieiKLUS8c1WrN/wZS7iI7Xuh3u3x+QWs9+0FFA7acMhBclKXq # ey+fiZ+bER9lddYiKVLhwWyaIxkyYdIERv+SY1aTg93670J9GcDjJQKmgSbDv8TF # wdtErsFDebZWW+7+M7/YsaVy38nnHbmzDKa+jxq/jlh9/4qCy1G7oGDK36dlfu+r # U35yg04BOpLNKoiBHqmwGq0INXKC2rvk/vzSZic/UA7yUZKZq2z7fBJBRkCbKMGt # NV8MBWe3 # SIG # End signature block |