Scoop.psm1

# Copyright (c) Thomas Nieto - All Rights Reserved
# You may use, distribute and modify this code under the
# terms of the MIT license.

class ScoopAppDetailed {
    [string] $Name
    [string] $Version
    [string] $Source
    [string] $Description
    [string] $Homepage
    [string[]] $Binaries
}

<#
    .SYNOPSIS
    Finds Scoop apps.
 
    .DESCRIPTION
    Finds Scoop apps in buckets.
 
    .PARAMETER Name
    Specifies the app name to find.
 
    .PARAMETER Bucket
    Specifies the bucket to find apps.
#>

function Find-ScoopApp {
    [CmdletBinding()]
    [OutputType([ScoopAppDetailed])]
    param (
        [Parameter(Position = 0,
            ValueFromPipeline,
            ValueFromPipelineByPropertyName)]
        [SupportsWildcards()]
        [string[]]
        $Name = '*',

        [Parameter(Position = 1,
            ValueFromPipelineByPropertyName)]
        [SupportsWildcards()]
        [string]
        $Bucket = '*'
    )

    begin {
        try {
            $bucketPath = Get-Command -Name Scoop -CommandType ExternalScript -ErrorAction Stop |
            Select-Object -ExpandProperty Path |
            Split-Path |
            Split-Path |
            Join-Path -ChildPath buckets
        }
        catch {
            throw $_
        }
    }

    process {
        foreach ($_name in $Name) {
            Get-ChildItem -Path (Join-Path -Path $bucketPath -ChildPath "$Bucket/bucket/$_name.json") |
            ForEach-Object {
                $value = $_ | Get-Content | ConvertFrom-Json

                $binaries = if ($value.Bin) {
                    $value.Bin |
                    Where-Object { $_ -isnot [object[]] } |
                    Split-Path -Leaf
                } else {
                    $null
                }

                [ScoopAppDetailed]@{
                    Name        = $_.BaseName
                    Version     = $value.Version
                    Source      = ($_.Directory | Split-Path -Parent | Split-Path -Leaf)
                    Description = $value.Description
                    Binaries    = $binaries
                    Homepage    = $value.Homepage
                }
            }
        }
    }
}

<#
    .SYNOPSIS
    Get installed Scoop apps.
 
    .DESCRIPTION
    Get installed Scoop apps.
 
    .PARAMETER Name
    Specifies the app name.
#>

function Get-ScoopApp {
    [CmdletBinding()]
    param (
        [Parameter(Position = 0,
            ValueFromPipeline,
            ValueFromPipelineByPropertyName)]
        [SupportsWildcards()]
        [ValidateNotNullOrEmpty()]
        [string[]]
        $Name = '*'
    )

    begin {
        $apps = & scoop list 6>$null
    }

    process {
        foreach ($_name in $Name) {
            $apps |
            Where-Object Name -like $_name
        }
    }
}

<#
    .SYNOPSIS
    Install Scoop apps.
 
    .DESCRIPTION
    Install Scoop apps.
 
    .PARAMETER Name
    Specifies the app name.
 
    .PARAMETER Version
    Specifies the app version.
 
    .PARAMETER Architecture
    Specifies the app architecture.
 
    .PARAMETER Global
    Installs as a global app for all users.
 
    .PARAMETER SkipDependencies
    Skip installing app dependencies.
 
    .PARAMETER NoCache
    Does not use the download cache.
 
    .PARAMETER NoScoopUpdate
    Does not update Scoop.
 
    .PARAMETER SkipHashCheck
    Skips hash validation.
#>

