AnyPackage.PSResourceGet.psm1
# Copyright (c) Thomas Nieto - All Rights Reserved # You may use, distribute and modify this code under the # terms of the MIT license. using module AnyPackage using module Microsoft.PowerShell.PSResourceGet using namespace System.Collections.Generic using namespace System.Threading using namespace AnyPackage.Provider using namespace AnyPackage.Feedback using namespace Microsoft.PowerShell.PSResourceGet.UtilClasses [PackageProvider('PSResourceGet')] class PSResourceGetProvider : PackageProvider, IGetPackage, IFindPackage, IInstallPackage, ISavePackage, IUninstallPackage, IUpdatePackage, IPublishPackage, IGetSource, ISetSource, ICommandNotFound { [PackageProviderInfo] Initialize([PackageProviderInfo] $providerInfo) { return [PSResourceGetProviderInfo]::new($providerInfo) } [IEnumerable[CommandNotFoundFeedback]] FindPackage([CommandNotFoundContext] $context, [CancellationToken] $token) { $dict = [Dictionary[string, CommandNotFoundFeedback]]::new([StringComparer]::OrdinalIgnoreCase) $packages = $this.ProviderInfo.CommandCache[$context.Command] foreach ($package in $packages) { if (!$dict.ContainsKey($package.Name)) { $feedback = [CommandNotFoundFeedback]::new($package.Name, $this.ProviderInfo) $dict.Add($package.Name, $feedback) } } return $dict.Values } #region GetPackage [void] GetPackage([PackageRequest] $request) { $params = @{ Name = $request.Name ErrorAction = 'SilentlyContinue' } if ($request.Version) { $params['Version'] = $request.Version } $request.DynamicParameters | ConvertTo-Hashtable -Hashtable $params -IsBound Get-InstalledPSResource @params | Write-Package -Request $request } #endregion #region FindPackage [void] FindPackage([PackageRequest] $request) { $params = @{ Name = $request.Name Prerelease = $request.Prerelease ErrorAction = 'SilentlyContinue' } if ($request.Version) { $params['Version'] = $request.Version } if ($request.Source) { $params['Repository'] = $request.Source } $request.DynamicParameters | ConvertTo-Hashtable -Hashtable $params -Exclude 'Latest' -IsBound $resources = Find-PSResource @params if ($request.DynamicParameters.Latest) { $resources = $resources | Get-Latest } $resources | Write-Package -Request $request } #endregion #region InstallPackage [void] InstallPackage([PackageRequest] $request) { $params = @{ Name = $request.Name Prerelease = $request.Prerelease ErrorAction = 'SilentlyContinue' } if ($request.Version) { $params['Version'] = $request.Version } if ($request.Source) { $params['Repository'] = $request.Source } $installParams = @{ } $request.DynamicParameters | ConvertTo-Hashtable -Hashtable $installParams -IsBound Find-PSResource @params | Get-Latest | Install-PSResource @installParams -TrustRepository -PassThru | Write-Package -Request $request } #endregion #region SavePackage [void] SavePackage([PackageRequest] $request) { $params = @{ Name = $request.Name Prerelease = $request.Prerelease ErrorAction = 'SilentlyContinue' } if ($request.Version) { $params['Version'] = $request.Version } if ($request.Source) { $params['Repository'] = $request.Source } $saveParams = @{ } $request.DynamicParameters | ConvertTo-Hashtable -Hashtable $saveParams -IsBound Find-PSResource @params | Get-Latest | Save-PSResource @saveParams -Path $request.Path -TrustRepository -PassThru | Write-Package -Request $request } #endregion #region UninstallPackage [void] UninstallPackage([PackageRequest] $request) { $params = @{ Name = $request.Name ErrorAction = 'SilentlyContinue' } if ($request.Version) { $params['Version'] = $request.Version } $uninstallParams = @{ } $request.DynamicParameters | ConvertTo-Hashtable -Hashtable $uninstallParams -IsBound # Issue to get PassThru parameter added # https://github.com/PowerShell/PSResourceGet/issues/667 # Prerelease parameter causes it to silently fail # https://github.com/PowerShell/PSResourceGet/issues/842 Get-InstalledPSResource @params | ForEach-Object { try { $_ | Uninstall-PSResource @uninstallParams -ErrorAction Stop $_ | Write-Package -Request $request } catch { $_ } } } #endregion #region UpdatePackage [void] UpdatePackage([PackageRequest] $request) { $params = @{ Prerelease = $request.Prerelease } if ($request.Version) { $params['Version'] = $request.Version } if ($request.Source) { $params['Repository'] = $request.Source } $updateParams = @{ } $request.DynamicParameters | ConvertTo-Hashtable -Hashtable $updateParams -IsBound # Find-PSResource pipeline input # https://github.com/PowerShell/PSResourceGet/issues/666 Get-InstalledPSResource -Name $request.Name -ErrorAction SilentlyContinue | Select-Object -ExpandProperty Name -Unique | Find-PSResource @params -ErrorAction SilentlyContinue | Select-Object -ExpandProperty Name -Unique | Update-PSResource @params @updateParams -TrustRepository -PassThru | Write-Package -Request $request } #endregion #region PublishPackage [void] PublishPackage([PackageRequest] $request) { $params = @{ Path = $request.Path } if ($request.Source) { $params['Repository'] = $request.Source } $request.DynamicParameters | ConvertTo-Hashtable -Hashtable $params -IsBound try { # PassThru parameter # https://github.com/PowerShell/PSResourceGet/issues/718 Publish-PSResource @params -ErrorAction Stop $params.Remove('Path') $params['Name'] = Get-Item -Path $request.Path | Select-Object -ExpandProperty BaseName Find-PSResource @params | Get-Latest | Write-Package -Request $request } catch { throw $_ } } #endregion #region Source [void] GetSource([SourceRequest] $sourceRequest) { Get-PSResourceRepository -Name $sourceRequest.Name | Write-Source -SourceRequest $sourceRequest } [void] SetSource([SourceRequest] $sourceRequest) { $params = @{ PassThru = $true } if ($sourceRequest.Location) { $params.Uri = $sourceRequest.Location } if ($null -ne $sourceRequest.Trusted) { $params.Trusted = $sourceRequest.Trusted } $sourceRequest.DynamicParameters | ConvertTo-Hashtable -Hashtable $params -IsBound Get-PSResourceRepository -Name $sourceRequest.Name | Set-PSResourceRepository @params | Write-Source -SourceRequest $sourceRequest } [void] RegisterSource([SourceRequest] $sourceRequest) { $params = @{ Trusted = $sourceRequest.Trusted PassThru = $true } if ($sourceRequest.DynamicParameters.PSGallery) { $params['PSGallery'] = $true } else { $params['Name'] = $sourceRequest.Name $params['Uri'] = $sourceRequest.Location } $sourceRequest.DynamicParameters | ConvertTo-Hashtable -Hashtable $params -Exclude 'PSGallery' -IsBound Register-PSResourceRepository @params | Write-Source -SourceRequest $sourceRequest } [void] UnregisterSource([SourceRequest] $sourceRequest) { Get-PSResourceRepository -Name $sourceRequest.Name | Unregister-PSResourceRepository -PassThru | Write-Source -SourceRequest $sourceRequest } #endregion [object] GetDynamicParameters([string] $commandName) { return $(switch ($commandName) { 'Get-Package' { return [GetPackageDynamicParameters]::new() } 'Find-Package' { return [FindPackageDynamicParameters]::new() } 'Install-Package' { return [InstallPackageDynamicParameters]::new() } 'Publish-Package' { return [PublishPackageDynamicParameters]::new() } 'Save-Package' { return [SavePackageDynamicParameters]::new() } 'Uninstall-Package' { return [UninstallPackageDynamicParameters]::new() } 'Update-Package' { return [UpdatePackageDynamicParameters]::new() } 'Set-PackageSource' { return [SetPackageSourceDynamicParameters]::new() } 'Register-PackageSource' { return [RegisterPackageSourceDynamicParameters]::new() } default { return $null } }) } } class PSResourceGetProviderInfo : PackageProviderInfo { [Dictionary[string, List[PSResourceInfo]]] $CommandCache = [Dictionary[string, List[PSResourceInfo]]]::new([StringComparer]::OrdinalIgnoreCase) PSResourceGetProviderInfo([PackageProviderInfo] $providerInfo) : base($providerInfo) { if ([Runspace]::DefaultRunspace.Name -eq $this.FullName) { $this.SetCommandCache() } } [void] SetCommandCache() { $packages = Find-PSResource -Name * -Type Module foreach ($package in $packages) { $commands = $package.Tags | Where-Object { $_ -like "PSCommand*" } | ForEach-Object { $_ -replace 'PSCommand_', '' } foreach ($command in $commands) { if ($this.CommandCache.ContainsKey($command)) { $this.CommandCache[$command] += $package } else { $list = [List[PSResourceInfo]]::new() $list += $package $this.CommandCache.Add($command, $package) } } } } } class GetPackageDynamicParameters { [Parameter()] [string] $Path [Parameter()] [ScopeType] $Scope } class FindPackageDynamicParameters { [Parameter()] [switch] $Credential [Parameter()] [string[]] $Tag [Parameter()] [ResourceType] $Type [Parameter()] [switch] $Latest [Parameter()] [switch] $IncludeDependencies } class PublishPackageDynamicParameters { [Parameter()] [string] $ApiKey [Parameter()] [switch] $Credential [Parameter()] [string] $DestinationPath [Parameter()] [switch] $SkipDependenciesCheck [Parameter()] [switch] $SkipModuleManifestValidate [Parameter()] [uri] $Proxy [Parameter()] [pscredential] $ProxyCredential } class InstallDynamicParameters { [Parameter()] [switch] $AuthenticodeCheck [Parameter()] [switch] $Credential [Parameter()] [switch] $SkipDependencyCheck [Parameter()] [string] $TemporaryPath } class InstallUpdateDynamicParameters : InstallDynamicParameters { [Parameter()] [switch] $AcceptLicense [Parameter()] [ScopeType] $Scope } class InstallPackageDynamicParameters : InstallUpdateDynamicParameters { [Parameter()] [switch] $Reinstall # Install-PSResource -NoClobber fails # https://github.com/PowerShell/PSResourceGet/issues/946 # [Parameter()] # [switch] $NoClobber } class SavePackageDynamicParameters : InstallDynamicParameters { # Pipeline input fails with -AsNupkg # https://github.com/PowerShell/PSResourceGet/issues/948 # [Parameter()] # [switch] $AsNupkg # Pipeline input fails with -IncludeXml # https://github.com/PowerShell/PSResourceGet/issues/949 # [Parameter()] # [switch] $IncludeXml } class UninstallPackageDynamicParameters { [Parameter()] [switch] $SkipDependencyCheck [Parameter()] [ScopeType] $Scope } class UpdatePackageDynamicParameters : InstallUpdateDynamicParameters { [Parameter()] [switch] $Force } class SetPackageSourceDynamicParameters { [Parameter()] [int] $Priority [Parameter()] [PSCredentialInfo] $CredentialInfo } class RegisterPackageSourceDynamicParameters : SetPackageSourceDynamicParameters { [Parameter(ParameterSetName = 'PSGallery')] [switch] $PSGallery } [guid] $id = 'c9a39544-274b-4935-9cad-7423e8c47e6b' [PackageProviderManager]::RegisterProvider($id, [PSResourceGetProvider], $MyInvocation.MyCommand.ScriptBlock.Module) $MyInvocation.MyCommand.ScriptBlock.Module.OnRemove = { [PackageProviderManager]::UnregisterProvider($id) } function ConvertTo-Hashtable { [CmdletBinding()] [OutputType([hashtable])] [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseSingularNouns', '')] param ( [Parameter(Position = 0, ValueFromPipeline, ValueFromPipelineByPropertyName)] [object] $InputObject, [Parameter()] [hashtable] $Hashtable = @{ }, [Parameter()] [string[]] $Exclude, [Parameter()] [switch] $IsBound ) process { if ($null -eq $InputObject) { return } $properties = $InputObject | Get-Member -MemberType Properties | Where-Object Name -notin $Exclude | Select-Object -ExpandProperty Name foreach ($property in $properties) { if ($IsBound -and -not $InputObject.$property) { continue } $Hashtable[$property] = $InputObject.$property } $Hashtable } } function Get-Latest { [CmdletBinding()] param( [Parameter(Mandatory, ValueFromPipeline)] [PSResourceInfo] $Resource ) begin { $resources = [List[PSResourceInfo]]::new() } process { $resources.Add($resource) } end { $resources | Group-Object -Property Name | ForEach-Object { # PSResourceGet returns the latest as the first object $_.Group | Select-Object -First 1 } } } function Write-Source { [CmdletBinding()] param ( [Parameter(Mandatory, ValueFromPipeline)] [PSRepositoryInfo] $Source, [Parameter(Mandatory)] [SourceRequest] $SourceRequest ) process { $sourceInfo = [PackageSourceInfo]::new($Source.Name, $Source.Uri, [bool]::Parse($Source.Trusted), @{ Priority = $Source.Priority CredentialInfo = $Source.CredentialInfo }, $SourceRequest.ProviderInfo) $SourceRequest.WriteSource($sourceInfo) } } function Write-Package { [CmdletBinding()] param ( [Parameter(Mandatory, ValueFromPipeline)] [PSResourceInfo] $Resource, [Parameter(Mandatory)] [PackageRequest] $Request ) begin { $sources = Get-PackageSource -Provider AnyPackage.PSResourceGet\PSResourceGet } process { $ht = ConvertTo-Hashtable $resource $deps = [List[PackageDependency]]::new() foreach ($dep in $resource.Dependencies) { $versionRange = [PackageVersionRange]::new($dep.VersionRange, $true) $dependency = [PackageDependency]::new($dep.Name, $versionRange) $deps.Add($dependency) } $source = $sources | Where-Object Name -eq $resource.Repository # Blank RepositorySourceLocation # https://github.com/PowerShell/PSResourceGet/issues/1052 if (-not $source -and $resource.RepositorySourceLocation) { $source = [PackageSourceInfo]::new($resource.Repository, $resource.RepositorySourceLocation, $false, $Request.ProviderInfo) } if ($resource.Prerelease) { $version = "{0}-{1}" -f $resource.Version, $resource.Prerelease } else { $version = $resource.Version } $package = [PackageInfo]::new($resource.Name, $version, $source, $resource.Description, $deps, $ht, $Request.ProviderInfo) $Request.WritePackage($package) } } # SIG # Begin signature block # MIIlQgYJKoZIhvcNAQcCoIIlMzCCJS8CAQExDzANBglghkgBZQMEAgEFADB5Bgor # BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG # KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCDbka/I59e2UBfe # UrOJkdGSHv0BEi1G+EFQKaxBwabmQ6CCHtcwggW2MIIEHqADAgECAhEApPLKEVUi # zMqDeJHsqFBBojANBgkqhkiG9w0BAQwFADBUMQswCQYDVQQGEwJHQjEYMBYGA1UE # ChMPU2VjdGlnbyBMaW1pdGVkMSswKQYDVQQDEyJTZWN0aWdvIFB1YmxpYyBDb2Rl # IFNpZ25pbmcgQ0EgUjM2MB4XDTIyMTIwNjAwMDAwMFoXDTI1MTIwNTIzNTk1OVow # TDELMAkGA1UEBhMCVVMxDzANBgNVBAgMBkthbnNhczEVMBMGA1UECgwMVGhvbWFz # IE5pZXRvMRUwEwYDVQQDDAxUaG9tYXMgTmlldG8wggGiMA0GCSqGSIb3DQEBAQUA # A4IBjwAwggGKAoIBgQDKp+8DxaXlIDZh+gMApqRZ4hD4IDTdRkUEZZqSnUwxQnD5 # k8VpbDeqrEkmww2TKUeOWQbSRMT70T/8R8+ld2GrwzLDQ83k5mrGXIXSXCHRiLIZ # UnLmVjDtf/4FXTEni+acu8dddbCS0GAAzDfnINFgVRQR9GdoaYLqoipRVpLVRBzb # eR2gSCtzwmpuX1d4NR58NvfUkfzNxy0kaLfqamHl9ae0JHyGvyzrHgOUyt2ttA/F # idNo8KudwGau/gfyko5egOAURpEqgJHrcaekTVio+GRQs2IFNHIvNnfF9Rm7kn5w # PZuxWL4UaV5xGyYWLupny3Lpp1n+XlVg6UgYZX25BWwbdbfLvsDPHnlbPB2L0WgC # CeG6ZOZjB2dmFaYHhwozRzcvX0FLoYXv1/Vo9QviUTm2QIqb/gaxt3xl/rtcCZOj # ly518iHNR2f1ClS+dPiD7KO5r2owmUsaMZiPVeMnD8/dKT8c9jPhyRBntbX9V6ho # RXD6J2mf5SKSql8pwC8CAwEAAaOCAYkwggGFMB8GA1UdIwQYMBaAFA8qyyCHKLjs # b0iuK1SmKaoXpM0MMB0GA1UdDgQWBBQOV7tpbOOE2AnSg3DwNoU56VePWzAOBgNV # HQ8BAf8EBAMCB4AwDAYDVR0TAQH/BAIwADATBgNVHSUEDDAKBggrBgEFBQcDAzBK # BgNVHSAEQzBBMDUGDCsGAQQBsjEBAgEDAjAlMCMGCCsGAQUFBwIBFhdodHRwczov # L3NlY3RpZ28uY29tL0NQUzAIBgZngQwBBAEwSQYDVR0fBEIwQDA+oDygOoY4aHR0 # cDovL2NybC5zZWN0aWdvLmNvbS9TZWN0aWdvUHVibGljQ29kZVNpZ25pbmdDQVIz # Ni5jcmwweQYIKwYBBQUHAQEEbTBrMEQGCCsGAQUFBzAChjhodHRwOi8vY3J0LnNl # Y3RpZ28uY29tL1NlY3RpZ29QdWJsaWNDb2RlU2lnbmluZ0NBUjM2LmNydDAjBggr # BgEFBQcwAYYXaHR0cDovL29jc3Auc2VjdGlnby5jb20wDQYJKoZIhvcNAQEMBQAD # ggGBADWsCefpJbT4oKIh1SXIR4hnfGN/YqI6g3aFIntNgoiurd5uxLkow2WSpfaC # iXjWjMubDpBvynVvaBsRfcrwT0WGBTwz4miuITPKKkIXYIVb5dCf33ghMJ4UD+zH # P73ioUtDV545Yeb5WVLrPN8ZCC++u0kX+jck30w56LCvng6gDpPHU/KNroQxENjG # pcycTf7Gq9tkcEVbGersD6R64NhI6r8uDH6l0s5NMep1x4yTs0MBPmlB6ZHK+88Y # GDVdfTSYbLpQuvmLkEMHNaPOL0YyTjJJbeaHvGuTfqQb17HDLFJGd70CKrQumvcI # CrJM9il3B+bNsSKjTLQtGbp5JdJ5paUahybiJKyZlDw5QPRrFEnwHiWaTI/9zfRc # iZyX4kYnLkPpp8rlZFXBqfIzf0gRhgCjVVZoMwZHWqyZNL7gS4C6uvEn2t/3BqM7 # 548OuoPLH7W07orz8T7iP7aNYtJpAttZOhAbqB2EKoY3qcKClqKOMQGvc12/16mA # KE7E+TCCBhQwggP8oAMCAQICEHojrtpTaZYPkcg+XPTH4z8wDQYJKoZIhvcNAQEM # BQAwVzELMAkGA1UEBhMCR0IxGDAWBgNVBAoTD1NlY3RpZ28gTGltaXRlZDEuMCwG # A1UEAxMlU2VjdGlnbyBQdWJsaWMgVGltZSBTdGFtcGluZyBSb290IFI0NjAeFw0y # MTAzMjIwMDAwMDBaFw0zNjAzMjEyMzU5NTlaMFUxCzAJBgNVBAYTAkdCMRgwFgYD # VQQKEw9TZWN0aWdvIExpbWl0ZWQxLDAqBgNVBAMTI1NlY3RpZ28gUHVibGljIFRp # bWUgU3RhbXBpbmcgQ0EgUjM2MIIBojANBgkqhkiG9w0BAQEFAAOCAY8AMIIBigKC # AYEAzZjYQ0GrboIr7PYzfiY05ImM0+8iEoBUPu8mr4wOgYPjoiIz5vzf7d5wu8GF # K1JWN5hciN9rdqOhbdxLcSVwnOTJmUGfAMQm4eXOls3iQwfapEFWuOsYmBKXPNSp # wZAFoLGl5y1EaGGc5LByM8wjcbSF52/Z42YaJRsPXY545E3QAPN2mxDh0OLozhiG # gYT1xtjXVfEzYBVmfQaI5QL35cTTAjsJAp85R+KAsOfuL9Z7LFnjdcuPkZWjssME # TFIueH69rxbFOUD64G+rUo7xFIdRAuDNvWBsv0iGDPGaR2nZlY24tz5fISYk1sPY # 4gir99aXAGnoo0vX3Okew4MsiyBn5ZnUDMKzUcQrpVavGacrIkmDYu/bcOUR1mVB # IZ0X7P4bKf38JF7Mp7tY3LFF/h7hvBS2tgTYXlD7TnIMPrxyXCfB5yQq3FFoXRXM # 3/DvqQ4shoVWF/mwwz9xoRku05iphp22fTfjKRIVpm4gFT24JKspEpM8mFa9eTgK # WWCvAgMBAAGjggFcMIIBWDAfBgNVHSMEGDAWgBT2d2rdP/0BE/8WoWyCAi/QCj0U # JTAdBgNVHQ4EFgQUX1jtTDF6omFCjVKAurNhlxmiMpswDgYDVR0PAQH/BAQDAgGG # MBIGA1UdEwEB/wQIMAYBAf8CAQAwEwYDVR0lBAwwCgYIKwYBBQUHAwgwEQYDVR0g # BAowCDAGBgRVHSAAMEwGA1UdHwRFMEMwQaA/oD2GO2h0dHA6Ly9jcmwuc2VjdGln # by5jb20vU2VjdGlnb1B1YmxpY1RpbWVTdGFtcGluZ1Jvb3RSNDYuY3JsMHwGCCsG # AQUFBwEBBHAwbjBHBggrBgEFBQcwAoY7aHR0cDovL2NydC5zZWN0aWdvLmNvbS9T # ZWN0aWdvUHVibGljVGltZVN0YW1waW5nUm9vdFI0Ni5wN2MwIwYIKwYBBQUHMAGG # F2h0dHA6Ly9vY3NwLnNlY3RpZ28uY29tMA0GCSqGSIb3DQEBDAUAA4ICAQAS13sg # rQ41WAyegR0lWP1MLWd0r8diJiH2VVRpxqFGhnZbaF+IQ7JATGceTWOS+kgnMAzG # YRzpm8jIcjlSQ8JtcqymKhgx1s6cFZBSfvfeoyigF8iCGlH+SVSo3HHr98NepjSF # JTU5KSRKK+3nVSWYkSVQgJlgGh3MPcz9IWN4I/n1qfDGzqHCPWZ+/Mb5vVyhgaeq # xLPbBIqv6cM74Nvyo1xNsllECJJrOvsrJQkajVz4xJwZ8blAdX5umzwFfk7K/0K3 # fpjgiXpqNOpXaJ+KSRW0HdE0FSDC7+ZKJJSJx78mn+rwEyT+A3z7Ss0gT5CpTrcm # hUwIw9jbvnYuYRKxFVWjKklW3z83epDVzoWJttxFpujdrNmRwh1YZVIB2guAAjEQ # oF42H0BA7WBCueHVMDyV1e4nM9K4As7PVSNvQ8LI1WRaTuGSFUd9y8F8jw22BZC6 # mJoB40d7SlZIYfaildlgpgbgtu6SDsek2L8qomG57Yp5qTqof0DwJ4Q4HsShvRl/ # 59T4IJBovRwmqWafH0cIPEX7cEttS5+tXrgRtMjjTOp6A9l0D6xcKZtxnLqiTH9K # PCy6xZEi0UDcMTww5Fl4VvoGbMG2oonuX3f1tsoHLaO/Fwkj3xVr3lDkmeUqiveb # QTvGkx5hGuJaSVQ+x60xJ/Y29RBr8Tm9XJ59AjCCBhowggQCoAMCAQICEGIdbQxS # AZ47kHkVIIkhHAowDQYJKoZIhvcNAQEMBQAwVjELMAkGA1UEBhMCR0IxGDAWBgNV # BAoTD1NlY3RpZ28gTGltaXRlZDEtMCsGA1UEAxMkU2VjdGlnbyBQdWJsaWMgQ29k # ZSBTaWduaW5nIFJvb3QgUjQ2MB4XDTIxMDMyMjAwMDAwMFoXDTM2MDMyMTIzNTk1 # OVowVDELMAkGA1UEBhMCR0IxGDAWBgNVBAoTD1NlY3RpZ28gTGltaXRlZDErMCkG # A1UEAxMiU2VjdGlnbyBQdWJsaWMgQ29kZSBTaWduaW5nIENBIFIzNjCCAaIwDQYJ # KoZIhvcNAQEBBQADggGPADCCAYoCggGBAJsrnVP6NT+OYAZDasDP9X/2yFNTGMjO # 02x+/FgHlRd5ZTMLER4ARkZsQ3hAyAKwktlQqFZOGP/I+rLSJJmFeRno+DYDY1UO # AWKA4xjMHY4qF2p9YZWhhbeFpPb09JNqFiTCYy/Rv/zedt4QJuIxeFI61tqb7/fo # XT1/LW2wHyN79FXSYiTxcv+18Irpw+5gcTbXnDOsrSHVJYdPE9s+5iRF2Q/TlnCZ # GZOcA7n9qudjzeN43OE/TpKF2dGq1mVXn37zK/4oiETkgsyqA5lgAQ0c1f1IkOb6 # rGnhWqkHcxX+HnfKXjVodTmmV52L2UIFsf0l4iQ0UgKJUc2RGarhOnG3B++OxR53 # LPys3J9AnL9o6zlviz5pzsgfrQH4lrtNUz4Qq/Va5MbBwuahTcWk4UxuY+PynPjg # w9nV/35gRAhC3L81B3/bIaBb659+Vxn9kT2jUztrkmep/aLb+4xJbKZHyvahAEx2 # XKHafkeKtjiMqcUf/2BG935A591GsllvWwIDAQABo4IBZDCCAWAwHwYDVR0jBBgw # FoAUMuuSmv81lkgvKEBCcCA2kVwXheYwHQYDVR0OBBYEFA8qyyCHKLjsb0iuK1Sm # KaoXpM0MMA4GA1UdDwEB/wQEAwIBhjASBgNVHRMBAf8ECDAGAQH/AgEAMBMGA1Ud # JQQMMAoGCCsGAQUFBwMDMBsGA1UdIAQUMBIwBgYEVR0gADAIBgZngQwBBAEwSwYD # VR0fBEQwQjBAoD6gPIY6aHR0cDovL2NybC5zZWN0aWdvLmNvbS9TZWN0aWdvUHVi # bGljQ29kZVNpZ25pbmdSb290UjQ2LmNybDB7BggrBgEFBQcBAQRvMG0wRgYIKwYB # BQUHMAKGOmh0dHA6Ly9jcnQuc2VjdGlnby5jb20vU2VjdGlnb1B1YmxpY0NvZGVT # aWduaW5nUm9vdFI0Ni5wN2MwIwYIKwYBBQUHMAGGF2h0dHA6Ly9vY3NwLnNlY3Rp # Z28uY29tMA0GCSqGSIb3DQEBDAUAA4ICAQAG/4Lhd2M2bnuhFSCbE/8E/ph1RGHD # VpVx0ZE/haHrQECxyNbgcv2FymQ5PPmNS6Dah66dtgCjBsULYAor5wxxcgEPRl05 # pZOzI3IEGwwsepp+8iGsLKaVpL3z5CmgELIqmk/Q5zFgR1TSGmxqoEEhk60FqONz # Dn7D8p4W89h8sX+V1imaUb693TGqWp3T32IKGfIgy9jkd7GM7YCa2xulWfQ6E1xZ # tYNEX/ewGnp9ZeHPsNwwviJMBZL4xVd40uPWUnOJUoSiugaz0yWLODRtQxs5qU6E # 58KKmfHwJotl5WZ7nIQuDT0mWjwEx7zSM7fs9Tx6N+Q/3+49qTtUvAQsrEAxwmzO # TJ6Jp6uWmHCgrHW4dHM3ITpvG5Ipy62KyqYovk5O6cC+040Si15KJpuQ9VJnbPvq # YqfMB9nEKX/d2rd1Q3DiuDexMKCCQdJGpOqUsxLuCOuFOoGbO7Uv3RjUpY39jkkp # 0a+yls6tN85fJe+Y8voTnbPU1knpy24wUFBkfenBa+pRFHwCBB1QtS+vGNRhsceP # 3kSPNrrfN2sRzFYsNfrFaWz8YOdU254qNZQfd9O/VjxZ2Gjr3xgANHtM3HxfzPYF # 6/pKK8EE4dj66qKKtm2DTL1KFCg/OYJyfrdLJq1q2/HXntgr2GVw+ZWhrWgMTn8v # 1SjZsLlrgIfZHDCCBl0wggTFoAMCAQICEDpSaiyEzlXmHWX8zBLY6YkwDQYJKoZI # hvcNAQEMBQAwVTELMAkGA1UEBhMCR0IxGDAWBgNVBAoTD1NlY3RpZ28gTGltaXRl # ZDEsMCoGA1UEAxMjU2VjdGlnbyBQdWJsaWMgVGltZSBTdGFtcGluZyBDQSBSMzYw # HhcNMjQwMTE1MDAwMDAwWhcNMzUwNDE0MjM1OTU5WjBuMQswCQYDVQQGEwJHQjET # MBEGA1UECBMKTWFuY2hlc3RlcjEYMBYGA1UEChMPU2VjdGlnbyBMaW1pdGVkMTAw # LgYDVQQDEydTZWN0aWdvIFB1YmxpYyBUaW1lIFN0YW1waW5nIFNpZ25lciBSMzUw # ggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCN0Wf0wUibvf04STpNYYGb # w9jcRaVhBDaNBp7jmJaA9dQZW5ighrXGNMYjK7Dey5RIHMqLIbT9z9if753mYboj # JrKWO4ZP0N5dBT2TwZZaPb8E+hqaDZ8Vy2c+x1NiEwbEzTrPX4W3QFq/zJvDDbWK # L99qLL42GJQzX3n5wWo60KklfFn+Wb22mOZWYSqkCVGl8aYuE12SqIS4MVO4PUax # XeO+4+48YpQlNqbc/ndTgszRQLF4MjxDPjRDD1M9qvpLTZcTGVzxfViyIToRNxPP # 6DUiZDU6oXARrGwyP9aglPXwYbkqI2dLuf9fiIzBugCDciOly8TPDgBkJmjAfILN # iGcVEzg+40xUdhxNcaC+6r0juPiR7bzXHh7v/3RnlZuT3ZGstxLfmE7fRMAFwbHd # Dz5gtHLqjSTXDiNF58IxPtvmZPG2rlc+Yq+2B8+5pY+QZn+1vEifI0MDtiA6BxxQ # uOnj4PnqDaK7NEKwtD1pzoA3jJFuoJiwbatwhDkg1PIjYnMDbDW+wAc9FtRN6pUs # O405jaBgigoFZCw9hWjLNqgFVTo7lMb5rVjJ9aSBVVL2dcqzyFW2LdWk5Xdp65oe # eOALod7YIIMv1pbqC15R7QCYLxcK1bCl4/HpBbdE5mjy9JR70BHuYx27n4XNOZbw # rXcG3wZf9gEUk7stbPAoBQIDAQABo4IBjjCCAYowHwYDVR0jBBgwFoAUX1jtTDF6 # omFCjVKAurNhlxmiMpswHQYDVR0OBBYEFGjvpDJJabZSOB3qQzks9BRqngyFMA4G # A1UdDwEB/wQEAwIGwDAMBgNVHRMBAf8EAjAAMBYGA1UdJQEB/wQMMAoGCCsGAQUF # BwMIMEoGA1UdIARDMEEwNQYMKwYBBAGyMQECAQMIMCUwIwYIKwYBBQUHAgEWF2h0 # dHBzOi8vc2VjdGlnby5jb20vQ1BTMAgGBmeBDAEEAjBKBgNVHR8EQzBBMD+gPaA7 # hjlodHRwOi8vY3JsLnNlY3RpZ28uY29tL1NlY3RpZ29QdWJsaWNUaW1lU3RhbXBp # bmdDQVIzNi5jcmwwegYIKwYBBQUHAQEEbjBsMEUGCCsGAQUFBzAChjlodHRwOi8v # Y3J0LnNlY3RpZ28uY29tL1NlY3RpZ29QdWJsaWNUaW1lU3RhbXBpbmdDQVIzNi5j # cnQwIwYIKwYBBQUHMAGGF2h0dHA6Ly9vY3NwLnNlY3RpZ28uY29tMA0GCSqGSIb3 # DQEBDAUAA4IBgQCw3C7J+k82TIov9slP1e8YTx+fDsa//hJ62Y6SMr2E89rv82y/ # n8we5W6z5pfBEWozlW7nWp+sdPCdUTFw/YQcqvshH6b9Rvs9qZp5Z+V7nHwPTH8y # zKwgKzTTG1I1XEXLAK9fHnmXpaDeVeI8K6Lw3iznWZdLQe3zl+Rejdq5l2jU7iUf # MkthfhFmi+VVYPkR/BXpV7Ub1QyyWebqkjSHJHRmv3lBYbQyk08/S7TlIeOr9iQ+ # UN57fJg4QI0yqdn6PyiehS1nSgLwKRs46T8A6hXiSn/pCXaASnds0LsM5OVoKYfb # gOOlWCvKfwUySWoSgrhncihSBXxH2pAuDV2vr8GOCEaePZc0Dy6O1rYnKjGmqm/I # RNkJghSMizr1iIOPN+23futBXAhmx8Ji/4NTmyH9K0UvXHiuA2Pa3wZxxR9r9XeI # UVb2V8glZay+2ULlc445CzCvVSZV01ZB6bgvCuUuBx079gCcepjnZDCcEuIC5Se4 # F6yFaZ8RvmiJ4hgwggaCMIIEaqADAgECAhA2wrC9fBs656Oz3TbLyXVoMA0GCSqG # SIb3DQEBDAUAMIGIMQswCQYDVQQGEwJVUzETMBEGA1UECBMKTmV3IEplcnNleTEU # MBIGA1UEBxMLSmVyc2V5IENpdHkxHjAcBgNVBAoTFVRoZSBVU0VSVFJVU1QgTmV0 # d29yazEuMCwGA1UEAxMlVVNFUlRydXN0IFJTQSBDZXJ0aWZpY2F0aW9uIEF1dGhv # cml0eTAeFw0yMTAzMjIwMDAwMDBaFw0zODAxMTgyMzU5NTlaMFcxCzAJBgNVBAYT # AkdCMRgwFgYDVQQKEw9TZWN0aWdvIExpbWl0ZWQxLjAsBgNVBAMTJVNlY3RpZ28g # UHVibGljIFRpbWUgU3RhbXBpbmcgUm9vdCBSNDYwggIiMA0GCSqGSIb3DQEBAQUA # A4ICDwAwggIKAoICAQCIndi5RWedHd3ouSaBmlRUwHxJBZvMWhUP2ZQQRLRBQIF3 # FJmp1OR2LMgIU14g0JIlL6VXWKmdbmKGRDILRxEtZdQnOh2qmcxGzjqemIk8et8s # E6J+N+Gl1cnZocew8eCAawKLu4TRrCoqCAT8uRjDeypoGJrruH/drCio28aqIVEn # 45NZiZQI7YYBex48eL78lQ0BrHeSmqy1uXe9xN04aG0pKG9ki+PC6VEfzutu6Q3I # cZZfm00r9YAEp/4aeiLhyaKxLuhKKaAdQjRaf/h6U13jQEV1JnUTCm511n5avv4N # +jSVwd+Wb8UMOs4netapq5Q/yGyiQOgjsP/JRUj0MAT9YrcmXcLgsrAimfWY3MzK # m1HCxcquinTqbs1Q0d2VMMQyi9cAgMYC9jKc+3mW62/yVl4jnDcw6ULJsBkOkrcP # LUwqj7poS0T2+2JMzPP+jZ1h90/QpZnBkhdtixMiWDVgh60KmLmzXiqJc6lGwqoU # qpq/1HVHm+Pc2B6+wCy/GwCcjw5rmzajLbmqGygEgaj/OLoanEWP6Y52Hflef3XL # vYnhEY4kSirMQhtberRvaI+5YsD3XVxHGBjlIli5u+NrLedIxsE88WzKXqZjj9Zi # 5ybJL2WjeXuOTbswB7XjkZbErg7ebeAQUQiS/uRGZ58NHs57ZPUfECcgJC+v2wID # AQABo4IBFjCCARIwHwYDVR0jBBgwFoAUU3m/WqorSs9UgOHYm8Cd8rIDZsswHQYD # VR0OBBYEFPZ3at0//QET/xahbIICL9AKPRQlMA4GA1UdDwEB/wQEAwIBhjAPBgNV # HRMBAf8EBTADAQH/MBMGA1UdJQQMMAoGCCsGAQUFBwMIMBEGA1UdIAQKMAgwBgYE # VR0gADBQBgNVHR8ESTBHMEWgQ6BBhj9odHRwOi8vY3JsLnVzZXJ0cnVzdC5jb20v # VVNFUlRydXN0UlNBQ2VydGlmaWNhdGlvbkF1dGhvcml0eS5jcmwwNQYIKwYBBQUH # AQEEKTAnMCUGCCsGAQUFBzABhhlodHRwOi8vb2NzcC51c2VydHJ1c3QuY29tMA0G # CSqGSIb3DQEBDAUAA4ICAQAOvmVB7WhEuOWhxdQRh+S3OyWM637ayBeR7djxQ8Si # hTnLf2sABFoB0DFR6JfWS0snf6WDG2gtCGflwVvcYXZJJlFfym1Doi+4PfDP8s0c # qlDmdfyGOwMtGGzJ4iImyaz3IBae91g50QyrVbrUoT0mUGQHbRcF57olpfHhQESt # z5i6hJvVLFV/ueQ21SM99zG4W2tB1ExGL98idX8ChsTwbD/zIExAopoe3l6JrzJt # Pxj8V9rocAnLP2C8Q5wXVVZcbw4x4ztXLsGzqZIiRh5i111TW7HV1AtsQa6vXy63 # 3vCAbAOIaKcLAo/IU7sClyZUk62XD0VUnHD+YvVNvIGezjM6CRpcWed/ODiptK+e # vDKPU2K6synimYBaNH49v9Ih24+eYXNtI38byt5kIvh+8aW88WThRpv8lUJKaPn3 # 7+YHYafob9Rg7LyTrSYpyZoBmwRWSE4W6iPjB7wJjJpH29308ZkpKKdpkiS9WNsf # /eeUtvRrtIEiSJHN899L1P4l6zKVsdrUu1FX1T/ubSrsxrYJD+3f3aKg6yxdbugo # t06YwGXXiy5UUGZvOu3lXlxA+fC13dQ5OlL2gIb5lmF6Ii8+CQOYDwXM+yd9dbmo # cQsHjcRPsccUd5E9FiswEqORvz8g3s+jR3SFCgXhN4wz7NgAnOgpCdUo4uDyllU9 # PzGCBcEwggW9AgEBMGkwVDELMAkGA1UEBhMCR0IxGDAWBgNVBAoTD1NlY3RpZ28g # TGltaXRlZDErMCkGA1UEAxMiU2VjdGlnbyBQdWJsaWMgQ29kZSBTaWduaW5nIENB # IFIzNgIRAKTyyhFVIszKg3iR7KhQQaIwDQYJYIZIAWUDBAIBBQCggYQwGAYKKwYB # BAGCNwIBDDEKMAigAoAAoQKAADAZBgkqhkiG9w0BCQMxDAYKKwYBBAGCNwIBBDAc # BgorBgEEAYI3AgELMQ4wDAYKKwYBBAGCNwIBFTAvBgkqhkiG9w0BCQQxIgQgZIZT # UJEmd4BPafewUkWq3pSv0lV4INfU3LKro5X1sq0wDQYJKoZIhvcNAQEBBQAEggGA # FE3AtfxmB7ODZKglFG4VjmXzb5oywAf75WRPYMZR7hkeE6Spi+b4l1AkFT0T1OoE # /VCL65Yp+P9WqtI65w+GgDe/R7u6b9zh+TepHKRF+TjdZKFNQcPj53nOXduQCABu # cKMoIX1SkrPLDRPGfU/d/PLbiJ4GMmoc3xJpSaWS8XAaUiIF3LhQWctB7Q8iIqbQ # blPYtOBwbuPgCNJDL7aZWbo7BfbJWtdE6rehT0BjCMrUA3CCsSYEd6iydnL5nYiS # aS5jw1bCCOnFZTDo/9BrFOC85syjM8xOJg5xFpf+sbH+UBCHJBaaMkwADxbenf8L # 6yQ9tYRlfL6JxPvgBFbolaB1xe4hUkE9BQNSrJlrWGI2VJhq59gLJikGIpToQPdj # XK2ZfDjRRh24SwhoL5AfsnsZzcYkNJKLbOXRYgKcy4xjsJ8wtzMaH8toTaHQJLK2 # Ins5jHJM3b/jXyeE/nsJBXD2qJy/N3ZKSMtu3XCokhFqyC2I/DUmKAasBs5hOJhx # oYIDIjCCAx4GCSqGSIb3DQEJBjGCAw8wggMLAgEBMGkwVTELMAkGA1UEBhMCR0Ix # GDAWBgNVBAoTD1NlY3RpZ28gTGltaXRlZDEsMCoGA1UEAxMjU2VjdGlnbyBQdWJs # aWMgVGltZSBTdGFtcGluZyBDQSBSMzYCEDpSaiyEzlXmHWX8zBLY6YkwDQYJYIZI # AWUDBAICBQCgeTAYBgkqhkiG9w0BCQMxCwYJKoZIhvcNAQcBMBwGCSqGSIb3DQEJ # BTEPFw0yNDA4MTUxNjM1MjRaMD8GCSqGSIb3DQEJBDEyBDBDmK21Bx8fQNXnkFTl # WJL3lERbHnLTM1yYUUs/bl6MzbA5DdlGhTX2UDtfo+Azo1cwDQYJKoZIhvcNAQEB # BQAEggIAFoww1H8wEf+0+R+wJeWzCEgNaabM60NGXGhKCGrkUSjHDPwDSHd3hRnF # 5cSOLYl9oLuQGsLBzG0753iN3qJVDo5FK9NNs2GJX85Y3M+UjYO/C2K6I3ybgGon # Ue3YCSD6bsnUaO5HDPKUDyIPpqeMintk3u7hzLBRqR12lWa28GqkNDLcXmKl/Jl/ # opKmIxkFhn2FMK3fcgx6LBBggcewTYf48UFfyR3ONSCsQh3CzxKvuRI5/xu1pzSq # CvHtbO43fduGry4XSjOTSXoIochzKpQCUmKj+Db3HW07Y7RbxLj7RnCkeMTI4DHN # dGUOy681Ya+I+EI6RIld1c1RicTmJ/r3+pKpaiBmGOtDQk6vyrE077Kr0knEg3de # 7Gr6HUErDfCiztnI8NdmOOat442pP1sFA1wTq07CNM9uNowawdVjZIOBke99E5me # h9Cv7YpSUbXJ3QPZbvgxbHYdC7ZekWTPc8W/dOrO7AtuxMHSdWynmp1s3hoelsBZ # DzbYvKkb0wmK+LRXwffN3JFZF6WE4B4QxWgD3/sfV7DAjKWn4wAVq90WIepTaIPv # tsZxRdpe1Z8n/sno3+wqWPv8KQXFTaTTB4m3FjMfaTxuZjusXrCpmbZTRISbf/D0 # PSew7CwnztVfqiLhEWY6i+t+zsZgLPCCPfJdRDi+PmSyOumwX0M= # SIG # End signature block |