Classes/GraphSession.ps1

[Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSAvoidUsingConvertToSecureStringWithPlainText', '')]
class GraphSession {
    static [string]   $GraphEndpoint   = 'https://graph.microsoft.com/v1.0'
    static [string]   $PublicClientId  = '14d82eec-204b-4c2f-b7e8-296a70dab67e'
    static [int]      $MaxRetries      = 5
    static [int]      $MaxRetrySeconds = 600
    static [string[]] $RequiredScopes  = @(
        'Policy.Read.All','Policy.Read.AuthenticationMethod','Application.Read.All',
        'GroupMember.Read.All','RoleManagement.Read.Directory','AuditLog.Read.All',
        'User.Read.All','IdentityRiskyUser.Read.All','IdentityRiskEvent.Read.All'
    )

    hidden [string]       $_tenantId
    hidden [securestring] $_token
    hidden [datetime]     $_tokenExpiry       = [datetime]::MinValue
    hidden [int]          $_totalResourceUnits = 0
    # Plaintext cache (30s TTL)
    hidden [string]       $_tokenPlainCache
    hidden [datetime]     $_tokenCacheExpiry  = [datetime]::MinValue
    # Response cache
    hidden [hashtable]    $_responseCache     = @{}

    GraphSession([string]$tenantId) {
        if ($tenantId -notmatch '^[a-zA-Z0-9][a-zA-Z0-9._-]*\.[a-zA-Z]{2,}$' -and
            $tenantId -notmatch '^[0-9a-fA-F]{8}-([0-9a-fA-F]{4}-){3}[0-9a-fA-F]{12}$') {
            throw [System.ArgumentException]::new(
                "TenantId must be a valid domain (contoso.onmicrosoft.com) or GUID.", 'tenantId')
        }
        $this._tenantId = $tenantId
    }

    [void] Authenticate() {
        $transcriptSuspended = $false
        try { $null = Stop-Transcript -ErrorAction Stop; $transcriptSuspended = $true } catch { }
        try { $this._AuthenticateCore() }
        finally {
            if ($transcriptSuspended) {
                Write-Warning 'Transcript was paused during authentication to protect the device code. Run Start-Transcript to resume.'
            }
        }
    }

    hidden [void] _AuthenticateCore() {
        $body = @{
            client_id = [GraphSession]::PublicClientId
            scope     = ([GraphSession]::RequiredScopes + @('offline_access')) -join ' '
        }
        $dcResponse = Invoke-RestMethod -Method Post `
            -Uri "https://login.microsoftonline.com/$($this._tenantId)/oauth2/v2.0/devicecode" `
            -Body $body -ContentType 'application/x-www-form-urlencoded' -ErrorAction Stop

        # OS detection via .NET: automatic vars ($IsMacOS/$IsLinux) are not
        # accessible inside class methods, so resolve to local variables here.
        $isMac = [System.Runtime.InteropServices.RuntimeInformation]::IsOSPlatform([System.Runtime.InteropServices.OSPlatform]::OSX)
        $isLin = [System.Runtime.InteropServices.RuntimeInformation]::IsOSPlatform([System.Runtime.InteropServices.OSPlatform]::Linux)

        $clipboardOk = $false
        try {
            if ($isMac) {
                $dcResponse.user_code | & '/usr/bin/pbcopy'; $clipboardOk = $?
            } elseif ($isLin) {
                if (Get-Command xclip -ErrorAction SilentlyContinue) {
                    $dcResponse.user_code | & xclip -selection clipboard; $clipboardOk = $?
                } elseif (Get-Command xsel -ErrorAction SilentlyContinue) {
                    $dcResponse.user_code | & xsel --clipboard --input; $clipboardOk = $?
                }
            } else {
                Set-Clipboard -Value $dcResponse.user_code; $clipboardOk = $true
            }
        } catch { $clipboardOk = $false }

        if ($clipboardOk) {
            Write-Host "`n Code $($dcResponse.user_code) copied to clipboard." -ForegroundColor Cyan
        } else {
            Write-Host "`n Enter this code to sign in: $($dcResponse.user_code)" -ForegroundColor Cyan
        }
        Write-Host " Verify the URL is: $($dcResponse.verification_uri)" -ForegroundColor Yellow
        Write-Host " (Storm-2372 advisory: confirm the hostname before authenticating)" -ForegroundColor DarkGray
        Write-Host " Press Ctrl+C to cancel.`n" -ForegroundColor DarkGray
        try {
            if ($isMac) {
                & '/usr/bin/open' $dcResponse.verification_uri
            } elseif ($isLin) {
                & 'xdg-open' $dcResponse.verification_uri
            } else {
                Start-Process $dcResponse.verification_uri
            }
        } catch {
            Write-Host " Open this URL manually: $($dcResponse.verification_uri)" -ForegroundColor White
        }

        $interval = [math]::Max([int]$dcResponse.interval, 5)
        $maxWait  = [math]::Min($dcResponse.expires_in, 300)
        $deadline = [datetime]::UtcNow.AddSeconds($maxWait)
        $pollBody = @{
            grant_type  = 'urn:ietf:params:oauth:grant-type:device_code'
            client_id   = [GraphSession]::PublicClientId
            device_code = $dcResponse.device_code
        }
        $start = [datetime]::UtcNow

        while ([datetime]::UtcNow -lt $deadline) {
            $elapsed = [math]::Round(([datetime]::UtcNow - $start).TotalSeconds)
            $pct = [math]::Min(99, [math]::Round(($elapsed / $maxWait) * 100))
            Write-Progress -Activity 'Waiting for authentication' -PercentComplete $pct
            Start-Sleep -Seconds $interval
            try {
                $tr = Invoke-RestMethod -Method Post `
                    -Uri "https://login.microsoftonline.com/$($this._tenantId)/oauth2/v2.0/token" `
                    -Body $pollBody -ContentType 'application/x-www-form-urlencoded' -ErrorAction Stop

                $this._token       = ConvertTo-SecureString -String $tr.access_token -AsPlainText -Force
                $this._tokenExpiry = [datetime]::UtcNow.AddSeconds($tr.expires_in - 120)

                foreach ($prop in @('access_token','refresh_token','id_token')) {
                    if ($tr.PSObject.Properties[$prop]) { $tr.$prop = $null }
                }
                Remove-Variable tr -ErrorAction SilentlyContinue

                Write-Progress -Activity 'Waiting for authentication' -Completed
                Write-Host ' Authenticated.' -ForegroundColor Green
                return
            }
            catch {
                $errMsg = if ($_.ErrorDetails -and $_.ErrorDetails.Message) { $_.ErrorDetails.Message } else { $null }
                $err = $null
                if ($errMsg) { try { $err = $errMsg | ConvertFrom-Json } catch { $err = $null } }
                if ($null -ne $err -and $err.error -eq 'authorization_pending') { continue }
                if ($null -ne $err -and $err.error -eq 'slow_down') { $interval += 5; continue }
                if ($null -ne $err -and $err.error -eq 'authorization_declined') {
                    Write-Progress -Activity 'Waiting for authentication' -Completed
                    throw 'Authentication was declined by the user.'
                }
                if ($null -ne $err -and $err.error -eq 'expired_token') {
                    Write-Progress -Activity 'Waiting for authentication' -Completed
                    throw 'Device code expired before authentication was completed.'
                }
                throw $_
            }
        }
        Write-Progress -Activity 'Waiting for authentication' -Completed
        throw 'Authentication timed out. The device code was not completed in time.'
    }

    hidden [string] MarshalToken() {
        if ([datetime]::UtcNow -ge $this._tokenExpiry) {
            throw [System.InvalidOperationException]::new('Access token expired. Run Connect-NsSession to re-authenticate.')
        }
        if ($this._tokenPlainCache -and [datetime]::UtcNow -lt $this._tokenCacheExpiry) { return $this._tokenPlainCache }
        $bstr = [System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($this._token)
        try {
            $this._tokenPlainCache  = [System.Runtime.InteropServices.Marshal]::PtrToStringBSTR($bstr)
            $this._tokenCacheExpiry = [datetime]::UtcNow.AddSeconds(30)
            return $this._tokenPlainCache
        }
        finally { [System.Runtime.InteropServices.Marshal]::ZeroFreeBSTR($bstr) }
    }

    hidden [hashtable] BuildHeaders() {
        return @{
            Authorization       = "Bearer $($this.MarshalToken())"
            ConsistencyLevel    = 'eventual'
            'client-request-id' = [guid]::NewGuid().ToString()
        }
    }

    hidden [object] InvokeWithRetry([string]$method, [string]$uri, [object]$body) {
        $attempt = 0; $totalWait = 0
        while ($true) {
            $attempt++
            $headers = $this.BuildHeaders()
            try {
                $params = @{ Method = $method; Uri = $uri; Headers = $headers; ErrorAction = 'Stop' }
                if ($body) {
                    $params['Body']        = ($body | ConvertTo-Json -Depth 10 -Compress)
                    $params['ContentType'] = 'application/json'
                }
                $respHeaders = $null
                $response = Invoke-RestMethod @params -ResponseHeadersVariable 'respHeaders'
                if ($respHeaders -and $respHeaders.ContainsKey('x-ms-resource-unit')) {
                    $ru = [int]$respHeaders['x-ms-resource-unit'][0]
                    $this._totalResourceUnits += $ru
                }
                Write-Debug "Graph $method $uri OK [request-id=$($headers['client-request-id'])]"
                return $response
            }
            catch {
                $status = 0
                if ($_.Exception.Response) { $status = [int]$_.Exception.Response.StatusCode }
                $retryable = $status -in @(429, 503, 504)
                if (-not $retryable -or $attempt -gt [GraphSession]::MaxRetries) { throw }

                $waitSec = [math]::Pow(2, $attempt)
                if ($_.Exception.Response -and $_.Exception.Response.Headers) {
                    try {
                        $raValues = $_.Exception.Response.Headers.GetValues('Retry-After')
                        if ($raValues -and $raValues.Count -gt 0) { $waitSec = [math]::Max($waitSec, [int]$raValues[0]) }
                    } catch { }
                }
                $jitter  = Get-Random -Minimum 100 -Maximum 800
                $waitMs  = ([int]($waitSec * 1000)) + $jitter
                $totalWait += ($waitMs / 1000)
                if ($totalWait -gt [GraphSession]::MaxRetrySeconds) { throw }
                Write-Verbose "Graph throttled ($status). Retry $attempt/$([GraphSession]::MaxRetries) in $([math]::Round($waitMs/1000,1))s"
                Start-Sleep -Milliseconds $waitMs
            }
        }
        return $null
    }

    # Cached GET
    [object] Get([string]$path) {
        $uri = if ($path.StartsWith('http')) { $path } else { "$([GraphSession]::GraphEndpoint)$path" }
        if ($this._responseCache.ContainsKey($uri)) {
            Write-Debug "Graph cache HIT: $uri"
            return $this._responseCache[$uri]
        }
        $result = $this.InvokeWithRetry('GET', $uri, $null)
        $this._responseCache[$uri] = $result
        return $result
    }

    # Uncached GET
    [object] GetFresh([string]$path) {
        $uri = if ($path.StartsWith('http')) { $path } else { "$([GraphSession]::GraphEndpoint)$path" }
        return $this.InvokeWithRetry('GET', $uri, $null)
    }

    [object[]] GetAll([string]$path) {
        $results = [System.Collections.ArrayList]::new()
        $uri = if ($path.StartsWith('http')) { $path } else { "$([GraphSession]::GraphEndpoint)$path" }
        while ($uri) {
            $response = $this.InvokeWithRetry('GET', $uri, $null)
            if ($response.value) { $results.AddRange([object[]]$response.value) }
            $uri = $response.'@odata.nextLink'
        }
        return $results.ToArray()
    }

    [void] GetAllPaged([string]$path, [scriptblock]$pageHandler) {
        $uri = if ($path.StartsWith('http')) { $path } else { "$([GraphSession]::GraphEndpoint)$path" }
        while ($uri) {
            $response = $this.InvokeWithRetry('GET', $uri, $null)
            if ($response.value) { & $pageHandler $response.value }
            $uri = $response.'@odata.nextLink'
        }
    }

    [object[]] BatchResolve([string[]]$ids) {
        if ($ids.Count -eq 0) { return @() }
        $resolved = [System.Collections.ArrayList]::new()
        for ($i = 0; $i -lt $ids.Count; $i += 1000) {
            $end   = [math]::Min($i + 1000, $ids.Count)
            $chunk = $ids[$i..($end - 1)]
            $body  = @{ ids = $chunk; types = @('user','group','servicePrincipal','application','device') }
            $response = $this.InvokeWithRetry('POST', "$([GraphSession]::GraphEndpoint)/directoryObjects/getByIds", $body)
            if ($response.value) { $resolved.AddRange([object[]]$response.value) }
        }
        return $resolved.ToArray()
    }

    [void] ClearCache() { $this._responseCache.Clear() }

    [string[]] GetGrantedScopes() {
        $plain = $this.MarshalToken()
        $parts = $plain.Split('.')
        if ($parts.Count -lt 2) { return @() }
        $payload = $parts[1]
        # Fix base64url padding
        switch ($payload.Length % 4) { 2 { $payload += '==' } 3 { $payload += '=' } }
        $payload = $payload.Replace('-','+').Replace('_','/')
        try {
            $json = [System.Text.Encoding]::UTF8.GetString([System.Convert]::FromBase64String($payload)) | ConvertFrom-Json
            if ($json.scp) { return @($json.scp -split '\s+') }
            return @()
        } catch { return @() }
    }

    [void] Dispose() {
        $this._tokenPlainCache  = $null
        $this._tokenCacheExpiry = [datetime]::MinValue
        $this._responseCache.Clear()
        if ($this._token) { $this._token.Dispose(); $this._token = $null }
    }
}

# SIG # Begin signature block
# MII9FgYJKoZIhvcNAQcCoII9BzCCPQMCAQExDzANBglghkgBZQMEAgEFADB5Bgor
# BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG
# KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCA1gu4xMkggwy73
# 3H6l9NlGsD1pC625zx8p0+xK2aTdZ6CCIdwwggXMMIIDtKADAgECAhBUmNLR1FsZ
# lUgTecgRwIeZMA0GCSqGSIb3DQEBDAUAMHcxCzAJBgNVBAYTAlVTMR4wHAYDVQQK
# ExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xSDBGBgNVBAMTP01pY3Jvc29mdCBJZGVu
# dGl0eSBWZXJpZmljYXRpb24gUm9vdCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkgMjAy
# MDAeFw0yMDA0MTYxODM2MTZaFw00NTA0MTYxODQ0NDBaMHcxCzAJBgNVBAYTAlVT
# MR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xSDBGBgNVBAMTP01pY3Jv
# c29mdCBJZGVudGl0eSBWZXJpZmljYXRpb24gUm9vdCBDZXJ0aWZpY2F0ZSBBdXRo
# b3JpdHkgMjAyMDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBALORKgeD
# Bmf9np3gx8C3pOZCBH8Ppttf+9Va10Wg+3cL8IDzpm1aTXlT2KCGhFdFIMeiVPvH
# or+Kx24186IVxC9O40qFlkkN/76Z2BT2vCcH7kKbK/ULkgbk/WkTZaiRcvKYhOuD
# PQ7k13ESSCHLDe32R0m3m/nJxxe2hE//uKya13NnSYXjhr03QNAlhtTetcJtYmrV
# qXi8LW9J+eVsFBT9FMfTZRY33stuvF4pjf1imxUs1gXmuYkyM6Nix9fWUmcIxC70
# ViueC4fM7Ke0pqrrBc0ZV6U6CwQnHJFnni1iLS8evtrAIMsEGcoz+4m+mOJyoHI1
# vnnhnINv5G0Xb5DzPQCGdTiO0OBJmrvb0/gwytVXiGhNctO/bX9x2P29Da6SZEi3
# W295JrXNm5UhhNHvDzI9e1eM80UHTHzgXhgONXaLbZ7LNnSrBfjgc10yVpRnlyUK
# xjU9lJfnwUSLgP3B+PR0GeUw9gb7IVc+BhyLaxWGJ0l7gpPKWeh1R+g/OPTHU3mg
# trTiXFHvvV84wRPmeAyVWi7FQFkozA8kwOy6CXcjmTimthzax7ogttc32H83rwjj
# O3HbbnMbfZlysOSGM1l0tRYAe1BtxoYT2v3EOYI9JACaYNq6lMAFUSw0rFCZE4e7
# swWAsk0wAly4JoNdtGNz764jlU9gKL431VulAgMBAAGjVDBSMA4GA1UdDwEB/wQE
# AwIBhjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBTIftJqhSobyhmYBAcnz1AQ
# T2ioojAQBgkrBgEEAYI3FQEEAwIBADANBgkqhkiG9w0BAQwFAAOCAgEAr2rd5hnn
# LZRDGU7L6VCVZKUDkQKL4jaAOxWiUsIWGbZqWl10QzD0m/9gdAmxIR6QFm3FJI9c
# Zohj9E/MffISTEAQiwGf2qnIrvKVG8+dBetJPnSgaFvlVixlHIJ+U9pW2UYXeZJF
# xBA2CFIpF8svpvJ+1Gkkih6PsHMNzBxKq7Kq7aeRYwFkIqgyuH4yKLNncy2RtNwx
# AQv3Rwqm8ddK7VZgxCwIo3tAsLx0J1KH1r6I3TeKiW5niB31yV2g/rarOoDXGpc8
# FzYiQR6sTdWD5jw4vU8w6VSp07YEwzJ2YbuwGMUrGLPAgNW3lbBeUU0i/OxYqujY
# lLSlLu2S3ucYfCFX3VVj979tzR/SpncocMfiWzpbCNJbTsgAlrPhgzavhgplXHT2
# 6ux6anSg8Evu75SjrFDyh+3XOjCDyft9V77l4/hByuVkrrOj7FjshZrM77nq81YY
# uVxzmq/FdxeDWds3GhhyVKVB0rYjdaNDmuV3fJZ5t0GNv+zcgKCf0Xd1WF81E+Al
# GmcLfc4l+gcK5GEh2NQc5QfGNpn0ltDGFf5Ozdeui53bFv0ExpK91IjmqaOqu/dk
# ODtfzAzQNb50GQOmxapMomE2gj4d8yu8l13bS3g7LfU772Aj6PXsCyM2la+YZr9T
# 03u4aUoqlmZpxJTG9F9urJh4iIAGXKKy7aIwggabMIIEg6ADAgECAhMzAAGSX9Z/
# ePB9sQgIAAAAAZJfMA0GCSqGSIb3DQEBDAUAMFoxCzAJBgNVBAYTAlVTMR4wHAYD
# VQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xKzApBgNVBAMTIk1pY3Jvc29mdCBJ
# RCBWZXJpZmllZCBDUyBFT0MgQ0EgMDQwHhcNMjYwNjAxMTc1MjU4WhcNMjYwNjA0
# MTc1MjU4WjBfMQswCQYDVQQGEwJDQTEQMA4GA1UECBMHQWxiZXJ0YTEQMA4GA1UE
# BxMHQ2FsZ2FyeTEVMBMGA1UEChMMRGFycmVuIE1heWVzMRUwEwYDVQQDEwxEYXJy
# ZW4gTWF5ZXMwggGiMA0GCSqGSIb3DQEBAQUAA4IBjwAwggGKAoIBgQC6vpd+d66I
# l6cpo6XDaTIvujkV3rekVoeig10+dG/OTYmX57PD3Rum9xsPx2BdX8lzvGnQq7Pa
# v6KacGC8N9f5XmVTJhc8k341QPXbSVsj61bYPVcV3ZnEr5zX2ciTGqXOgN/woD1A
# QccrGB8mCfWn9sNyVRTzNtoJz3/BrZl2Au+4nK2clIPX1a1k/6r2wTeFiBFAh7fp
# GZUHRNl0rcAzogwi5qYykxCDz7Wzu73tuUfhxcbn2XzoqfqKd83VSIN+osxnOmrS
# 0vMT5gnJC4B8RAGoGIqOle4wexQOn1Zrx8B2l1ZaIyLWf8bhcugcvTDT6o/oC1VV
# CVQqymqGTAhyIfIMSV31wMQDmEyXpa9SQFV3A4rUL+ILNDTGcca8GFZ6a1kqRe83
# xM/LTqupi6OSQ8ruxEHzJsW3No7J4rsNRljjSf7MGXFrKQjPBVFnjN2tHOPShdYB
# Z60NS6q6pVj+xq5FBVGHzV8XtKdV0e3jigsEyXkEl0jcQ8/K2LIxphMCAwEAAaOC
# AdMwggHPMAwGA1UdEwEB/wQCMAAwDgYDVR0PAQH/BAQDAgeAMDoGA1UdJQQzMDEG
# CisGAQQBgjdhAQAGCCsGAQUFBwMDBhkrBgEEAYI3YYuPpXGB3qaBQNv/tAST5rkV
# MB0GA1UdDgQWBBSvmzOJzfqlOfze0TuSX3fnqAlzZDAfBgNVHSMEGDAWgBSa8VR3
# dQyHFjdGoKzeefn0f8F46TBnBgNVHR8EYDBeMFygWqBYhlZodHRwOi8vd3d3Lm1p
# Y3Jvc29mdC5jb20vcGtpb3BzL2NybC9NaWNyb3NvZnQlMjBJRCUyMFZlcmlmaWVk
# JTIwQ1MlMjBFT0MlMjBDQSUyMDA0LmNybDB0BggrBgEFBQcBAQRoMGYwZAYIKwYB
# BQUHMAKGWGh0dHA6Ly93d3cubWljcm9zb2Z0LmNvbS9wa2lvcHMvY2VydHMvTWlj
# cm9zb2Z0JTIwSUQlMjBWZXJpZmllZCUyMENTJTIwRU9DJTIwQ0ElMjAwNC5jcnQw
# VAYDVR0gBE0wSzBJBgRVHSAAMEEwPwYIKwYBBQUHAgEWM2h0dHA6Ly93d3cubWlj
# cm9zb2Z0LmNvbS9wa2lvcHMvRG9jcy9SZXBvc2l0b3J5Lmh0bTANBgkqhkiG9w0B
# AQwFAAOCAgEABRiO07jmU6SjeUNkagy4Me5BFFY3b/5L1BRn/Gf9C8kXZjlIyzMH
# 7FcbslPw6ySkN3AIikz4rDwk8QQ+fc/dp+/ru35kqbbXm/bcK0AVjQ3ZOy8kRAjG
# qmFJff56lydGk3ybw1KCBHYgSs9KXVxC45348w4T9pjSViDcGko8g4Z+m19GW7ov
# 6srqiP72u5zVIwIHuxP/g08zaf2wynKz9duWzmZS8LftUawJI8il/5pLFjNBlCeJ
# q4MWNfI4N4d1a2VCEhs12HLqax/C/PyQP3qRUI7PB5IfKYltajewMbYVw+rQDE4f
# ugNEu14zUYPislGt36hd/Tk1Dn8uIT6P+9RyMyIX19bH9uTghE132/HEwuVNL34R
# kxJYMIG+ESF6fs+G9RDkWyGAi1FlLWl6ptszZ8ThL22/Sc11b6FlR8s+pCnjSQ6T
# KWnmWGEwoG43uqFXfLcchz2m8AEE3xvj+XpwkujUSrayTGy5E/olv946WtyEq4po
# NlZsND6CbrWT6VNd4VvmrRbYZUThdX3wjrpBssSRY3a7T9ra9KyYFFeAFulbvdaq
# o1DgPBVAWgbskiHEdkSrySbbhTHmCWltbGzlPU3kqTwSrjw0CUOfry9Ve7iYtitt
# 6LVu6DqEOY/3TBfLnubeZyjd1LpkIQWPU4u9AA4d9L2SUKY5Ziyo+18wggabMIIE
# g6ADAgECAhMzAAGSX9Z/ePB9sQgIAAAAAZJfMA0GCSqGSIb3DQEBDAUAMFoxCzAJ
# BgNVBAYTAlVTMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xKzApBgNV
# BAMTIk1pY3Jvc29mdCBJRCBWZXJpZmllZCBDUyBFT0MgQ0EgMDQwHhcNMjYwNjAx
# MTc1MjU4WhcNMjYwNjA0MTc1MjU4WjBfMQswCQYDVQQGEwJDQTEQMA4GA1UECBMH
# QWxiZXJ0YTEQMA4GA1UEBxMHQ2FsZ2FyeTEVMBMGA1UEChMMRGFycmVuIE1heWVz
# MRUwEwYDVQQDEwxEYXJyZW4gTWF5ZXMwggGiMA0GCSqGSIb3DQEBAQUAA4IBjwAw
# ggGKAoIBgQC6vpd+d66Il6cpo6XDaTIvujkV3rekVoeig10+dG/OTYmX57PD3Rum
# 9xsPx2BdX8lzvGnQq7Pav6KacGC8N9f5XmVTJhc8k341QPXbSVsj61bYPVcV3ZnE
# r5zX2ciTGqXOgN/woD1AQccrGB8mCfWn9sNyVRTzNtoJz3/BrZl2Au+4nK2clIPX
# 1a1k/6r2wTeFiBFAh7fpGZUHRNl0rcAzogwi5qYykxCDz7Wzu73tuUfhxcbn2Xzo
# qfqKd83VSIN+osxnOmrS0vMT5gnJC4B8RAGoGIqOle4wexQOn1Zrx8B2l1ZaIyLW
# f8bhcugcvTDT6o/oC1VVCVQqymqGTAhyIfIMSV31wMQDmEyXpa9SQFV3A4rUL+IL
# NDTGcca8GFZ6a1kqRe83xM/LTqupi6OSQ8ruxEHzJsW3No7J4rsNRljjSf7MGXFr
# KQjPBVFnjN2tHOPShdYBZ60NS6q6pVj+xq5FBVGHzV8XtKdV0e3jigsEyXkEl0jc
# Q8/K2LIxphMCAwEAAaOCAdMwggHPMAwGA1UdEwEB/wQCMAAwDgYDVR0PAQH/BAQD
# AgeAMDoGA1UdJQQzMDEGCisGAQQBgjdhAQAGCCsGAQUFBwMDBhkrBgEEAYI3YYuP
# pXGB3qaBQNv/tAST5rkVMB0GA1UdDgQWBBSvmzOJzfqlOfze0TuSX3fnqAlzZDAf
# BgNVHSMEGDAWgBSa8VR3dQyHFjdGoKzeefn0f8F46TBnBgNVHR8EYDBeMFygWqBY
# hlZodHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20vcGtpb3BzL2NybC9NaWNyb3NvZnQl
# MjBJRCUyMFZlcmlmaWVkJTIwQ1MlMjBFT0MlMjBDQSUyMDA0LmNybDB0BggrBgEF
# BQcBAQRoMGYwZAYIKwYBBQUHMAKGWGh0dHA6Ly93d3cubWljcm9zb2Z0LmNvbS9w
# a2lvcHMvY2VydHMvTWljcm9zb2Z0JTIwSUQlMjBWZXJpZmllZCUyMENTJTIwRU9D
# JTIwQ0ElMjAwNC5jcnQwVAYDVR0gBE0wSzBJBgRVHSAAMEEwPwYIKwYBBQUHAgEW
# M2h0dHA6Ly93d3cubWljcm9zb2Z0LmNvbS9wa2lvcHMvRG9jcy9SZXBvc2l0b3J5
# Lmh0bTANBgkqhkiG9w0BAQwFAAOCAgEABRiO07jmU6SjeUNkagy4Me5BFFY3b/5L
# 1BRn/Gf9C8kXZjlIyzMH7FcbslPw6ySkN3AIikz4rDwk8QQ+fc/dp+/ru35kqbbX
# m/bcK0AVjQ3ZOy8kRAjGqmFJff56lydGk3ybw1KCBHYgSs9KXVxC45348w4T9pjS
# ViDcGko8g4Z+m19GW7ov6srqiP72u5zVIwIHuxP/g08zaf2wynKz9duWzmZS8Lft
# UawJI8il/5pLFjNBlCeJq4MWNfI4N4d1a2VCEhs12HLqax/C/PyQP3qRUI7PB5If
# KYltajewMbYVw+rQDE4fugNEu14zUYPislGt36hd/Tk1Dn8uIT6P+9RyMyIX19bH
# 9uTghE132/HEwuVNL34RkxJYMIG+ESF6fs+G9RDkWyGAi1FlLWl6ptszZ8ThL22/
# Sc11b6FlR8s+pCnjSQ6TKWnmWGEwoG43uqFXfLcchz2m8AEE3xvj+XpwkujUSray
# TGy5E/olv946WtyEq4poNlZsND6CbrWT6VNd4VvmrRbYZUThdX3wjrpBssSRY3a7
# T9ra9KyYFFeAFulbvdaqo1DgPBVAWgbskiHEdkSrySbbhTHmCWltbGzlPU3kqTwS
# rjw0CUOfry9Ve7iYtitt6LVu6DqEOY/3TBfLnubeZyjd1LpkIQWPU4u9AA4d9L2S
# UKY5Ziyo+18wggcoMIIFEKADAgECAhMzAAAAFydFCQuLh6/GAAAAAAAXMA0GCSqG
# SIb3DQEBDAUAMGMxCzAJBgNVBAYTAlVTMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29y
# cG9yYXRpb24xNDAyBgNVBAMTK01pY3Jvc29mdCBJRCBWZXJpZmllZCBDb2RlIFNp
# Z25pbmcgUENBIDIwMjEwHhcNMjYwMzI2MTgxMTMxWhcNMzEwMzI2MTgxMTMxWjBa
# MQswCQYDVQQGEwJVUzEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSsw
# KQYDVQQDEyJNaWNyb3NvZnQgSUQgVmVyaWZpZWQgQ1MgRU9DIENBIDA0MIICIjAN
# BgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAgsdk/gMPZioBlcyfk6tDzJ+PRt4r
# SLGKW8ewpS0kRxXtURC3T3GdbCKljobEn8ussqhGqQpRh/SXvRVwNXEIGb76UG5I
# PkCJ1S6/9BD61QQsKzPepW0SNj8TXgsFxvS7MltoRuikIIp7Q5jQgaOM6QyK9++6
# ZVXUpYmZulAe6x8JrwZ0dNkE+rZ66lqtoocwepUSVUxM7odDmn8yDHjJ2DNPsfr3
# uRDix3X4qvh14jH/SW+2Cx7WIMhyIiQO201i6hUixmk4e2ZW8W7C1wPdTjq6BKb+
# zo8xbrt7ZKQvRX5QOA6dhLquPqj5sVKnxqfk19IC0SafTSTs8yC43Ew965BRRW8V
# L9ccoOmr4rxQy7aCgYTNk3dd/LphNaTTmnGp7kmLTxyHkB5geoWhYuuGrywS8E0w
# Jv0W4rfOtHBV0e9sKvuUIeIUpnsx6ilxEVj6VQXvgD6yeCKnPmj3jJiJKAlmUDtt
# h5yzRVBUl44sMiG4L5R/yyACRKk2n088Q2YCoZS1O86+oMLKt1jaXGECOjbsVp8I
# d1VQw8he6J0KirOS5e25XlTdGPFb6oBOOaacgW78Kjf0bp+XzAgkc92mDGNJGYSj
# vdnj+7eMx6meW0DAIGdLRNj8/429MIspFBfz3KDqqpN71S4kQ2LLer3dxhDDczKV
# FL0HLwRuOvgjiG8CAwEAAaOCAdwwggHYMA4GA1UdDwEB/wQEAwIBhjAQBgkrBgEE
# AYI3FQEEAwIBADAdBgNVHQ4EFgQUmvFUd3UMhxY3RqCs3nn59H/BeOkwVAYDVR0g
# BE0wSzBJBgRVHSAAMEEwPwYIKwYBBQUHAgEWM2h0dHA6Ly93d3cubWljcm9zb2Z0
# LmNvbS9wa2lvcHMvRG9jcy9SZXBvc2l0b3J5Lmh0bTAZBgkrBgEEAYI3FAIEDB4K
# AFMAdQBiAEMAQTASBgNVHRMBAf8ECDAGAQH/AgEAMB8GA1UdIwQYMBaAFNlBKbAP
# D2Ns72nX9c0pnqRIajDmMHAGA1UdHwRpMGcwZaBjoGGGX2h0dHA6Ly93d3cubWlj
# cm9zb2Z0LmNvbS9wa2lvcHMvY3JsL01pY3Jvc29mdCUyMElEJTIwVmVyaWZpZWQl
# MjBDb2RlJTIwU2lnbmluZyUyMFBDQSUyMDIwMjEuY3JsMH0GCCsGAQUFBwEBBHEw
# bzBtBggrBgEFBQcwAoZhaHR0cDovL3d3dy5taWNyb3NvZnQuY29tL3BraW9wcy9j
# ZXJ0cy9NaWNyb3NvZnQlMjBJRCUyMFZlcmlmaWVkJTIwQ29kZSUyMFNpZ25pbmcl
# MjBQQ0ElMjAyMDIxLmNydDANBgkqhkiG9w0BAQwFAAOCAgEAkHVaGf1NJt/Jdoim
# mRZbMWr6baaDi8mkdWvWStk0hdZDpxSYTA7HuipAoLL3qIhI101XOl7fOiCh5++j
# ZOamQdAV79ojEUNoIgCZmL2XJrLaGanwdjNynecJyYVCTrRf2+h7KknpWOp4axdO
# s6K9ZQ5g0IsQWXCwfc0dfkSkLKNY3pDcWLlJPh2jd5NUue6pNDv/2G5MFNJhCwlt
# ODebyAjGceU+XOzav+7i721YQnQ+39m2aQOFO7zpAdaKAeAGhEd6Y6CdDGneSxco
# ujWvafWbv4ay3jo1ORSLUuWMbKr5X18QE4Sde+gppGLLSkZsrUh2eyYSkX1envWX
# 7ZPzg2/wiuKRlQFarDn+N9+20BqzhxwkNyLzfYJp1Lg4fCXb24XqFjx8SDdRgebF
# ImOfOLVze8XQ/CwkrEaib0PHu2t4GVk4FYroEbNUFqvjdBvTY3uiR5TdQoyXoYHv
# h+TxpLSY2vo7hhK9D/rpEpHC+qmmcRUE4d0gyO9Zb1vvt25fxM3ekjvDfVHcPq3q
# Mr0Rwsk4krKZWUEgU1SXT5qN6gqRrshxbT6OQgZ9/xT04qiXdzPQR6KindBvSpoO
# nxnALxcJyzVwNpKL+9u8EZYy98qX6i+4gE/2J6cbpekcB0ZXDn/XQxoNUUb6/djT
# /wllVyG+vIHkdq71PzbH5rYxdcAwggeeMIIFhqADAgECAhMzAAAAB4ejNKN7pY4c
# AAAAAAAHMA0GCSqGSIb3DQEBDAUAMHcxCzAJBgNVBAYTAlVTMR4wHAYDVQQKExVN
# aWNyb3NvZnQgQ29ycG9yYXRpb24xSDBGBgNVBAMTP01pY3Jvc29mdCBJZGVudGl0
# eSBWZXJpZmljYXRpb24gUm9vdCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkgMjAyMDAe
# Fw0yMTA0MDEyMDA1MjBaFw0zNjA0MDEyMDE1MjBaMGMxCzAJBgNVBAYTAlVTMR4w
# HAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xNDAyBgNVBAMTK01pY3Jvc29m
# dCBJRCBWZXJpZmllZCBDb2RlIFNpZ25pbmcgUENBIDIwMjEwggIiMA0GCSqGSIb3
# DQEBAQUAA4ICDwAwggIKAoICAQCy8MCvGYgo4t1UekxJbGkIVQm0Uv96SvjB6yUo
# 92cXdylN65Xy96q2YpWCiTas7QPTkGnK9QMKDXB2ygS27EAIQZyAd+M8X+dmw6SD
# tzSZXyGkxP8a8Hi6EO9Zcwh5A+wOALNQbNO+iLvpgOnEM7GGB/wm5dYnMEOguua1
# OFfTUITVMIK8faxkP/4fPdEPCXYyy8NJ1fmskNhW5HduNqPZB/NkWbB9xxMqowAe
# WvPgHtpzyD3PLGVOmRO4ka0WcsEZqyg6efk3JiV/TEX39uNVGjgbODZhzspHvKFN
# U2K5MYfmHh4H1qObU4JKEjKGsqqA6RziybPqhvE74fEp4n1tiY9/ootdU0vPxRp4
# BGjQFq28nzawuvaCqUUF2PWxh+o5/TRCb/cHhcYU8Mr8fTiS15kRmwFFzdVPZ3+J
# V3s5MulIf3II5FXeghlAH9CvicPhhP+VaSFW3Da/azROdEm5sv+EUwhBrzqtxoYy
# E2wmuHKws00x4GGIx7NTWznOm6x/niqVi7a/mxnnMvQq8EMse0vwX2CfqM7Le/sm
# bRtsEeOtbnJBbtLfoAsC3TdAOnBbUkbUfG78VRclsE7YDDBUbgWt75lDk53yi7C3
# n0WkHFU4EZ83i83abd9nHWCqfnYa9qIHPqjOiuAgSOf4+FRcguEBXlD9mAInS7b6
# V0UaNwIDAQABo4ICNTCCAjEwDgYDVR0PAQH/BAQDAgGGMBAGCSsGAQQBgjcVAQQD
# AgEAMB0GA1UdDgQWBBTZQSmwDw9jbO9p1/XNKZ6kSGow5jBUBgNVHSAETTBLMEkG
# BFUdIAAwQTA/BggrBgEFBQcCARYzaHR0cDovL3d3dy5taWNyb3NvZnQuY29tL3Br
# aW9wcy9Eb2NzL1JlcG9zaXRvcnkuaHRtMBkGCSsGAQQBgjcUAgQMHgoAUwB1AGIA
# QwBBMA8GA1UdEwEB/wQFMAMBAf8wHwYDVR0jBBgwFoAUyH7SaoUqG8oZmAQHJ89Q
# EE9oqKIwgYQGA1UdHwR9MHsweaB3oHWGc2h0dHA6Ly93d3cubWljcm9zb2Z0LmNv
# bS9wa2lvcHMvY3JsL01pY3Jvc29mdCUyMElkZW50aXR5JTIwVmVyaWZpY2F0aW9u
# JTIwUm9vdCUyMENlcnRpZmljYXRlJTIwQXV0aG9yaXR5JTIwMjAyMC5jcmwwgcMG
# CCsGAQUFBwEBBIG2MIGzMIGBBggrBgEFBQcwAoZ1aHR0cDovL3d3dy5taWNyb3Nv
# ZnQuY29tL3BraW9wcy9jZXJ0cy9NaWNyb3NvZnQlMjBJZGVudGl0eSUyMFZlcmlm
# aWNhdGlvbiUyMFJvb3QlMjBDZXJ0aWZpY2F0ZSUyMEF1dGhvcml0eSUyMDIwMjAu
# Y3J0MC0GCCsGAQUFBzABhiFodHRwOi8vb25lb2NzcC5taWNyb3NvZnQuY29tL29j
# c3AwDQYJKoZIhvcNAQEMBQADggIBAH8lKp7+1Kvq3WYK21cjTLpebJDjW4ZbOX3H
# D5ZiG84vjsFXT0OB+eb+1TiJ55ns0BHluC6itMI2vnwc5wDW1ywdCq3TAmx0KWy7
# xulAP179qX6VSBNQkRXzReFyjvF2BGt6FvKFR/imR4CEESMAG8hSkPYso+GjlngM
# 8JPn/ROUrTaeU/BRu/1RFESFVgK2wMz7fU4VTd8NXwGZBe/mFPZG6tWwkdmA/jLb
# p0kNUX7elxu2+HtHo0QO5gdiKF+YTYd1BGrmNG8sTURvn09jAhIUJfYNotn7OlTh
# tfQjXqe0qrimgY4Vpoq2MgDW9ESUi1o4pzC1zTgIGtdJ/IvY6nqa80jFOTg5qzAi
# RNdsUvzVkoYP7bi4wLCj+ks2GftUct+fGUxXMdBUv5sdr0qFPLPB0b8vq516slCf
# RwaktAxK1S40MCvFbbAXXpAZnU20FaAoDwqq/jwzwd8Wo2J83r7O3onQbDO9TyDS
# tgaBNlHzMMQgl95nHBYMelLEHkUnVVVTUsgC0Huj09duNfMaJ9ogxhPNThgq3i8w
# 3DAGZ61AMeF0C1M+mU5eucj1Ijod5O2MMPeJQ3/vKBtqGZg4eTtUHt/BPjN74SsJ
# syHqAdXVS5c+ItyKWg3Eforhox9k3WgtWTpgV4gkSiS4+A09roSdOI4vrRw+p+fL
# 4WrxSK5nMYIakDCCGowCAQEwcTBaMQswCQYDVQQGEwJVUzEeMBwGA1UEChMVTWlj
# cm9zb2Z0IENvcnBvcmF0aW9uMSswKQYDVQQDEyJNaWNyb3NvZnQgSUQgVmVyaWZp
# ZWQgQ1MgRU9DIENBIDA0AhMzAAGSX9Z/ePB9sQgIAAAAAZJfMA0GCWCGSAFlAwQC
# AQUAoF4wEAYKKwYBBAGCNwIBDDECMAAwGQYJKoZIhvcNAQkDMQwGCisGAQQBgjcC
# AQQwLwYJKoZIhvcNAQkEMSIEINGauvKA7hInZn4O5YPvuCsPCritFQ0WbqEqdPyP
# r76FMA0GCSqGSIb3DQEBAQUABIIBgIeit0Qjj6vYvgTJUfdV+BGRjehEjoDcc84E
# Q8nwgEBGJHB+uBc20zrKR1JLh/3cr5veEJPxC8CdvTB7e9xVoG8GfxlUvSe62VN1
# BddjLdcnXrBz2S+SLRgkQw1mZReS69dUgkNPRgHMPuoGpAxGOD/KxlCDRR4D0j8/
# 9Gs/1D2GFacxqMgIpkAiuu1TzoatuY8VC1XkadKn/WxMVEYfgrN0yjunIeGiKYZr
# Lm9KNHgl7xvGtBHsLdJPqRaa3GbHNXwKb46r+5CICFmnqt0yjkN9spme8eTYoslZ
# 3rkUdqabXFu4NOhcte8Ms5kOoTaOawqR5sunalixIvqKNG0W6TbKsDZ2zzpba4j4
# UJ6OSQ2aOClQUX6pzAZKVyFO6A+Zl5l4l5wU8SYVEyJgV3dZphm3ee+5gJxDTjEV
# 75djNXFqGEoS9BRM0TeHRghNnm/ioKsU3BtqMcBzpqZ+9BqAucb/feE9rNlS+8aJ
# IYiMVII2ghpLHdfHEVimx0UxxlU6w6GCGBAwghgMBgorBgEEAYI3AwMBMYIX/DCC
# F/gGCSqGSIb3DQEHAqCCF+kwghflAgEDMQ8wDQYJYIZIAWUDBAIBBQAwggFhBgsq
# hkiG9w0BCRABBKCCAVAEggFMMIIBSAIBAQYKKwYBBAGEWQoDATAxMA0GCWCGSAFl
# AwQCAQUABCDmjwUlRXG2Ick/fDirtAu8fuy3ribl5qlzrHKixw2B2gIGagxE8Xu6
# GBIyMDI2MDYwMjAxNTU0NS42MlowBIACAfSggeGkgd4wgdsxCzAJBgNVBAYTAlVT
# MRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQK
# ExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xJTAjBgNVBAsTHE1pY3Jvc29mdCBBbWVy
# aWNhIE9wZXJhdGlvbnMxJzAlBgNVBAsTHm5TaGllbGQgVFNTIEVTTjo3ODAwLTA1
# RTAtRDk0NzE1MDMGA1UEAxMsTWljcm9zb2Z0IFB1YmxpYyBSU0EgVGltZSBTdGFt
# cGluZyBBdXRob3JpdHmggg8hMIIHgjCCBWqgAwIBAgITMwAAAAXlzw//Zi7JhwAA
# AAAABTANBgkqhkiG9w0BAQwFADB3MQswCQYDVQQGEwJVUzEeMBwGA1UEChMVTWlj
# cm9zb2Z0IENvcnBvcmF0aW9uMUgwRgYDVQQDEz9NaWNyb3NvZnQgSWRlbnRpdHkg
# VmVyaWZpY2F0aW9uIFJvb3QgQ2VydGlmaWNhdGUgQXV0aG9yaXR5IDIwMjAwHhcN
# MjAxMTE5MjAzMjMxWhcNMzUxMTE5MjA0MjMxWjBhMQswCQYDVQQGEwJVUzEeMBwG
# A1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMTIwMAYDVQQDEylNaWNyb3NvZnQg
# UHVibGljIFJTQSBUaW1lc3RhbXBpbmcgQ0EgMjAyMDCCAiIwDQYJKoZIhvcNAQEB
# BQADggIPADCCAgoCggIBAJ5851Jj/eDFnwV9Y7UGIqMcHtfnlzPREwW9ZUZHd5HB
# XXBvf7KrQ5cMSqFSHGqg2/qJhYqOQxwuEQXG8kB41wsDJP5d0zmLYKAY8Zxv3lYk
# uLDsfMuIEqvGYOPURAH+Ybl4SJEESnt0MbPEoKdNihwM5xGv0rGofJ1qOYSTNcc5
# 5EbBT7uq3wx3mXhtVmtcCEr5ZKTkKKE1CxZvNPWdGWJUPC6e4uRfWHIhZcgCsJ+s
# ozf5EeH5KrlFnxpjKKTavwfFP6XaGZGWUG8TZaiTogRoAlqcevbiqioUz1Yt4FRK
# 53P6ovnUfANjIgM9JDdJ4e0qiDRm5sOTiEQtBLGd9Vhd1MadxoGcHrRCsS5rO9yh
# v2fjJHrmlQ0EIXmp4DhDBieKUGR+eZ4CNE3ctW4uvSDQVeSp9h1SaPV8UWEfyTxg
# GjOsRpeexIveR1MPTVf7gt8hY64XNPO6iyUGsEgt8c2PxF87E+CO7A28TpjNq5eL
# iiunhKbq0XbjkNoU5JhtYUrlmAbpxRjb9tSreDdtACpm3rkpxp7AQndnI0Shu/fk
# 1/rE3oWsDqMX3jjv40e8KN5YsJBnczyWB4JyeeFMW3JBfdeAKhzohFe8U5w9Wuvc
# P1E8cIxLoKSDzCCBOu0hWdjzKNu8Y5SwB1lt5dQhABYyzR3dxEO/T1K/BVF3rV69
# AgMBAAGjggIbMIICFzAOBgNVHQ8BAf8EBAMCAYYwEAYJKwYBBAGCNxUBBAMCAQAw
# HQYDVR0OBBYEFGtpKDo1L0hjQM972K9J6T7ZPdshMFQGA1UdIARNMEswSQYEVR0g
# ADBBMD8GCCsGAQUFBwIBFjNodHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20vcGtpb3Bz
# L0RvY3MvUmVwb3NpdG9yeS5odG0wEwYDVR0lBAwwCgYIKwYBBQUHAwgwGQYJKwYB
# BAGCNxQCBAweCgBTAHUAYgBDAEEwDwYDVR0TAQH/BAUwAwEB/zAfBgNVHSMEGDAW
# gBTIftJqhSobyhmYBAcnz1AQT2ioojCBhAYDVR0fBH0wezB5oHegdYZzaHR0cDov
# L3d3dy5taWNyb3NvZnQuY29tL3BraW9wcy9jcmwvTWljcm9zb2Z0JTIwSWRlbnRp
# dHklMjBWZXJpZmljYXRpb24lMjBSb290JTIwQ2VydGlmaWNhdGUlMjBBdXRob3Jp
# dHklMjAyMDIwLmNybDCBlAYIKwYBBQUHAQEEgYcwgYQwgYEGCCsGAQUFBzAChnVo
# dHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20vcGtpb3BzL2NlcnRzL01pY3Jvc29mdCUy
# MElkZW50aXR5JTIwVmVyaWZpY2F0aW9uJTIwUm9vdCUyMENlcnRpZmljYXRlJTIw
# QXV0aG9yaXR5JTIwMjAyMC5jcnQwDQYJKoZIhvcNAQEMBQADggIBAF+Idsd+bbVa
# FXXnTHho+k7h2ESZJRWluLE0Oa/pO+4ge/XEizXvhs0Y7+KVYyb4nHlugBesnFqB
# GEdC2IWmtKMyS1OWIviwpnK3aL5JedwzbeBF7POyg6IGG/XhhJ3UqWeWTO+Czb1c
# 2NP5zyEh89F72u9UIw+IfvM9lzDmc2O2END7MPnrcjWdQnrLn1Ntday7JSyrDvBd
# mgbNnCKNZPmhzoa8PccOiQljjTW6GePe5sGFuRHzdFt8y+bN2neF7Zu8hTO1I64X
# NGqst8S+w+RUdie8fXC1jKu3m9KGIqF4aldrYBamyh3g4nJPj/LR2CBaLyD+2BuG
# ZCVmoNR/dSpRCxlot0i79dKOChmoONqbMI8m04uLaEHAv4qwKHQ1vBzbV/nG89LD
# KbRSSvijmwJwxRxLLpMQ/u4xXxFfR4f/gksSkbJp7oqLwliDm/h+w0aJ/U5ccnYh
# Yb7vPKNMN+SZDWycU5ODIRfyoGl59BsXR/HpRGtiJquOYGmvA/pk5vC1lcnbeMrc
# WD/26ozePQ/TWfNXKBOmkFpvPE8CH+EeGGWzqTCjdAsno2jzTeNSxlx3glDGJgcd
# z5D/AAxw9Sdgq/+rY7jjgs7X6fqPTXPmaCAJKVHAP19oEjJIBwD1LyHbaEgBxFCo
# gYSOiUIr0Xqcr1nJfiWG2GwYe6ZoAF1bMIIHlzCCBX+gAwIBAgITMwAAAFck05Xg
# ounJMQAAAAAAVzANBgkqhkiG9w0BAQwFADBhMQswCQYDVQQGEwJVUzEeMBwGA1UE
# ChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMTIwMAYDVQQDEylNaWNyb3NvZnQgUHVi
# bGljIFJTQSBUaW1lc3RhbXBpbmcgQ0EgMjAyMDAeFw0yNTEwMjMyMDQ2NTNaFw0y
# NjEwMjIyMDQ2NTNaMIHbMQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3Rv
# bjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0
# aW9uMSUwIwYDVQQLExxNaWNyb3NvZnQgQW1lcmljYSBPcGVyYXRpb25zMScwJQYD
# VQQLEx5uU2hpZWxkIFRTUyBFU046NzgwMC0wNUUwLUQ5NDcxNTAzBgNVBAMTLE1p
# Y3Jvc29mdCBQdWJsaWMgUlNBIFRpbWUgU3RhbXBpbmcgQXV0aG9yaXR5MIICIjAN
# BgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAsWylCpMIfbizJLY1kPXO2cmX2HRW
# vRbAmeKSZ5ex7/jCymdV7Eap+Ic2iqRtWDkKKe5gL6JV80wtn5C2qHJLPxUYFKNG
# 3UkHkAI21MoCN+YWnhT8K/YuPib6+6970jdbeFKIiZMWwd5hnpX9J3jeteuEdXbp
# /DfFBK15JuD3JOzWuF2suQCPgqYjQPk/gpq+3KCKtXJRbXSCSJ9YtITU2IHwmfdE
# 7l2PfZ154w041po+fDeTj0gJOzcV/Jv56Q0M+w19jAKo/I5PEzrLV1IPQnmP4or1
# X4RbJXk8ONXyOOfXOxK2VLpNxgklK1yAezbFP2uzqihaXkW1h9GQLGENKESnezwg
# dRaLNNaYtm8AT/pZHYJ35mZVqkZdMIckpQHJk/F1fSLyDKeKtH4TC4cc3ESKUMgI
# tq07ZZm74JCsfhmrQ1ijVNDi1Sln+QBamgC7WviZbkQnceQRq9DY+6hANwOrasAZ
# UiVr2kPuj1jHDOXzUG4O9QTK70P/oXSqZAN1oTv3UfF8JTGmAxg+l1ZPOz50MY96
# HBDw/3bI/wBGNvLk6fLVnrxGN5B5unF/lYvjjWbIUdyBPVQnPOKXu08SRHbY19M1
# HoWX6PNZv+vzSeqVeWWHKdKjC3GjVjbbGpi+JLbiyaKRSwEqo49tJLvu69cQ7dWs
# bksai4TURnVj2mMCAwEAAaOCAcswggHHMB0GA1UdDgQWBBSOg8leLTUOAglIZ+bj
# XpiD7RKSpzAfBgNVHSMEGDAWgBRraSg6NS9IY0DPe9ivSek+2T3bITBsBgNVHR8E
# ZTBjMGGgX6BdhltodHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20vcGtpb3BzL2NybC9N
# aWNyb3NvZnQlMjBQdWJsaWMlMjBSU0ElMjBUaW1lc3RhbXBpbmclMjBDQSUyMDIw
# MjAuY3JsMHkGCCsGAQUFBwEBBG0wazBpBggrBgEFBQcwAoZdaHR0cDovL3d3dy5t
# aWNyb3NvZnQuY29tL3BraW9wcy9jZXJ0cy9NaWNyb3NvZnQlMjBQdWJsaWMlMjBS
# U0ElMjBUaW1lc3RhbXBpbmclMjBDQSUyMDIwMjAuY3J0MAwGA1UdEwEB/wQCMAAw
# FgYDVR0lAQH/BAwwCgYIKwYBBQUHAwgwDgYDVR0PAQH/BAQDAgeAMGYGA1UdIARf
# MF0wUQYMKwYBBAGCN0yDfQEBMEEwPwYIKwYBBQUHAgEWM2h0dHA6Ly93d3cubWlj
# cm9zb2Z0LmNvbS9wa2lvcHMvRG9jcy9SZXBvc2l0b3J5Lmh0bTAIBgZngQwBBAIw
# DQYJKoZIhvcNAQEMBQADggIBAHJ1wHY86Zk5SUBDPY25d/u9YJVaaNa71uxjX4cy
# O/XJ4uPENCSOwkRTnNogPLxTD0Fg3z4TFf/2T/0IFSxdtWVtTjhzrn+WLInzeRaw
# UhTCFVrPBJKEWVshm+Ig7/nB7JbJN88+ltImBbL5kT1StBLfG6UksAcDbNSQww90
# CUXhGueBxlnSvjkAX1ohiN16y1bB2s0rvQx8Csepl2CuBefTfDrMGzW/tzNx5YaK
# 2D8OWweqTWZcGlJO4YjZNI83cTrQghfHl/8AXOHj8cWL3wEFltQQs2xeRYAb3Kdn
# l7oIWKKXWaBYJY5P3QPsiC+DTMp7ejdYKTrb396f3gr+wL/Ms5/Z3vIWZPJJv18q
# Nw40fUNveRnwzMQnx8dM2bGuXXQZ5y7P8aXT4HJMo349qZtn4XQwiUE/DDp++MUL
# 0kgjvd/Deo7Xr371PFPPYb4TboZhjV1x9+wCHDoOpNCBt+VuXU78ytJdKzQ1Jv2c
# EP1F9H9/wSLsMDUvWME7u9mGElOPDZPMVr8AuBEuLdbTSEdaLwsZBplzxLBcgxhZ
# /Cs30yBhuE3QhqT1YDZ2pa56RexPA2SasPcToT6gJgJ6E06BmZ2zQTNvWOjs5XQq
# HbYuXcoeDcwe2UaC7EDOGD8GmLE9LiqtQsuQCM7v7I2xR+sPZT2Ax/85HjIkM+3M
# zTK1MYIHQzCCBz8CAQEweDBhMQswCQYDVQQGEwJVUzEeMBwGA1UEChMVTWljcm9z
# b2Z0IENvcnBvcmF0aW9uMTIwMAYDVQQDEylNaWNyb3NvZnQgUHVibGljIFJTQSBU
# aW1lc3RhbXBpbmcgQ0EgMjAyMAITMwAAAFck05XgounJMQAAAAAAVzANBglghkgB
# ZQMEAgEFAKCCBJwwEQYLKoZIhvcNAQkQAg8xAgUAMBoGCSqGSIb3DQEJAzENBgsq
# hkiG9w0BCRABBDAcBgkqhkiG9w0BCQUxDxcNMjYwNjAyMDE1NTQ1WjAvBgkqhkiG
# 9w0BCQQxIgQgl9I2rR/6hKBnINU7FTVZrsFQnXuL+WTTzbXUP+TLXx0wgbkGCyqG
# SIb3DQEJEAIvMYGpMIGmMIGjMIGgBCD1PJ9ktQVuTGWIbKLO4f1VUOlUU29ARCEp
# DZmFTHjbUjB8MGWkYzBhMQswCQYDVQQGEwJVUzEeMBwGA1UEChMVTWljcm9zb2Z0
# IENvcnBvcmF0aW9uMTIwMAYDVQQDEylNaWNyb3NvZnQgUHVibGljIFJTQSBUaW1l
# c3RhbXBpbmcgQ0EgMjAyMAITMwAAAFck05XgounJMQAAAAAAVzCCA14GCyqGSIb3
# DQEJEAISMYIDTTCCA0mhggNFMIIDQTCCAikCAQEwggEJoYHhpIHeMIHbMQswCQYD
# VQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEe
# MBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSUwIwYDVQQLExxNaWNyb3Nv
# ZnQgQW1lcmljYSBPcGVyYXRpb25zMScwJQYDVQQLEx5uU2hpZWxkIFRTUyBFU046
# NzgwMC0wNUUwLUQ5NDcxNTAzBgNVBAMTLE1pY3Jvc29mdCBQdWJsaWMgUlNBIFRp
# bWUgU3RhbXBpbmcgQXV0aG9yaXR5oiMKAQEwBwYFKw4DAhoDFQD9LzE5nEJRAUE2
# Ss3xaKKPXHnLw6BnMGWkYzBhMQswCQYDVQQGEwJVUzEeMBwGA1UEChMVTWljcm9z
# b2Z0IENvcnBvcmF0aW9uMTIwMAYDVQQDEylNaWNyb3NvZnQgUHVibGljIFJTQSBU
# aW1lc3RhbXBpbmcgQ0EgMjAyMDANBgkqhkiG9w0BAQsFAAIFAO3Ij48wIhgPMjAy
# NjA2MDEyMzA5MDNaGA8yMDI2MDYwMjIzMDkwM1owdDA6BgorBgEEAYRZCgQBMSww
# KjAKAgUA7ciPjwIBADAHAgEAAgIVRTAHAgEAAgISSTAKAgUA7cnhDwIBADA2Bgor
# BgEEAYRZCgQCMSgwJjAMBgorBgEEAYRZCgMCoAowCAIBAAIDB6EgoQowCAIBAAID
# AYagMA0GCSqGSIb3DQEBCwUAA4IBAQBNKZXhNEhXkxpO/lbxR3O+XZwer5GHiMEV
# n+tx2Pe+4o2LjmMWLS4gvkAvxwadgn/PWJNTnrE9cBxqvuR4botN4bC1yiRpBrQs
# KUgDgnqoCXhqXDzhwIVmMoqB2FuB7doAuMgoBhZdxKen4LywkRPPbzCf4SJsLoWc
# 2KvJTZtuBUWaGzljc/SZppv6tTJ1Iwcp2esHj53ZvaN7oxVHdUjC13XHHqxUsn7z
# yeGGs6yPPUC4fD0cFhw2aq+WeAMHxCCsmNpEm2VvIrSIhK0h6Q1HLBJl3303YKgF
# sT9N4uXjMylFksHjZearxku+W1y+zJ08JKE5S4FXuJ+3IdaktmhTMA0GCSqGSIb3
# DQEBAQUABIICAG/nlDspg4MmniF/5aDjKm0P0uIDpTj4TDjSdJRz71MPuHhQZLLb
# WaKj6QCNWX22lljJBsx2jzWgtFAreISTR5xtKouKr+mQulhw8InE8xUR+TdN43FS
# KuicvPLEq13E5XN87IcOtlSf8WpQeQ4NHnTPaOqk3RZT7RArwTzMbV9B4rn1X/MR
# 2EyjolMKcitIeMjzyfBaHQKP+ApWM6FYidd9gOZCgrXgVJHkD01E1w37PCZoxYD9
# GEYjyTZgWg28oPyKj5Sl/58tpxsl8rvHGlF4GnzX+8KNDMJ2eY8NbZckO+b3uIwL
# hCT5n7T/DXHAPrudwjHe5nErKHMTWNzRzvS87Nr8JmnI9SvNgHwcYQ1O5phnYUZH
# JPjTiDfRe3jTFfupuuCbNMG7iCgWL+PzwKc3kKSn5exFugc00ogQwgMNoEwtUSsG
# 216Wa2bsCOoUWiWnbOgNwlAufPPYYmhVvWYOZKaWBdM9Qjynu+4uogJNohs/arxV
# 5wS5z2jTzuJOMBjh6F6gYULho6NFAkWorBmAixuXB+ABMXudylZTdyE1mdFkx0+5
# 4OmIak+rDfJ3nDmb7cBtNJr4TjW7dWKEBvO8djO+x2FHJok6BmzZTkXngZpB/4LC
# zrdIzv+QERBszK5TiEgGr16+iGvtK9MlYhHugUM7zcUX6YWAdSuOkHG/
# SIG # End signature block