function Install-ScoopApp {
    [CmdletBinding(SupportsShouldProcess)]
    param (
        [Parameter(Mandatory,
            Position = 0,
            ValueFromPipeline,
            ValueFromPipelineByPropertyName)]
        [ValidateNotNullOrEmpty()]
        [string[]]
        $Name,

        [Parameter(Position = 1,
            ValueFromPipelineByPropertyName)]
        [ValidateNotNullOrEmpty()]
        [string]
        $Version,

        [ValidateSet('32bit', '64bit', 'arm64')]
        [string]
        $Architecture,

        [switch]
        $Global,

        [switch]
        $SkipDependencies,

        [switch]
        $NoCache,

        [switch]
        $NoScoopUpdate,

        [switch]
        $SkipHashCheck
    )

    process {
        foreach ($_name in $Name) {
            if ($PSCmdlet.ShouldProcess($_name)) {
                if ($Version) {
                    $_name += "@$version"
                }

                $command = "& scoop install $_name"

                if ($PSBoundParameters.ContainsKey('Architecture')) {
                    $command += " --arch $Architecture"
                }

                if ($Global) { $command += " --global" }
                if ($SkipDependencies) { $command += " --independent" }
                if ($NoCache) { $command += " --no-cache" }
                if ($NoScoopUpdate) { $command += " --no-update-scoop" }
                if ($SkipHash) { $command += " --skip" }

                Write-Verbose -Message $command

                Invoke-Expression $command 6>&1 |
                ForEach-Object {
                    if ($_ -match '^ERROR') {
                        throw ($_ -replace '^ERROR ')
                    }

                    Write-Verbose -Message $_
                }
            }
        }
    }
}

<#
    .SYNOPSIS
    Updates Scoop apps.
 
    .DESCRIPTION
    Updates Scoop apps.
 
    .PARAMETER Name
    Specifies the app to update.
 
    .PARAMETER Global
    Updates a globally installed app.
 
    .PARAMETER SkipDependencies
    Skip installing app dependencies.
 
    .PARAMETER NoCache
    Does not use download cache.
 
    .PARAMETER SkipHashCheck
    Skips hash validation.
 
    .PARAMETER Force
    Reinstalls the app even if there is not a newer version.
#>

function Update-ScoopApp {
    [CmdletBinding(SupportsShouldProcess)]
    param (
        [Parameter(Position = 0,
            ValueFromPipeline,
            ValueFromPipelineByPropertyName)]
        [string[]]
        $Name = '*',

        [switch]
        $Global,

        [switch]
        $SkipDependencies,

        [switch]
        $NoCache,

        [switch]
        $SkipHashCheck,

        [Alias('Reinstall')]
        [switch]
        $Force
    )

    process {
        foreach ($_name in $Name) {
            if ($PSCmdlet.ShouldProcess($_name)) {
                $command = "& scoop update $_name"

                if ($Global) { $command += " --global" }
                if ($SkipDependencies) { $command += " --independent" }
                if ($NoCache) { $command += " --no-cache" }
                if ($SkipHashCheck) { $command += " --skip" }
                if ($Force) { $command += " --force" }

                Invoke-Expression $command 6>&1 |
                ForEach-Object {
                    if ($_ -match '^ERROR') {
                        throw ($_ -replace '^ERROR ')
                    }

                    Write-Verbose -Message $_
                }
            }
        }
    }
}

<#
.SYNOPSIS
Uninstalls Scoop apps.
 
.DESCRIPTION
Uninstalls Scoop apps.
 
.PARAMETER Name
Specifies the app name to uninstall.
 
.PARAMETER Global
Uninstalls a globally installed app.
 
.PARAMETER Purge
Remove all persistent data.
 
.EXAMPLE
An example
 
.NOTES
General notes
#>

function Uninstall-ScoopApp {
    [CmdletBinding(SupportsShouldProcess)]
    param (
        [Parameter(Mandatory,
            Position = 0,
            ValueFromPipeline,
            ValueFromPipelineByPropertyName)]
        [ValidateNotNullOrEmpty()]
        [string[]]
        $Name,

        [switch]
        $Global,

        [switch]
        $Purge
    )

    process {
        foreach ($_name in $Name) {
            if ($PSCmdlet.ShouldProcess($_name)) {
                $command = "& scoop uninstall $_name"
                if ($Global) { $command += " --global" }
                if ($Purge) { $command += " --purge" }

                Invoke-Expression $command 6>&1 |
                ForEach-Object {
                    if ($_ -match '^ERROR') {
                        throw ($_ -replace '^ERROR ')
                    }

                    Write-Verbose -Message $_
                }
            }
        }
    }
}

