AnyPackage.Scoop.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 Scoop using namespace AnyPackage.Provider using namespace AnyPackage.Feedback using namespace System.Collections.Generic using namespace System.IO using namespace System.Management.Automation using namespace System.Threading [PackageProvider('Scoop')] class ScoopProvider : PackageProvider, IFindPackage, IGetPackage, IInstallPackage, IUpdatePackage, IUninstallPackage, IGetSource, ISetSource, ICommandNotFound { [PackageProviderInfo] Initialize([PackageProviderInfo] $providerInfo) { return [ScoopProviderInfo]::new($providerInfo) } [IEnumerable[CommandNotFoundFeedback]] FindPackage([CommandNotFoundContext] $context, [CancellationToken] $token) { $dict = [Dictionary[string, CommandNotFoundFeedback]]::new([StringComparer]::OrdinalIgnoreCase) $key = [Path]::GetFileNameWithoutExtension($context.Command) $packages = $this.ProviderInfo.CommandCache[$key] foreach ($package in $packages) { if (!$dict.ContainsKey($package.Name) -and $package.Binaries -match $context.Command) { $feedback = [CommandNotFoundFeedback]::new($package.Name, $this.ProviderInfo) $dict.Add($package.Name, $feedback) } } return $dict.Values } [void] FindPackage([PackageRequest] $request) { Find-ScoopApp -Name $request.Name | Write-Package -Request $request -OfficialSources $this.ProviderInfo.OfficialSources } [void] GetPackage([PackageRequest] $request) { Get-ScoopApp -Name $request.Name | Write-Package -Request $request -OfficialSources $this.ProviderInfo.OfficialSources } [void] InstallPackage([PackageRequest] $request) { $installScoopAppParams = @{ } if ($request.DynamicParameters.Architecture) { $installScoopAppParams['Architecture'] = $request.DynamicParameters.Architecture } if ($request.DynamicParameters.SkipDependencies) { $installScoopAppParams['SkipDependencies'] = $request.DynamicParameters.SkipDependencies } if ($request.DynamicParameters.NoCache) { $installScoopAppParams['NoCache'] = $request.DynamicParameters.NoCache } if ($request.DynamicParameters.SkipHashCheck) { $installScoopAppParams['SkipHashCheck'] = $request.DynamicParameters.SkipHashCheck } if ($request.DynamicParameters.Scope -eq 'AllUsers') { $installScoopAppParams['Global'] = $true } $findPackageParameters = @{ Name = $request.Name } if ($request.Source) { $findPackageParameters['Bucket'] = $request.Source } Find-ScoopApp @findPackageParameters | Where-Object { $request.IsMatch([PackageVersion]$_.Version) } | Select-Object -Property Name | Install-ScoopApp @installScoopAppParams Get-ScoopApp -Name $request.Name | Write-Package -Request $request -OfficialSources $this.ProviderInfo.OfficialSources } [void] UpdatePackage([PackageRequest] $request) { $installScoopAppParams = @{ } if ($request.DynamicParameters.SkipDependencies) { $installScoopAppParams['SkipDependencies'] = $request.DynamicParameters.SkipDependencies } if ($request.DynamicParameters.NoCache) { $installScoopAppParams['NoCache'] = $request.DynamicParameters.NoCache } if ($request.DynamicParameters.SkipHashCheck) { $installScoopAppParams['SkipHashCheck'] = $request.DynamicParameters.SkipHashCheck } if ($request.DynamicParameters.Scope -eq 'AllUsers') { $installScoopAppParams['Global'] = $true } if ($request.DynamicParameters.Reinstall) { $installScoopAppParams['Force'] = $true } $getPackageParameters = @{ Name = $request.Name } $findPackageParameters = @{ } if ($request.Source) { $findPackageParameters['Bucket'] = $request.Source } Get-ScoopApp @getPackageParameters | Find-ScoopApp @findPackageParameters | Where-Object { $request.IsMatch([PackageVersion]$_.Version) } | Select-Object -Property Name | Update-ScoopApp @installScoopAppParams Get-ScoopApp -Name $request.Name | Write-Package -Request $request -OfficialSources $this.ProviderInfo.OfficialSources } [void] UninstallPackage([PackageRequest] $request) { $uninstallScoopAppParams = @{ } if ($request.DynamicParameters.RemoveData) { $uninstallScoopAppParams['Purge'] = $request.DynamicParameters.RemoveData } if ($request.DynamicParameters.Scope -eq 'AllUsers') { $uninstallScoopAppParams['Global'] = $true } $package = Get-ScoopApp -Name $request.Name | Where-Object { $request.IsMatch([PackageVersion]$_.Version) } $package | Uninstall-ScoopApp @uninstallScoopAppParams if (-not ($package | Get-ScoopApp)) { $package | Write-Package -Request $request -OfficialSources $this.PackageInfo.OfficialSources } } [void] GetSource([SourceRequest] $sourceRequest) { Get-ScoopBucket | Write-Source -Request $sourceRequest -OfficialSources $this.ProviderInfo.OfficialSources } [void] RegisterSource([SourceRequest] $sourceRequest) { if ($sourceRequest.Trusted) { throw 'Scoop provider does not support Trusted parameter.' } $registerBucketParams = @{ Force = $sourceRequest.Force } if ($sourceRequest.Name) { $name = $sourceRequest.Name $registerBucketParams['Name'] = $sourceRequest.Name $registerBucketParams['Uri'] = $sourceRequest.Location } else { if (-not ($sourceRequest.DynamicParameters.Official -in $this.ProviderInfo.OfficialSources.Keys)) { throw "'$($sourceRequest.DynamicParameters.Official)' is not an official source." } $name = $sourceRequest.DynamicParameters.Official $registerBucketParams['Official'] = $sourceRequest.DynamicParameters.Official } if ((Get-ScoopBucket -Name $name) -and -not $sourceRequest.Force) { throw "Source '$name' already exists. Use -Force to recreate the source." } Register-ScoopBucket @registerBucketParams Get-ScoopBucket -Name $name | Write-Source -Request $sourceRequest -OfficialSources $this.ProviderInfo.OfficialSources } [void] SetSource([SourceRequest] $sourceRequest) { if ($sourceRequest.Trusted) { throw 'Scoop provider does not support Trusted parameter.' } Register-ScoopBucket -Name $sourceRequest.Name -Uri $sourceRequest.Location -Force $this.GetSource($sourceRequest) } [void] UnregisterSource([SourceRequest] $sourceRequest) { $source = Get-ScoopBucket -Name $sourceRequest.Name if (-not $source) { return } Unregister-ScoopBucket -Name $sourceRequest.Name $source | Write-Source -Request $sourceRequest -OfficialSources $this.ProviderInfo.OfficialSources } [object] GetDynamicParameters([string] $commandName) { return $(switch ($commandName) { 'Register-PackageSource' { [RegisterPackageSourceDynamicParameters]::new() } 'Install-Package' { [InstallPackageDynamicParameters]::new() } 'Uninstall-Package' { [UninstallPackageDynamicParameters]::new() } 'Update-Package' { [UpdatePackageDynamicParameters]::new() } default { $null } }) } } class RegisterPackageSourceDynamicParameters { [Parameter(Mandatory, ParameterSetName = 'Official')] [ValidateNotNullOrEmpty()] [string] $Official } class ScopeDynamicParameters { [Parameter()] [ValidateSet('CurrentUser', 'AllUsers')] [string] $Scope = 'CurrentUser' } class OptionalDynamicParameters : ScopeDynamicParameters { [Parameter()] [switch] $SkipDependencies [Parameter()] [switch] $NoCache [Parameter()] [switch] $SkipHashCheck } class InstallPackageDynamicParameters : OptionalDynamicParameters { [Parameter()] [ValidateSet('32bit', '64bit', 'arm64')] [string] $Architecture } class UpdatePackageDynamicParameters : OptionalDynamicParameters { [Parameter()] [switch] $Reinstall } class UninstallPackageDynamicParameters : ScopeDynamicParameters { [Parameter()] [switch] $RemoveData } class ScoopProviderInfo : PackageProviderInfo { [hashtable] $OfficialSources [Dictionary[string, List[ScoopAppDetailed]]] $CommandCache = [Dictionary[string, List[ScoopAppDetailed]]]::new([StringComparer]::OrdinalIgnoreCase) ScoopProviderInfo([PackageProviderInfo] $providerInfo) : base($providerInfo) { $this.SetOfficialSources() if ([Runspace]::DefaultRunspace.Name -eq $this.FullName) { $this.SetCommandCache() } } [void] SetOfficialSources() { $path = Get-Command -Name Scoop | Select-Object -ExpandProperty Path | Split-Path | Split-Path | Join-Path -ChildPath apps\scoop\current\buckets.json $sources = Get-Content -Path $path | ConvertFrom-Json $keys = $sources | Get-Member -MemberType NoteProperty | Select-Object -ExpandProperty Name $ht = @{ } foreach ($key in $keys) { $ht[$key] = $sources.$key } $this.OfficialSources = $ht } [void] SetCommandCache() { $packages = Find-ScoopApp foreach ($package in $packages) { foreach ($command in $package.Binaries) { $key = [Path]::GetFileNameWithoutExtension($command) if ($this.CommandCache.ContainsKey($key)) { $this.CommandCache[$key] += $package } else { $list = [List[ScoopAppDetailed]]::new() $list += $package $this.CommandCache.Add($key, $package) } } } } } $ScriptBlock = { param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameters) # Suppress PSReviewUnusedParameter warning since suppressing it does not work. $null = $commandName, $parameterName, $commandAst, $fakeBoundParameters Get-PackageProvider -Name Scoop | Select-Object -ExpandProperty OfficialSources | Select-Object -ExpandProperty Keys | Where-Object Name -Like "$wordToComplete*" | ForEach-Object { [CompletionResult]::new($_) } } Register-ArgumentCompleter -CommandName Register-PackageSource -ParameterName Official -ScriptBlock $ScriptBlock [guid] $id = '28111522-ea7a-4e8a-b598-85389c17f8be' [PackageProviderManager]::RegisterProvider($id, [ScoopProvider], $MyInvocation.MyCommand.ScriptBlock.Module) $MyInvocation.MyCommand.ScriptBlock.Module.OnRemove = { [PackageProviderManager]::UnregisterProvider($id) } function Write-Source { [CmdletBinding()] param ( [Parameter(ValueFromPipelineByPropertyName)] [string] $Name, [Parameter(ValueFromPipelineByPropertyName)] [Alias('Source')] [string] $Location, [Parameter(ValueFromPipelineByPropertyName)] [datetime] $Updated, [Parameter(ValueFromPipelineByPropertyName)] [int] $Manifests, [Parameter(Mandatory)] [SourceRequest] $Request, [Parameter()] [hashtable] $OfficialSources ) process { if ($Name -like $Request.Name) { $trusted = if ($Location -in $OfficialSources.Values) { $true } else { $false } $source = [PackageSourceInfo]::new($Name, $Location, $trusted, @{ Updated = $Updated; Manifests = $Manifests }, $Request.ProviderInfo) $Request.WriteSource($source) } } } function Write-Package { [CmdletBinding()] param ( [Parameter(ValueFromPipelineByPropertyName)] [string] $Name, [Parameter(ValueFromPipelineByPropertyName)] [string] $Version, [Parameter(ValueFromPipelineByPropertyName)] [string] $Description, [Parameter(ValueFromPipelineByPropertyName)] [string] $Source, [Parameter(ValueFromPipelineByPropertyName)] [datetime] $Updated, [Parameter(ValueFromPipelineByPropertyName)] [string] $Info, [Parameter(Mandatory)] [PackageRequest] $Request, [Parameter()] [hashtable] $OfficialSources ) begin { $buckets = Get-ScoopBucket } process { $metadata = @{ } if ($Updated) { $metadata['Updated'] = $Updated } if ($Info) { $metadata['Info'] = $Info } if ($Request.Source -and $Source -ne $Request.Source) { return } if ($Source -eq '<auto-generated>' -or (Test-Path -Path $Source)) { $sourceInfo = $null } else { $bucket = $buckets | Where-Object Name -EQ $Source $trusted = if ($bucket.Source -in $OfficialSources.Values) { $true } else { $false } $sourceInfo = [PackageSourceInfo]::new($bucket.Name, $bucket.Source, $trusted, @{ Updated = $bucket.Updated; Manifests = $bucket.Manifests }, $Request.ProviderInfo) } if ($Request.IsMatch($Name, $Version)) { $package = [PackageInfo]::new($Name, $Version, $sourceInfo, $Description, $null, $metadata, $Request.ProviderInfo) $Request.WritePackage($package) } } } # SIG # Begin signature block # MIIlQgYJKoZIhvcNAQcCoIIlMzCCJS8CAQExDzANBglghkgBZQMEAgEFADB5Bgor # BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG # KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCBO+o7pXrOLou86 # fWubAwS+Qv87xvXA9PjsHVUYOBLPvKCCHtcwggW2MIIEHqADAgECAhEApPLKEVUi # 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 # BgorBgEEAYI3AgELMQ4wDAYKKwYBBAGCNwIBFTAvBgkqhkiG9w0BCQQxIgQgYYbl # hwBnc+MEdSQijnn4yPWClnmWCbnoI8jb3jUDmOowDQYJKoZIhvcNAQEBBQAEggGA # gDjvCTC0cEavDEDMYZ5O2w23KXLj3r32hPsxpZ5BcLXYbA8/ayOqH+utFH2q7Wql # LJCAjjP/8pK9DOeIav+l0mPyCpjIMa7Lftl3f2TzrtgatyY8ZPzEsQHoNxzcZs7B # SMWFnU5wIrV7/Ip7JVfgp+EcEt8G9WzU0w6kuphKM3rYgmj9SVSkhnZDgs7M8C6T # SdQ8Qu/J1QiHhimpwJHGFbHGS66YyuTQxRkx7G8SRjaOojvNq6xpiGdrl2YPMghX # e06BR3zhAyELGbvey8iXLFWbH24UCJ/x3+r2zh36XtfsY3+MfbpzeqFNnaD+p6PT # XUEkmdSN01dh6Uqc48Rx64H2dhNJyKD5H0T8fe3BXzL7ehM3yBvGIqbRDxv3/bOn # /tXSd6gfgd+/OyRHc1MbQhJuiv2DBULFjeJontuBqzn+PAVvvfmQjegAofklBOf+ # 6P9f0CLu30WhUTBTvzIrirfE/g+xU3oUqPqT8kcnKBw4ztNSfF4LYh9EOCA7nLU9 # oYIDIjCCAx4GCSqGSIb3DQEJBjGCAw8wggMLAgEBMGkwVTELMAkGA1UEBhMCR0Ix # GDAWBgNVBAoTD1NlY3RpZ28gTGltaXRlZDEsMCoGA1UEAxMjU2VjdGlnbyBQdWJs # aWMgVGltZSBTdGFtcGluZyBDQSBSMzYCEDpSaiyEzlXmHWX8zBLY6YkwDQYJYIZI # AWUDBAICBQCgeTAYBgkqhkiG9w0BCQMxCwYJKoZIhvcNAQcBMBwGCSqGSIb3DQEJ # BTEPFw0yNDA4MTUxNjE0NTlaMD8GCSqGSIb3DQEJBDEyBDA75XqkVo61JN/03Px4 # bI1Rfd7EcToQDrEBpPQSMSUOpoUlfzkzxxnDag619z7hQgUwDQYJKoZIhvcNAQEB # BQAEggIAWWgvF4avyfDWqh8oT/MPy/gZy3fDMhVVLR4e/x0SJ00zUyN/yNxNL/EV # 26WuApLgLZVjlAGl/cwQVOV14RuBOMkFXl/JbN7aoCHlfZqtS57MD5aykSaohY2p # yGIgZC9us9JQJv2ydNj1WdDoAAKOSJE0XRyWSGrwXuR8OiSL7zq6cFclQs9t6pe5 # Ieoce1H741XSgHfs7Pnmybo9ZEi1lL4USJadYwWl7GWd1oYeJmkPIiEjApYjrVjB # Sdu82dtaOMn+E7lB6915TuNMmHKNxwVMrrwjwOJ/a6XdjhHlTPxwukFLs9IRhDHz # 7HHrNN+99tRNJkUevvM6g63LARpa3730N+lB4Ma+dFuJLqr1r3xiR3SwSn7diuRv # kSbjvKp/v/l6N1Z5rIRGgUTilHmtjywSYJvYTELqnG9UcL13uzWoH4ShfavKPgwW # Vra03EkNGAYa7dSscaARwDO0IpbVUUCFDe7EQ59u0s7/tUdbvq6eJ2rPtNOhrjmP # l83hlAjlH1uShNjvLjoQ6iAb7060XZU8ZUvxrfQ49kkJeb8sSGZ9zZXk7AMG2Vo3 # kv4yyVW6ASROApRgcce9eojNcxPek3YcEFgqEu/nbYmJM8jmYXpW3b+Qc/7dthoV # toKHiy54a8Kz6li1/PPcoDCYOyrLlaFTs4mGwfuG4Tc7WBzZ1A0= # SIG # End signature block |