AzStackHci.EnvironmentChecker.PortableUtilities.psm1

<#
    Small portable lightweight/standalone functions that are required to be called as signed local modules
    for the purpose of satisfying the Constraint Language Mode.
#>


function Get-SslCertificateChain
{
    <#
    .SYNOPSIS
        Retrieve remote ssl certificate & chain from https endpoint for Desktop and Core
    .NOTES
        Credit: https://github.com/markekraus
    #>

    [CmdletBinding()]
    param (
        [system.uri]
        $url,

        [Parameter()]
        [string]
        $Proxy,

        [Parameter()]
        [pscredential]
        $ProxyCredential
    )
    try
    {
        $cs = @'
    using System;
    using System.Collections.Generic;
    using System.Net.Http;
    using System.Net.Security;
    using System.Security.Cryptography.X509Certificates;
 
    namespace CertificateCapture
    {
        public class Utility
        {
            public static Func<HttpRequestMessage,X509Certificate2,X509Chain,SslPolicyErrors,Boolean> ValidationCallback =
                (message, cert, chain, errors) => {
                    CapturedCertificates.Clear();
                    var newCert = new X509Certificate2(cert);
                    var newChain = new X509Chain();
                    newChain.Build(newCert);
                    CapturedCertificates.Add(new CapturedCertificate(){
                        Certificate = newCert,
                        CertificateChain = newChain,
                        PolicyErrors = errors,
                        URI = message.RequestUri
                    });
                    return true;
                };
            public static List<CapturedCertificate> CapturedCertificates = new List<CapturedCertificate>();
        }
 
        public class CapturedCertificate
        {
            public X509Certificate2 Certificate { get; set; }
            public X509Chain CertificateChain { get; set; }
            public SslPolicyErrors PolicyErrors { get; set; }
            public Uri URI { get; set; }
        }
    }
'@


        try
        {
            if (-not ('CertificateCapture.Utility' -as [type]))
            {
                if ($PSEdition -ne 'Core')
                {
                    Add-Type -AssemblyName System.Net.Http
                    Add-Type $cs -ReferencedAssemblies System.Net.Http
                }
                else
                {
                    Add-Type $cs
                }
            }
        }
        catch
        {
            if ($_.Exception.Message -notmatch 'Definition of new types is not supported in this language mode')
            {
                throw "Language mode does not allow this test Error: $_"
            }
        }

        $Certs = [CertificateCapture.Utility]::CapturedCertificates
        $Handler = [System.Net.Http.HttpClientHandler]::new()
        if ($Proxy)
        {
            $Handler.Proxy = New-Object System.Net.WebProxy($proxy)
            if ($proxyCredential)
            {
                $Handler.DefaultProxyCredentials = $ProxyCredential
            }
        }
        $Handler.ServerCertificateCustomValidationCallback = [CertificateCapture.Utility]::ValidationCallback
        $Client = [System.Net.Http.HttpClient]::new($Handler)
        $null = $Client.GetAsync($url).Result
        return $Certs.CertificateChain
    }
    catch
    {
        throw $_
    }
}

function Test-Elevation
{
    ([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole] 'Administrator')
}