<#
    .SYNOPSIS
    Get Scoop buckets.
 
    .DESCRIPTION
    Get Scoop buckets.
 
    .PARAMETER Name
    Specifies the bucket.
#>

function Get-ScoopBucket {
    [CmdletBinding()]
    param (
        [Parameter(Position = 0,
            ValueFromPipeline,
            ValueFromPipelineByPropertyName)]
        [SupportsWildcards()]
        [ValidateNotNullOrEmpty()]
        [string[]]
        $Name = '*'
    )

    begin {
        $sources = & scoop bucket list 6>$null
    }

    process {
        foreach ($_name in $Name) {
            $sources |
            Where-Object Name -like $_name
        }
    }
}

<#
    .SYNOPSIS
    Registers a Scoop bucket.
 
    .DESCRIPTION
    Registers a Scoop bucket.
 
    .PARAMETER Name
    Specifies the bucket name.
 
    .PARAMETER Uri
    Specifies the bucket Uri.
 
    .PARAMETER Official
    Specifies an official bucket.
 
    .PARAMETER Force
    Reregister the bucket if it already exists.
#>

function Register-ScoopBucket {
    [CmdletBinding(DefaultParameterSetName = 'Name',
        SupportsShouldProcess,
        ConfirmImpact = 'Low')]
    param (
        [Parameter(Mandatory,
            ParameterSetName = 'Name',
            Position = 0)]
        [string]
        $Name,

        [Parameter(Mandatory,
            ParameterSetName = 'Name',
            Position = 1)]
        [string]
        $Uri,

        [Parameter(Mandatory,
            ParameterSetName = 'Official')]
        [ValidateScript({
                if ($_ -in (& scoop bucket known)) {
                    $true
                }
                else {
                    throw "Bucket '$_' is not an official bucket."
                }
            })]
        [string]
        $Official,

        [Parameter()]
        [switch]
        $Force
    )

    if ($PSCmdlet.ParameterSetName -eq 'Official') {
        $Name = $Official
    }

    $existing = Get-ScoopBucket -Name $Name

    if ($existing -and -not $Force) {
        throw "Bucket '$Name' already exists."
    }

    if ($PSCmdlet.ShouldProcess($Name)) {
        if ($existing) {
            & scoop bucket rm $Name
        }

        & scoop bucket add $Name $Uri 6>&1 |
        ForEach-Object {
            if ($_ -match '^ERROR') {
                throw ($_ -replace '^ERROR ')
            }

            Write-Verbose -Message $_
        }
    }
}

<#
    .SYNOPSIS
    Unregister Scoop bucket.
 
    .DESCRIPTION
    Unregister Scoop bucket.
 
    .PARAMETER Name
    Specifies the bucket name to unregister.
#>

function Unregister-ScoopBucket {
    [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'Low')]
    param (
        [Parameter(Mandatory, Position = 0)]
        [ValidateNotNullOrEmpty()]
        [string]
        $Name
    )

    if (-not (Get-ScoopBucket -Name $Name)) {
        throw "Bucket '$Name' does not exist."
    }

    if ($PSCmdlet.ShouldProcess($Name)) {
        & scoop bucket rm $Name
    }

    if (Get-ScoopBucket -Name $Name) {
        throw "Failed to remove bucket '$Name'."
    }
}

# SIG # Begin signature block
# MIIlQgYJKoZIhvcNAQcCoIIlMzCCJS8CAQExDzANBglghkgBZQMEAgEFADB5Bgor
# BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG
# KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCC5gvkdtblJh3mN
# gjSDtgBmqBmnRna/PQTxd271BV3qb6CCHtcwggW2MIIEHqADAgECAhEApPLKEVUi
# 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
# BgorBgEEAYI3AgELMQ4wDAYKKwYBBAGCNwIBFTAvBgkqhkiG9w0BCQQxIgQgxLSk
# IFrBzmVZaSxEoFUggjmyZlrmdC90KjQJVI1chUAwDQYJKoZIhvcNAQEBBQAEggGA
# HMJmVcN09jAAq65CwtlRvLroNasuPqxmBahtLx8Lc6Seiojigg4sPILhUhxE2JPM
# wUTH+vM0I7cvSeJ9seQW7q61scu7hUhVj7K6rR3pCGoCpPfcYSNwTkKNjgkwz7mv
# B/F/jnOgHNOflV+IxyqF5biJ8yVDPyXeoQfPDJIxC2GKnj6QBRYrF+wgNP5IF3jr
# A9+iMDDcEep7BXPp/1zMbF3M3oUqn/Wiva9rmz2OtbLbCgcK6/laMpiNkoJu60py
# hwoEkScl6UPAGVMiGu2qytNVc0wvKCT+1qPuKGEtoYsUpunrFQxbLGplw0vHxrla
# 28iIFKdxf7q0R0Yif8ZarssAEm8FiRPgiptOG8e+RTVSOTf/nY/tnDaAE8hONTFK
# dahuIMsk2ojPBKR9bCSp+oHNlAoP39JgXjodO+DUVzLtHqJgwCalzV9WH6u1hDzm
# xEf+CYYJnTqxEFB5VH2tp9wrKhQUQIhg7yhQxsOa914O/4RHtP32NrWwyXCxSnNz
# oYIDIjCCAx4GCSqGSIb3DQEJBjGCAw8wggMLAgEBMGkwVTELMAkGA1UEBhMCR0Ix
# GDAWBgNVBAoTD1NlY3RpZ28gTGltaXRlZDEsMCoGA1UEAxMjU2VjdGlnbyBQdWJs
# aWMgVGltZSBTdGFtcGluZyBDQSBSMzYCEDpSaiyEzlXmHWX8zBLY6YkwDQYJYIZI
# AWUDBAICBQCgeTAYBgkqhkiG9w0BCQMxCwYJKoZIhvcNAQcBMBwGCSqGSIb3DQEJ
# BTEPFw0yNDA4MTUxNTQ1NTBaMD8GCSqGSIb3DQEJBDEyBDBuMjmD2KrpaCDorBRH
# glp2p4WhtMOOqge62PS7GmV/xQdjILrQs42qaYkf5VqJBUYwDQYJKoZIhvcNAQEB
# BQAEggIAAi0f+iVij95OoRWt8JnMJIXLfxD7Uk3hYMp8OXhluRaj3QTXwJnA4QOt
# /0CHF3OFAqemD/UoAchERznSohdq7P6KOczoOLTV7AgerYwOznUZ7lvbQd6bopvc
# oYy/n2F5UG836bIWHpDV5rV8NBemxojflY4g15mNBJmRDt8HbBHXhL96djFX+Vw1
# ZKOL4dCLjWQLyWFqeYmIlF2s/DLMDH1I2ej/r7k2xhBhsd6erqhwCCsQn1y63SMX
# SSFLwNJ578AaK7cr2WsObXraqby9DFcvz3ggsqHCpZ9VQ+kNKaFEsEXRiuKvMR9A
# 8tyj+brPQwHvq95s2v6a28n6w9hjlgQAutLoaFPngETpvmh2Zsapuz8iqoxragmk
# B0Rvla+XMHMaT1Iyg/tfG61vNpIL93Dzixwc1gyfTOKqERi4v7mN3/lkowG3YTxy
# GhNfFdXDz0O94TbJ5pW30kJms+vB5p21BboivYe5gsqmiUa2zNwYHECFNbHFgv2c
# 9E/AFDaOVZS9pfFOzYtHCbDX9fOxDbtM/87F8nhFFJPd459y9Gm1b+/ZnPe3oYD6
# GqWWNNf8oi++6CluXGe58i6HPOZL15Pcei8m6bq/LsKM47hkq0pFS/Ji8vyabbjN
# 9/D3fZ0In1RB8VgHnIrzteEuxvqdWxnYnRNqBegES3Nz9v+3b8E=
# SIG # End signature block