function Copy-RemoteItem {
    <#
    .SYNOPSIS
        Copies a file or folder from a local source to a remote machine.
 
    .DESCRIPTION
        This robust function supports copying either a single file or an entire folder to a remote system.
        It checks SMB connectivity, maps a PSDrive to the remote UNC share, and uses robocopy to perform the copy.
        If DestinationPath is not specified, the remote system’s TEMP folder is used.
        Robocopy’s output is logged to a file under $env:LocalRootFolderPath\MasLogs.
        Optionally, if a CmdletName is provided (and the copy is a file), the module is imported on the remote system
        and the existence of the specified cmdlet is verified.
 
    .PARAMETER SourcePath
        Full local path of the file or folder to copy.
 
    .PARAMETER DestinationPath
        (Optional) Destination path on the remote machine (for example, "C:\Temp" or "C:\Temp\MyFolder").
        If not provided, defaults to the remote system’s TEMP folder.
 
    .PARAMETER PsSession
        (Optional) An array of active PSSessions representing remote machines.
 
    .PARAMETER TargetNodeName
        (Optional) The remote machine name (if PsSession is not provided).
 
    .PARAMETER Credential
        (Optional) Credential to use when accessing the remote machine.
 
    .PARAMETER ExcludeDirs
        (Optional) Array of directory names to exclude (used only for folder copies).
 
    .PARAMETER ExcludeFiles
        (Optional) Array of file names to exclude (used only for folder copies).
 
    .PARAMETER SkipFirewallCheck
        (Optional) If specified, the function will skip checking/enabling the SMB firewall rule.
 
    .PARAMETER CmdletName
        (Optional) If provided (and the copy is a file), after copying the file the function imports it as a module
        on the remote machine and verifies that the specified cmdlet exists.
    #>

        [CmdletBinding()]
        param(
            [Parameter(Mandatory = $true)]
            [string]$SourcePath,

            [Parameter(Mandatory = $false)]
            [string]$DestinationPath,

            [Parameter(Mandatory = $false)]
            [System.Management.Automation.Runspaces.PSSession[]]$PsSession,

            [Parameter(Mandatory = $false)]
            [string]$TargetNodeName,

            [Parameter(Mandatory = $false)]
            [PSCredential]$Credential,

            [Parameter(Mandatory = $false)]
            [string[]]$ExcludeDirs,

            [Parameter(Mandatory = $false)]
            [string[]]$ExcludeFiles,

            [Parameter(Mandatory = $false)]
            [switch]$SkipFirewallCheck,

            [Parameter(Mandatory = $false)]
            [string]$CmdletName
        )

        try {
            # Ensure the log directory exists.
            $logDir = "$($env:LocalRootFolderPath)\MasLogs"
            if (-not (Test-Path $logDir)) {
                New-Item -ItemType Directory -Path $logDir | Out-Null
            }

            # Verify the source exists.
            if (-not (Test-Path -Path $SourcePath)) {
                throw "Source path '$SourcePath' does not exist."
            }
            $sourceIsDirectory = Test-Path -Path $SourcePath -PathType Container

            # Build a list of targets.
            $targets = @()
            if ($PsSession) {
                foreach ($session in $PsSession) {
                    $targets += [PSCustomObject]@{
                        ComputerName = $session.ComputerName
                        Credential   = $session.Runspace.ConnectionInfo.Credential
                        Session      = $session
                        IsLocal      = $false
                    }
                }
            }
            elseif ($TargetNodeName) {
                # Determine if the target is local.
                $targetIsLocal = $false
                if ($env:ComputerName -eq $TargetNodeName) {
                    $targetIsLocal = $true
                }
                else {
                    $dnsInfo = (Resolve-DnsName -Name $TargetNodeName -ErrorAction SilentlyContinue | Select-Object -First 1)
                    if ($dnsInfo.NameHost) {
                        # Case when IP is resolved
                        $thisComputerName = ($dnsInfo.NameHost).Split('.')[0]
                        if ($env:ComputerName -eq $thisComputerName) {
                            $targetIsLocal = $true
                        }
                    }
                    elseif ($dnsInfo.Name) {
                        # Case when hostname is resolved
                        $thisComputerName = ($dnsInfo.Name).Split('.')[0]
                        if ($env:ComputerName -eq $thisComputerName) {
                            $targetIsLocal = $true
                        }
                    }
                    else {
                        # No DNS match so try IP address instead
                        [array]$myIP = (Get-NetIPAddress).IPAddress
                        if ($TargetNodeName -in $myIP) {
                            $targetIsLocal = $true
                        }
                    }
                }
                $targets += [PSCustomObject]@{
                    ComputerName = $TargetNodeName
                    Credential   = $Credential
                    Session      = $null
                    IsLocal      = $targetIsLocal
                }
            }
            else {
                throw "Either PsSession or TargetNodeName must be provided."
            }

            foreach ($target in $targets) {
                $remoteComputer = $target.ComputerName

                # Determine the destination folder on the remote machine.
                # If no DestinationPath is provided, get the remote TEMP folder.
                $destPathForTarget = $DestinationPath
                if (-not $destPathForTarget) {
                    if ($target.Session) {
                        $destPathForTarget = Invoke-Command -Session $target.Session -ScriptBlock { return $env:TEMP }
                    }
                    else {
                        $destPathForTarget = Invoke-Command -ComputerName $remoteComputer -Credential $target.Credential -ScriptBlock { return $env:TEMP }
                    }
                }

                # Build log file name based on the destination folder’s leaf and remote computer.
                $destLeaf = Split-Path -Path $destPathForTarget -Leaf
                $timestamp = Get-Date -Format "yyyyMMdd-HHmmss"
                $logPath = "$logDir\AzStackHciEnvironment-envChecker-$destLeaf-$remoteComputer-Copy-$timestamp.log"

                Log-Info -Message "Initiating copy of '$SourcePath' to '$($remoteComputer):$($destPathForTarget)'" -Type Info

                # Check SMB connectivity (unless skipped) and enable the SMB firewall rule if needed.
                if (-not $SkipFirewallCheck) {
                    $firewallRulesChanged = @()
                    if (-not (Test-NetConnection -ComputerName $remoteComputer -Port 445 -InformationLevel Quiet)) {
                        Log-Info -Message "Cannot reach $remoteComputer on port 445. Enabling SMB firewall rule." -Type Info
                        if ($target.Session) {
                            $firewallRulesChanged += Enable-SmbAccess -PsSession $target.Session
                        }
                        else {
                            $firewallRulesChanged += Invoke-Command -ComputerName $remoteComputer -Credential $target.Credential -ScriptBlock {
                                param($computer)
                                # TODO : Would this function be available in the remote session?
                                Enable-SmbAccess -ComputerName $computer
                            } -ArgumentList $remoteComputer
                        }
                        if ($firewallRulesChanged.Count -gt 0) {
                            foreach ($node in $firewallRulesChanged.Keys) {
                                Log-Info -Message "SMB firewall rules enabled on '$($node)': $($firewallRulesChanged.$node -join ',')" -Type Info
                            }
                        }
                        # Retry the SMB connection check after enabling the firewall rule.
                        if (-not (Test-NetConnection -ComputerName $remoteComputer -Port 445 -InformationLevel Quiet)) {
                            Log-Info "Failed to reach $($remoteComputer) on port 445 after enabling SMB-In firewall rules."
                            Log-Info "Some other network policy, or a custom local firewall rule is blocking SMB access to '$($remoteComputer)'."
                            throw "Failed to reach $($remoteComputer) on port 445 after enabling SMB-In firewall rules."
                        }
                    }
                }

                # Determine the UNC path to map.
                # If the destination path is like "C:\Temp" (or the remote TEMP folder),
                # then build the UNC path as "\\RemoteComputer\C$\Temp".
                if ($destPathForTarget -match '^(\w):\\(.*)$') {
                    $driveLetter = $Matches[1]
                    $folderPath  = $Matches[2]
                    $remoteShare = "\\$remoteComputer\$($driveLetter + '$')"
                    if ($folderPath -ne "") {
                        $remoteUNC = Join-Path -Path $remoteShare -ChildPath $folderPath
                    }
                    else {
                        $remoteUNC = $remoteShare
                    }
                }
                else {
                    $remoteUNC = $destPathForTarget
                }
                Log-Info -Message "Mapping PSDrive 'RemoteCopy' to '$remoteUNC' on '$remoteComputer'" -Type Info

                # Remove any existing PSDrive mapping.
                if (Get-PSDrive -Name RemoteCopy -ErrorAction SilentlyContinue) {
                    Get-PSDrive -Name RemoteCopy -ErrorAction SilentlyContinue | Remove-PSDrive -Force
                }

                # Retry mapping the PSDrive.
                $maxRetry  = 4
                $attempt   = 0
                $driveMapped = $false
                while (-not $driveMapped -and $attempt -lt $maxRetry) {
                    $attempt++
                    Log-Info -Message "Attempt $($attempt) to map PSDrive to '$remoteUNC'" -Type Info
                    try {
                        New-PSDrive -Name RemoteCopy -PSProvider FileSystem -Root $remoteUNC -Credential $target.Credential -ErrorAction Stop | Out-Null
                        if (Get-PSDrive -Name RemoteCopy -ErrorAction SilentlyContinue) {
                            $driveMapped = $true
                            Log-Info -Message "PSDrive mapped successfully to '$remoteUNC'" -Type Info
                        }
                    }
                    catch {
                        Log-Info -Message "Mapping PSDrive failed on attempt $($attempt): $($_.Exception.Message)" -Type Info
                        if ($attempt -ge $maxRetry) {
                            throw "Failed to map PSDrive to '$remoteUNC' for file '$SourcePath' on node '$remoteComputer' after $attempt attempts."
                        }
                        Start-Sleep -Seconds 15
                    }
                }

                # Build the final destination path.
                # If the user provided a DestinationPath, do drive-letter replacement;
                # otherwise, simply use the mapped PSDrive’s root.
                if ($DestinationPath) {
                    if ($destPathForTarget -match '^\w:') {
                        $finalDestPath = $destPathForTarget -replace '^\w:', (Get-PSDrive -Name RemoteCopy).Root
                    }
                    else {
                        $finalDestPath = (Get-PSDrive -Name RemoteCopy).Root
                    }
                }
                else {
                    $finalDestPath = (Get-PSDrive -Name RemoteCopy).Root
                }
                Log-Info -Message "Final destination path set to '$finalDestPath'" -Type Info

                # Build the robocopy command.
                if ($sourceIsDirectory) {
                    $copyCmd = "robocopy.exe `"$SourcePath`" `"$finalDestPath`" *.* /MIR /NP /R:2 /W:10 /LOG:`"$logPath`""
                }
                else {
                    $sourceDir  = Split-Path -Path $SourcePath
                    $fileName   = Split-Path -Path $SourcePath -Leaf
                    $copyCmd = "robocopy.exe `"$sourceDir`" `"$finalDestPath`" `"$fileName`" /COPYALL /NP /R:2 /W:10 /LOG:`"$logPath`""
                }
                if ($ExcludeFiles) {
                    $copyCmd += " /XF " + ($ExcludeFiles -join ' ')
                }
                if ($ExcludeDirs) {
                    $copyCmd += " /XD " + ($ExcludeDirs -join ' ')
                }

                Log-Info -Message "Calling: $copyCmd" -Type Info

                try {
                    # Execute robocopy.
                    $output = Invoke-Command -ScriptBlock { param($cmd) cmd.exe /c $cmd } -ArgumentList $copyCmd
                    if ($LASTEXITCODE -ge 8) {
                        Log-Info -Message ("Robocopy failed with exit code {0}" -f $LASTEXITCODE) -ConsoleOut -Type Error
                        Log-Info -Message ($output | Out-String).Trim() -ConsoleOut -Type Info
                        throw "Robocopy failed with exit code $LASTEXITCODE"
                    }
                    else {
                        Log-Info -Message "Robocopy completed successfully." -Type Info
                    }
                }
                catch {
                    Log-Info -Message ("Copy operation failed: " + $_.Exception.Message) -Type Error
                    throw "Copy operation failed for file '$SourcePath' to destination '$destPathForTarget' on node '$remoteComputer': $($_.Exception.Message)"
                }
                finally {
                    # Clean up: remove the mapped PSDrive.
                    if (Get-PSDrive -Name RemoteCopy -ErrorAction SilentlyContinue) {
                        Get-PSDrive -Name RemoteCopy -ErrorAction SilentlyContinue | Remove-PSDrive -Force
                    }
                    # Revert the firewall rule if it was enabled.
                    if ($firewallRulesChanged.Count -gt 0) {
                        Log-Info -Message "Reverting SMB firewall rule on '$remoteComputer'" -Type Info
                        if ($target.Session) {
                            Disable-SmbAccess -PsSession $target.Session | Out-Null
                        }
                        else {
                            Invoke-Command -ComputerName $remoteComputer -Credential $target.Credential -ScriptBlock {
                                param($computer)
                                # TODO : Would this function be available in the remote session?
                                Disable-SmbAccess -ComputerName $computer
                            } -ArgumentList $remoteComputer | Out-Null
                        }
                    }
                }
                Log-Info -Message "Successfully copied '$SourcePath' to '$($remoteComputer):$($destPathForTarget)'" -Type Info

                # If a CmdletName is provided (and this is a file copy), import the module on the remote system.
                if ($CmdletName -and -not $sourceIsDirectory) {
                    $destinationFileName = Split-Path -Path $SourcePath -Leaf
                    # Use the local path (as returned by the remote system) for the module import.
                    $modulePath = Join-Path $destPathForTarget $destinationFileName
                    Log-Info -Message "Importing module from '$modulePath' on '$remoteComputer' to verify cmdlet '$CmdletName'" -Type Info
                    if ($target.Session) {
                        Invoke-Command -Session $target.Session -ScriptBlock {
                            param($modulePath, $CmdletName)
                            Import-Module $modulePath -Force
                            if (-not (Get-Command -Name $CmdletName -ErrorAction SilentlyContinue)) {
                                throw "Failed to import module on $($env:COMPUTERNAME) for cmdlet $CmdletName"
                            }
                        } -ArgumentList $modulePath, $CmdletName
                    }
                    else {
                        Invoke-Command -ComputerName $remoteComputer -Credential $target.Credential -ScriptBlock {
                            param($modulePath, $CmdletName)
                            Import-Module $modulePath -Force
                            if (-not (Get-Command -Name $CmdletName -ErrorAction SilentlyContinue)) {
                                throw "Failed to import module on $($env:COMPUTERNAME) for cmdlet $CmdletName"
                            }
                        } -ArgumentList $modulePath, $CmdletName
                    }
                    Log-Info -Message "Module imported and cmdlet '$CmdletName' verified on '$remoteComputer'" -Type Info
                }
            }
        }
        catch {
            throw "Copy-RemoteItem failed for file '$SourcePath' to destination '$destPathForTarget' on node '$remoteComputer'. Error: $($_.Exception.Message)"
        }
    }

function Enable-SmbAccess
{
    param (
        [Parameter(Mandatory = $true)]
        [System.Management.Automation.Runspaces.PSSession[]]
        $PsSession,

        [Parameter(Mandatory = $false)]
        [string[]]$Rules = @('FPS-SMB-In-TCP','FailoverCluster-SMB-TCP-In')
    )
    try
    {
        $rulesEnabled = @()
        foreach ($session in $PsSession)
        {
            Log-Info "Enabling SMB access through firewall on $($session.ComputerName)"
            [string[]]$enabled = Invoke-Command -Session $session -ScriptBlock {
                $fwRuleChanged = @()
                foreach ($name in $using:Rules)
                {
                    if ((Get-NetFirewallRule -Name $name).Enabled -eq 'False')
                    {
                        Set-NetFirewallRule -Name $name -Enabled True | Out-Null
                        $fwRuleChanged += $name
                    }
                }
                $fwRuleChanged
            }
            if ($enabled.Count -gt 0)
            {
                $rulesEnabled += @{$session.ComputerName = $enabled}
                foreach ($name in $enabled)
                {
                    Log-Info "SMB access enabled on $($session.ComputerName) for rule: $name"
                }
            }
            else
            {
                Log-Info "No changes made to SMB access on $($session.ComputerName)"
            }
        }
        return $rulesEnabled
    }
    catch
    {
        Log-Info "Failed to enable SMB access on '$($session.ComputerName)'. Error: $($_.Exception)"
    }
}

function Disable-SmbAccess
{
    param (
        [Parameter(Mandatory = $true)]
        [System.Management.Automation.Runspaces.PSSession[]]
        $PsSession,

        [Parameter(Mandatory = $false)]
        [string[]]$Rules = @('FPS-SMB-In-TCP','FailoverCluster-SMB-TCP-In')
    )
    try
    {
        foreach ($session in $PsSession)
        {
            foreach ($name in $Rules)
            {
                Log-Info "Disabling SMB access through firewall on $($session.ComputerName) for rule $name"
                Invoke-Command -Session $session -ScriptBlock {
                    param($RuleName)
                    if ((Get-NetFirewallRule -Name $RuleName).Enabled -eq 'True')
                    {
                        Set-NetFirewallRule -Name $RuleName -Enabled False | Out-Null
                    }
                } -ArgumentList $name
            }
        }
    }
    catch
    {
        Log-Info "Failed to disable SMB access on '$($session.ComputerName)'. Error: $($_.Exception)"
    }
}

function Remove-UtilityModule
{
    <#
    .SYNOPSIS
        Remove EnvironmentChecker module
    .DESCRIPTION
        Removes EnvironmentChecker module
    #>

    [CmdletBinding()]
    param (
        [Parameter()]
        [System.Management.Automation.Runspaces.PSSession[]]
        $PsSession
    )
    try
    {
        foreach ($Session in $PsSession)
        {
            Log-Info "Removing module from $($Session.ComputerName)"
            Invoke-Command -Session $Session -ScriptBlock {
                Remove-Item -Path "$env:TEMP\AzStackHci.EnvironmentChecker.PortableUtilities.psm1" -Force -ErrorAction SilentlyContinue
            }
        }
    }
    catch
    {
        Log-Info "Failed to remove EnvironmentChecker module. Error: $($_.exception)"
    }
}

Export-ModuleMember -Function Copy-RemoteItem
Export-ModuleMember -Function Remove-UtilityModule
Export-ModuleMember -Function Test-Elevation
Export-ModuleMember -Function Get-SslCertificateChain
Export-ModuleMember -Function Enable-SmbAccess
Export-ModuleMember -Function Disable-SmbAccess
# SIG # Begin signature block
# MIIoRgYJKoZIhvcNAQcCoIIoNzCCKDMCAQExDzANBglghkgBZQMEAgEFADB5Bgor
# BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG
# KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCBp5MpomCjVHdCW
# NdakDjzBnVNYeLW7CYcgNelMtk0vEKCCDXYwggX0MIID3KADAgECAhMzAAAEBGx0
# Bv9XKydyAAAAAAQEMA0GCSqGSIb3DQEBCwUAMH4xCzAJBgNVBAYTAlVTMRMwEQYD
# VQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNy
# b3NvZnQgQ29ycG9yYXRpb24xKDAmBgNVBAMTH01pY3Jvc29mdCBDb2RlIFNpZ25p
# bmcgUENBIDIwMTEwHhcNMjQwOTEyMjAxMTE0WhcNMjUwOTExMjAxMTE0WjB0MQsw
# CQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9u
# ZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMR4wHAYDVQQDExVNaWNy
# b3NvZnQgQ29ycG9yYXRpb24wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB
# AQC0KDfaY50MDqsEGdlIzDHBd6CqIMRQWW9Af1LHDDTuFjfDsvna0nEuDSYJmNyz
# NB10jpbg0lhvkT1AzfX2TLITSXwS8D+mBzGCWMM/wTpciWBV/pbjSazbzoKvRrNo
# DV/u9omOM2Eawyo5JJJdNkM2d8qzkQ0bRuRd4HarmGunSouyb9NY7egWN5E5lUc3
# a2AROzAdHdYpObpCOdeAY2P5XqtJkk79aROpzw16wCjdSn8qMzCBzR7rvH2WVkvF
# HLIxZQET1yhPb6lRmpgBQNnzidHV2Ocxjc8wNiIDzgbDkmlx54QPfw7RwQi8p1fy
# 4byhBrTjv568x8NGv3gwb0RbAgMBAAGjggFzMIIBbzAfBgNVHSUEGDAWBgorBgEE
# AYI3TAgBBggrBgEFBQcDAzAdBgNVHQ4EFgQU8huhNbETDU+ZWllL4DNMPCijEU4w
# RQYDVR0RBD4wPKQ6MDgxHjAcBgNVBAsTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEW
# MBQGA1UEBRMNMjMwMDEyKzUwMjkyMzAfBgNVHSMEGDAWgBRIbmTlUAXTgqoXNzci
# tW2oynUClTBUBgNVHR8ETTBLMEmgR6BFhkNodHRwOi8vd3d3Lm1pY3Jvc29mdC5j
# b20vcGtpb3BzL2NybC9NaWNDb2RTaWdQQ0EyMDExXzIwMTEtMDctMDguY3JsMGEG
# CCsGAQUFBwEBBFUwUzBRBggrBgEFBQcwAoZFaHR0cDovL3d3dy5taWNyb3NvZnQu
# Y29tL3BraW9wcy9jZXJ0cy9NaWNDb2RTaWdQQ0EyMDExXzIwMTEtMDctMDguY3J0
# MAwGA1UdEwEB/wQCMAAwDQYJKoZIhvcNAQELBQADggIBAIjmD9IpQVvfB1QehvpC
# Ge7QeTQkKQ7j3bmDMjwSqFL4ri6ae9IFTdpywn5smmtSIyKYDn3/nHtaEn0X1NBj
# L5oP0BjAy1sqxD+uy35B+V8wv5GrxhMDJP8l2QjLtH/UglSTIhLqyt8bUAqVfyfp
# h4COMRvwwjTvChtCnUXXACuCXYHWalOoc0OU2oGN+mPJIJJxaNQc1sjBsMbGIWv3
# cmgSHkCEmrMv7yaidpePt6V+yPMik+eXw3IfZ5eNOiNgL1rZzgSJfTnvUqiaEQ0X
# dG1HbkDv9fv6CTq6m4Ty3IzLiwGSXYxRIXTxT4TYs5VxHy2uFjFXWVSL0J2ARTYL
# E4Oyl1wXDF1PX4bxg1yDMfKPHcE1Ijic5lx1KdK1SkaEJdto4hd++05J9Bf9TAmi
# u6EK6C9Oe5vRadroJCK26uCUI4zIjL/qG7mswW+qT0CW0gnR9JHkXCWNbo8ccMk1
# sJatmRoSAifbgzaYbUz8+lv+IXy5GFuAmLnNbGjacB3IMGpa+lbFgih57/fIhamq
# 5VhxgaEmn/UjWyr+cPiAFWuTVIpfsOjbEAww75wURNM1Imp9NJKye1O24EspEHmb
# DmqCUcq7NqkOKIG4PVm3hDDED/WQpzJDkvu4FrIbvyTGVU01vKsg4UfcdiZ0fQ+/
# V0hf8yrtq9CkB8iIuk5bBxuPMIIHejCCBWKgAwIBAgIKYQ6Q0gAAAAAAAzANBgkq
# hkiG9w0BAQsFADCBiDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24x
# EDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlv
# bjEyMDAGA1UEAxMpTWljcm9zb2Z0IFJvb3QgQ2VydGlmaWNhdGUgQXV0aG9yaXR5
# IDIwMTEwHhcNMTEwNzA4MjA1OTA5WhcNMjYwNzA4MjEwOTA5WjB+MQswCQYDVQQG
# EwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwG
# A1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSgwJgYDVQQDEx9NaWNyb3NvZnQg
# Q29kZSBTaWduaW5nIFBDQSAyMDExMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIIC
# CgKCAgEAq/D6chAcLq3YbqqCEE00uvK2WCGfQhsqa+laUKq4BjgaBEm6f8MMHt03
# a8YS2AvwOMKZBrDIOdUBFDFC04kNeWSHfpRgJGyvnkmc6Whe0t+bU7IKLMOv2akr
# rnoJr9eWWcpgGgXpZnboMlImEi/nqwhQz7NEt13YxC4Ddato88tt8zpcoRb0Rrrg
# OGSsbmQ1eKagYw8t00CT+OPeBw3VXHmlSSnnDb6gE3e+lD3v++MrWhAfTVYoonpy
# 4BI6t0le2O3tQ5GD2Xuye4Yb2T6xjF3oiU+EGvKhL1nkkDstrjNYxbc+/jLTswM9
# sbKvkjh+0p2ALPVOVpEhNSXDOW5kf1O6nA+tGSOEy/S6A4aN91/w0FK/jJSHvMAh
# dCVfGCi2zCcoOCWYOUo2z3yxkq4cI6epZuxhH2rhKEmdX4jiJV3TIUs+UsS1Vz8k
# A/DRelsv1SPjcF0PUUZ3s/gA4bysAoJf28AVs70b1FVL5zmhD+kjSbwYuER8ReTB
# w3J64HLnJN+/RpnF78IcV9uDjexNSTCnq47f7Fufr/zdsGbiwZeBe+3W7UvnSSmn
# Eyimp31ngOaKYnhfsi+E11ecXL93KCjx7W3DKI8sj0A3T8HhhUSJxAlMxdSlQy90
# lfdu+HggWCwTXWCVmj5PM4TasIgX3p5O9JawvEagbJjS4NaIjAsCAwEAAaOCAe0w
# ggHpMBAGCSsGAQQBgjcVAQQDAgEAMB0GA1UdDgQWBBRIbmTlUAXTgqoXNzcitW2o
# ynUClTAZBgkrBgEEAYI3FAIEDB4KAFMAdQBiAEMAQTALBgNVHQ8EBAMCAYYwDwYD
# VR0TAQH/BAUwAwEB/zAfBgNVHSMEGDAWgBRyLToCMZBDuRQFTuHqp8cx0SOJNDBa
# BgNVHR8EUzBRME+gTaBLhklodHRwOi8vY3JsLm1pY3Jvc29mdC5jb20vcGtpL2Ny
# bC9wcm9kdWN0cy9NaWNSb29DZXJBdXQyMDExXzIwMTFfMDNfMjIuY3JsMF4GCCsG
# AQUFBwEBBFIwUDBOBggrBgEFBQcwAoZCaHR0cDovL3d3dy5taWNyb3NvZnQuY29t
# L3BraS9jZXJ0cy9NaWNSb29DZXJBdXQyMDExXzIwMTFfMDNfMjIuY3J0MIGfBgNV
# HSAEgZcwgZQwgZEGCSsGAQQBgjcuAzCBgzA/BggrBgEFBQcCARYzaHR0cDovL3d3
# dy5taWNyb3NvZnQuY29tL3BraW9wcy9kb2NzL3ByaW1hcnljcHMuaHRtMEAGCCsG
# AQUFBwICMDQeMiAdAEwAZQBnAGEAbABfAHAAbwBsAGkAYwB5AF8AcwB0AGEAdABl
# AG0AZQBuAHQALiAdMA0GCSqGSIb3DQEBCwUAA4ICAQBn8oalmOBUeRou09h0ZyKb
# C5YR4WOSmUKWfdJ5DJDBZV8uLD74w3LRbYP+vj/oCso7v0epo/Np22O/IjWll11l
# hJB9i0ZQVdgMknzSGksc8zxCi1LQsP1r4z4HLimb5j0bpdS1HXeUOeLpZMlEPXh6
# I/MTfaaQdION9MsmAkYqwooQu6SpBQyb7Wj6aC6VoCo/KmtYSWMfCWluWpiW5IP0
# wI/zRive/DvQvTXvbiWu5a8n7dDd8w6vmSiXmE0OPQvyCInWH8MyGOLwxS3OW560
# STkKxgrCxq2u5bLZ2xWIUUVYODJxJxp/sfQn+N4sOiBpmLJZiWhub6e3dMNABQam
# ASooPoI/E01mC8CzTfXhj38cbxV9Rad25UAqZaPDXVJihsMdYzaXht/a8/jyFqGa
# J+HNpZfQ7l1jQeNbB5yHPgZ3BtEGsXUfFL5hYbXw3MYbBL7fQccOKO7eZS/sl/ah
# XJbYANahRr1Z85elCUtIEJmAH9AAKcWxm6U/RXceNcbSoqKfenoi+kiVH6v7RyOA
# 9Z74v2u3S5fi63V4GuzqN5l5GEv/1rMjaHXmr/r8i+sLgOppO6/8MO0ETI7f33Vt
# Y5E90Z1WTk+/gFcioXgRMiF670EKsT/7qMykXcGhiJtXcVZOSEXAQsmbdlsKgEhr
# /Xmfwb1tbWrJUnMTDXpQzTGCGiYwghoiAgEBMIGVMH4xCzAJBgNVBAYTAlVTMRMw
# EQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVN
# aWNyb3NvZnQgQ29ycG9yYXRpb24xKDAmBgNVBAMTH01pY3Jvc29mdCBDb2RlIFNp
# Z25pbmcgUENBIDIwMTECEzMAAAQEbHQG/1crJ3IAAAAABAQwDQYJYIZIAWUDBAIB
# BQCgga4wGQYJKoZIhvcNAQkDMQwGCisGAQQBgjcCAQQwHAYKKwYBBAGCNwIBCzEO
# MAwGCisGAQQBgjcCARUwLwYJKoZIhvcNAQkEMSIEIH1c4kA2Z8+QkU0u+yIwj7Aj
# Iy9AptdkaT9IPeoi3HGZMEIGCisGAQQBgjcCAQwxNDAyoBSAEgBNAGkAYwByAG8A
# cwBvAGYAdKEagBhodHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20wDQYJKoZIhvcNAQEB
# BQAEggEAdBf6Ms6NqqjSsaKfrIXH3ZwA3GVe+jcfNXmfn2wyGKCnszc7pK/BoybU
# gaVyK8lt69gFHCTlevbrGRSKk95NLpU5PX/mNuL8qQ0vMKn010/prlkVBGRizeJ4
# EvdQcDPag3bGlacRbEZExAHN5okX5rS77uhb1ycSFYkb69G9xwN7pUfA/v62kJyv
# OWl0eriWIoeG9M2di6xkQOmqE1VQrfd2scW4lZGsTGHvWheHq2XEawgMW5CLW69P
# GiYxQ39tn2O4eusOkV5DGToGakUYisjItYmeoY5ie13XMXiY24V1svJSd6WqzV6Y
# c2YazXDU+Z/tsRyGDwjHygt5//KCfKGCF7AwghesBgorBgEEAYI3AwMBMYIXnDCC
# F5gGCSqGSIb3DQEHAqCCF4kwgheFAgEDMQ8wDQYJYIZIAWUDBAIBBQAwggFaBgsq
# hkiG9w0BCRABBKCCAUkEggFFMIIBQQIBAQYKKwYBBAGEWQoDATAxMA0GCWCGSAFl
# AwQCAQUABCDnBmOB5d1sJ0jh/9kpCz9C6C3GTcIq6p4JBTDu1TgnOAIGaC4/lVwg
# GBMyMDI1MDYxMDE1NDgzMS4zNjNaMASAAgH0oIHZpIHWMIHTMQswCQYDVQQGEwJV
# UzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UE
# ChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMS0wKwYDVQQLEyRNaWNyb3NvZnQgSXJl
# bGFuZCBPcGVyYXRpb25zIExpbWl0ZWQxJzAlBgNVBAsTHm5TaGllbGQgVFNTIEVT
# Tjo1NzFBLTA1RTAtRDk0NzElMCMGA1UEAxMcTWljcm9zb2Z0IFRpbWUtU3RhbXAg
# U2VydmljZaCCEf4wggcoMIIFEKADAgECAhMzAAAB+8vLbDdn5TCVAAEAAAH7MA0G
# CSqGSIb3DQEBCwUAMHwxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9u
# MRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRp
# b24xJjAkBgNVBAMTHU1pY3Jvc29mdCBUaW1lLVN0YW1wIFBDQSAyMDEwMB4XDTI0
# MDcyNTE4MzExM1oXDTI1MTAyMjE4MzExM1owgdMxCzAJBgNVBAYTAlVTMRMwEQYD
# VQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNy
# b3NvZnQgQ29ycG9yYXRpb24xLTArBgNVBAsTJE1pY3Jvc29mdCBJcmVsYW5kIE9w
# ZXJhdGlvbnMgTGltaXRlZDEnMCUGA1UECxMeblNoaWVsZCBUU1MgRVNOOjU3MUEt
# MDVFMC1EOTQ3MSUwIwYDVQQDExxNaWNyb3NvZnQgVGltZS1TdGFtcCBTZXJ2aWNl
# MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAqMJWQeWAq4LwvSjYsjP0
# Uvhvm0j0aAOJiMLg0sLfxKoTXAdKD6oMuq5rF5oEiOxV+9ox0H95Q8fhoZq3x9lx
# guZyTOK4l2xtcgtJCtjXRllM2bTpjOg35RUrBy0cAloBU9GJBs7LBNrcbH6rBiOv
# qDQNicPRZwq16xyjMidU1J1AJuat9yLn7taifoD58blYEcBvkj5dH1la9zU846QD
# eOoRO6NcqHLsDx8/zVKZxP30mW6Y7RMsqtB8cGCgGwVVurOnaNLXs31qTRTyVHX8
# ppOdoSihCXeqebgJCRzG8zG/e/k0oaBjFFGl+8uFELwCyh4wK9Z5+azTzfa2GD4p
# 6ihtskXs3lnW05UKfDJhAADt6viOc0Rk/c8zOiqzh0lKpf/eWUY2o/hvcDPZNgLa
# HvyfDqb8AWaKvO36iRZSXqhSw8SxJo0TCpsbCjmtx0LpHnqbb1UF7cq09kCcfWTD
# PcN12pbYLqck0bIIfPKbc7HnrkNQks/mSbVZTnDyT3O8zF9q4DCfWesSr1akycDd
# uGxCdKBvgtJh1YxDq1skTweYx5iAWXnB7KMyls3WQZbTubTCLLt8Xn8t+slcKm5D
# kvobubmHSriuTA3wTyIy4FxamTKm0VDu9mWds8MtjUSJVwNVVlBXaQ3ZMcVjijyV
# oUNVuBY9McwYcIQK62wQ20ECAwEAAaOCAUkwggFFMB0GA1UdDgQWBBRHVSGYUNQ3
# RwOl71zIAuUjIKg1KjAfBgNVHSMEGDAWgBSfpxVdAF5iXYP05dJlpxtTNRnpcjBf
# BgNVHR8EWDBWMFSgUqBQhk5odHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20vcGtpb3Bz
# L2NybC9NaWNyb3NvZnQlMjBUaW1lLVN0YW1wJTIwUENBJTIwMjAxMCgxKS5jcmww
# bAYIKwYBBQUHAQEEYDBeMFwGCCsGAQUFBzAChlBodHRwOi8vd3d3Lm1pY3Jvc29m
# dC5jb20vcGtpb3BzL2NlcnRzL01pY3Jvc29mdCUyMFRpbWUtU3RhbXAlMjBQQ0El
# MjAyMDEwKDEpLmNydDAMBgNVHRMBAf8EAjAAMBYGA1UdJQEB/wQMMAoGCCsGAQUF
# BwMIMA4GA1UdDwEB/wQEAwIHgDANBgkqhkiG9w0BAQsFAAOCAgEAwzoIKOY2dnUj
# fWuMiGoz/ovoc1e86VwWaZNFdgRmOoQuRe4nLdtZONtTHNk3Sj3nkyBszzxSbZEQ
# 0DduyKHHI5P8V87jFttGnlR0wPP22FAebbvAbutkMMVQMFzhVBWiWD0VAnu9x0fj
# ifLKDAVXLwoun5rCFqwbasXFc7H/0DPiC+DBn3tUxefvcxUCys4+DC3s8CYp7WWX
# pZ8Wb/vdBhDliHmB7pWcmsB83uc4/P2GmAI3HMkOEu7fCaSYoQhouWOr07l/KM4T
# ndylIirm8f2WwXQcFEzmUvISM6ludUwGlVNfTTJUq2bTDEd3tlDKtV9AUY3rrnFw
# HTwJryLtT4IFhvgBfND3mL1eeSakKf7xTII4Jyt15SXhHd5oI/XGjSgykgJrWA57
# rGnAC7ru3/ZbFNCMK/Jj6X8X4L6mBOYa2NGKwH4A37YGDrecJ/qXXWUYvfLYqHGf
# 8ThYl12Yg1rwSKpWLolA/B1eqBw4TRcvVY0IvNNi5sm+//HJ9Aw6NJuR/uDR7X7v
# DXicpXMlRNgFMyADb8AFIvQPdHqcRpRorY+YUGlvzeJx/2gNYyezAokbrFhACsJ2
# BfyeLyCEo6AuwEHn511PKE8dK4JvlmLSoHj7VFR3NHDk3zRkx0ExkmF8aOdpvoKh
# uwBCxoZ/JhbzSzrvZ74GVjKKIyt5FA0wggdxMIIFWaADAgECAhMzAAAAFcXna54C
# m0mZAAAAAAAVMA0GCSqGSIb3DQEBCwUAMIGIMQswCQYDVQQGEwJVUzETMBEGA1UE
# CBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9z
# b2Z0IENvcnBvcmF0aW9uMTIwMAYDVQQDEylNaWNyb3NvZnQgUm9vdCBDZXJ0aWZp
# Y2F0ZSBBdXRob3JpdHkgMjAxMDAeFw0yMTA5MzAxODIyMjVaFw0zMDA5MzAxODMy
# MjVaMHwxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQH
# EwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xJjAkBgNV
# BAMTHU1pY3Jvc29mdCBUaW1lLVN0YW1wIFBDQSAyMDEwMIICIjANBgkqhkiG9w0B
# AQEFAAOCAg8AMIICCgKCAgEA5OGmTOe0ciELeaLL1yR5vQ7VgtP97pwHB9KpbE51
# yMo1V/YBf2xK4OK9uT4XYDP/XE/HZveVU3Fa4n5KWv64NmeFRiMMtY0Tz3cywBAY
# 6GB9alKDRLemjkZrBxTzxXb1hlDcwUTIcVxRMTegCjhuje3XD9gmU3w5YQJ6xKr9
# cmmvHaus9ja+NSZk2pg7uhp7M62AW36MEBydUv626GIl3GoPz130/o5Tz9bshVZN
# 7928jaTjkY+yOSxRnOlwaQ3KNi1wjjHINSi947SHJMPgyY9+tVSP3PoFVZhtaDua
# Rr3tpK56KTesy+uDRedGbsoy1cCGMFxPLOJiss254o2I5JasAUq7vnGpF1tnYN74
# kpEeHT39IM9zfUGaRnXNxF803RKJ1v2lIH1+/NmeRd+2ci/bfV+AutuqfjbsNkz2
# K26oElHovwUDo9Fzpk03dJQcNIIP8BDyt0cY7afomXw/TNuvXsLz1dhzPUNOwTM5
# TI4CvEJoLhDqhFFG4tG9ahhaYQFzymeiXtcodgLiMxhy16cg8ML6EgrXY28MyTZk
# i1ugpoMhXV8wdJGUlNi5UPkLiWHzNgY1GIRH29wb0f2y1BzFa/ZcUlFdEtsluq9Q
# BXpsxREdcu+N+VLEhReTwDwV2xo3xwgVGD94q0W29R6HXtqPnhZyacaue7e3Pmri
# Lq0CAwEAAaOCAd0wggHZMBIGCSsGAQQBgjcVAQQFAgMBAAEwIwYJKwYBBAGCNxUC
# BBYEFCqnUv5kxJq+gpE8RjUpzxD/LwTuMB0GA1UdDgQWBBSfpxVdAF5iXYP05dJl
# pxtTNRnpcjBcBgNVHSAEVTBTMFEGDCsGAQQBgjdMg30BATBBMD8GCCsGAQUFBwIB
# FjNodHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20vcGtpb3BzL0RvY3MvUmVwb3NpdG9y
# eS5odG0wEwYDVR0lBAwwCgYIKwYBBQUHAwgwGQYJKwYBBAGCNxQCBAweCgBTAHUA
# YgBDAEEwCwYDVR0PBAQDAgGGMA8GA1UdEwEB/wQFMAMBAf8wHwYDVR0jBBgwFoAU
# 1fZWy4/oolxiaNE9lJBb186aGMQwVgYDVR0fBE8wTTBLoEmgR4ZFaHR0cDovL2Ny
# bC5taWNyb3NvZnQuY29tL3BraS9jcmwvcHJvZHVjdHMvTWljUm9vQ2VyQXV0XzIw
# MTAtMDYtMjMuY3JsMFoGCCsGAQUFBwEBBE4wTDBKBggrBgEFBQcwAoY+aHR0cDov
# L3d3dy5taWNyb3NvZnQuY29tL3BraS9jZXJ0cy9NaWNSb29DZXJBdXRfMjAxMC0w
# Ni0yMy5jcnQwDQYJKoZIhvcNAQELBQADggIBAJ1VffwqreEsH2cBMSRb4Z5yS/yp
# b+pcFLY+TkdkeLEGk5c9MTO1OdfCcTY/2mRsfNB1OW27DzHkwo/7bNGhlBgi7ulm
# ZzpTTd2YurYeeNg2LpypglYAA7AFvonoaeC6Ce5732pvvinLbtg/SHUB2RjebYIM
# 9W0jVOR4U3UkV7ndn/OOPcbzaN9l9qRWqveVtihVJ9AkvUCgvxm2EhIRXT0n4ECW
# OKz3+SmJw7wXsFSFQrP8DJ6LGYnn8AtqgcKBGUIZUnWKNsIdw2FzLixre24/LAl4
# FOmRsqlb30mjdAy87JGA0j3mSj5mO0+7hvoyGtmW9I/2kQH2zsZ0/fZMcm8Qq3Uw
# xTSwethQ/gpY3UA8x1RtnWN0SCyxTkctwRQEcb9k+SS+c23Kjgm9swFXSVRk2XPX
# fx5bRAGOWhmRaw2fpCjcZxkoJLo4S5pu+yFUa2pFEUep8beuyOiJXk+d0tBMdrVX
# VAmxaQFEfnyhYWxz/gq77EFmPWn9y8FBSX5+k77L+DvktxW/tM4+pTFRhLy/AsGC
# onsXHRWJjXD+57XQKBqJC4822rpM+Zv/Cuk0+CQ1ZyvgDbjmjJnW4SLq8CdCPSWU
# 5nR0W2rRnj7tfqAxM328y+l7vzhwRNGQ8cirOoo6CGJ/2XBjU02N7oJtpQUQwXEG
# ahC0HVUzWLOhcGbyoYIDWTCCAkECAQEwggEBoYHZpIHWMIHTMQswCQYDVQQGEwJV
# UzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UE
# ChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMS0wKwYDVQQLEyRNaWNyb3NvZnQgSXJl
# bGFuZCBPcGVyYXRpb25zIExpbWl0ZWQxJzAlBgNVBAsTHm5TaGllbGQgVFNTIEVT
# Tjo1NzFBLTA1RTAtRDk0NzElMCMGA1UEAxMcTWljcm9zb2Z0IFRpbWUtU3RhbXAg
# U2VydmljZaIjCgEBMAcGBSsOAwIaAxUABHHn7NCGusZz2RfVbyuwYwPykBWggYMw
# gYCkfjB8MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UE
# BxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSYwJAYD
# VQQDEx1NaWNyb3NvZnQgVGltZS1TdGFtcCBQQ0EgMjAxMDANBgkqhkiG9w0BAQsF
# AAIFAOvycXswIhgPMjAyNTA2MTAwODU1MjNaGA8yMDI1MDYxMTA4NTUyM1owdzA9
# BgorBgEEAYRZCgQBMS8wLTAKAgUA6/JxewIBADAKAgEAAgIOAwIB/zAHAgEAAgIU
# eTAKAgUA6/PC+wIBADA2BgorBgEEAYRZCgQCMSgwJjAMBgorBgEEAYRZCgMCoAow
# CAIBAAIDB6EgoQowCAIBAAIDAYagMA0GCSqGSIb3DQEBCwUAA4IBAQAYtduBuopn
# aAM1opo/1MJdGcryYsNf+hZSA2Jd/Akf1X8+uZv/fkDdd+PH3cS7iTSY550XgdjP
# A8PuEc8rwkgQTJIglENfhEgleHGk7If9il/7KfS07k8ihaTSgWPRIc5c+6hEbFjF
# tkKT925rEGDtgT0Gy2YcWrj+yJt4DEBphOFzx40UJ+sKtHwfwPq5yRVXBnqS/XZJ
# pWRb3eqyrp+3lucTe9qT0vUEEwUrkWMm1K64J8TC5z8KwtgC0KQFGrhU0cShS1FD
# XCEaVU9J8gidRq0blF6SWd8G6RYg8KVpwPpLA935avEm2bMynQYYoQmPlJTWnFxn
# 07nx1QepPRQuMYIEDTCCBAkCAQEwgZMwfDELMAkGA1UEBhMCVVMxEzARBgNVBAgT
# Cldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29m
# dCBDb3Jwb3JhdGlvbjEmMCQGA1UEAxMdTWljcm9zb2Z0IFRpbWUtU3RhbXAgUENB
# IDIwMTACEzMAAAH7y8tsN2flMJUAAQAAAfswDQYJYIZIAWUDBAIBBQCgggFKMBoG
# CSqGSIb3DQEJAzENBgsqhkiG9w0BCRABBDAvBgkqhkiG9w0BCQQxIgQg1E7nVSIK
# 5ehj415kqp+Oqp6dWPmQvhfRwDvbBvIOZ4AwgfoGCyqGSIb3DQEJEAIvMYHqMIHn
# MIHkMIG9BCA52wKr/KCFlVNYiWsCLsB4qhjEYEP3xHqYqDu1SSTlGDCBmDCBgKR+
# MHwxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdS
# ZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xJjAkBgNVBAMT
# HU1pY3Jvc29mdCBUaW1lLVN0YW1wIFBDQSAyMDEwAhMzAAAB+8vLbDdn5TCVAAEA
# AAH7MCIEIApGs4/3/KDq7Lg6ZdjOISAbSvfnliJTmkbvxVUA80skMA0GCSqGSIb3
# DQEBCwUABIICAAm25X1FgoQ4Irn8KgcN0PN6qJ7t6oLX9JroNTnjlSvmQ8GXeTkA
# yAYng+I9oaIkS7PaPZok7mK3k2y5q1NKEbhS3HiBmiKf1TkwaYsnX+Xr1wF31rbh
# K+QFgP6Cy+8PShWlvd6pw95Zzhj5cR7/ltZ0oam11E9+nsfUx6MEH4zSGXXJ4aGj
# EsS0fqdfukdfyWpLL0g2b8WOqUzHjv+ufuO4FWGLBF/NnQvvnrWrB9VmDxDFYoH+
# KXVAPGxuWInFflje8k07o8u866+7rTm27DwRktrVKppg3nW3RX4uSjwdmgtqE/9Z
# /kmIoPJSjpHMy0AARbgSqP6fzUuBDWKe9ZJth7y4sfiRu4Spr33mq8q1uJEUtLOW
# k7jjsAcGKTlYGYWdYi0UUyMGA5TymcMi0xeasxspPQCX7n1tGQBzk1gzfRrwnByM
# tlgp1+XEVLeiTaNRjkDuSdmqtpP3wdFuV8eDz9za3VrdIUvcOw9hv59jeZ2x+50v
# MSnEK9XtebIKHz6zYVuXsGhkfBfrRxFEohJn5i2KYM5gsxSI46kkxQak+howPTDV
# BeGXf44c+1FkTEUXfvNMfjlS16k9nMiicmdfX2phA5fhzSOviP0OmkVxovQfaemI
# +O0vmSeOa3rU2J8ZrTEhOfIA/zRNvR8MHJM2TYuTa5RLpVNCY3p2zjxf
# SIG # End signature block