LISSTech.DrainCtl.psm1

#Requires -Version 5.1

Set-StrictMode -Version Latest

# Locate drainctl.dll — may be alongside the psm1 (MSI install) or in bin/ subdir (dev/PSGallery).
$script:DllPath = Join-Path $PSScriptRoot 'drainctl.dll'
if (-not (Test-Path $script:DllPath)) {
    $script:DllPath = Join-Path (Join-Path $PSScriptRoot 'bin') 'drainctl.dll'
}
if (-not (Test-Path $script:DllPath)) {
    throw "DrainCtl: drainctl.dll not found. Module installation may be corrupt."
}

# Locate drainctl.exe — add its directory to PATH if not already there.
$script:BinDir = Split-Path $script:DllPath
$script:ExePath = Join-Path $script:BinDir 'drainctl.exe'

if ($env:PATH -notlike "*$($script:BinDir)*") {
    $env:PATH = "$($script:BinDir);$env:PATH"
}

$MyInvocation.MyCommand.ScriptBlock.Module.OnRemove = {
    $env:PATH = ($env:PATH -split ';' | Where-Object { $_ -ne $script:BinDir }) -join ';'
}

# ── Native interop ──────────────────────────────────────────────────────────

# P/Invoke declarations with a UTF-8 helper that works on both
# .NET Framework 4.x (Windows PowerShell 5.1) and .NET 6+ (PowerShell 7+).
Add-Type -TypeDefinition @"
using System;
using System.Runtime.InteropServices;
using System.Text;
 
public static class DrainCtlNative {
    [DllImport("$($script:DllPath.Replace('\','\\'))", CallingConvention = CallingConvention.Cdecl)]
    public static extern IntPtr DrainCtl_Version();
 
    [DllImport("$($script:DllPath.Replace('\','\\'))", CallingConvention = CallingConvention.Cdecl)]
    public static extern IntPtr DrainCtl_ReadDrainMode();
 
    [DllImport("$($script:DllPath.Replace('\','\\'))", CallingConvention = CallingConvention.Cdecl)]
    public static extern IntPtr DrainCtl_Check(
        [MarshalAs(UnmanagedType.LPStr)] string dbPath,
        int graceMinutes,
        int retentionDays);
 
    [DllImport("$($script:DllPath.Replace('\','\\'))", CallingConvention = CallingConvention.Cdecl)]
    public static extern IntPtr DrainCtl_History(
        [MarshalAs(UnmanagedType.LPStr)] string dbPath,
        int limit,
        int changesOnly);
 
    [DllImport("$($script:DllPath.Replace('\','\\'))", CallingConvention = CallingConvention.Cdecl)]
    public static extern IntPtr DrainCtl_AuditSetup();
 
    [DllImport("$($script:DllPath.Replace('\','\\'))", CallingConvention = CallingConvention.Cdecl)]
    public static extern IntPtr DrainCtl_GetNotifyConfig();
 
    [DllImport("$($script:DllPath.Replace('\','\\'))", CallingConvention = CallingConvention.Cdecl)]
    public static extern IntPtr DrainCtl_SetNotifyConfig(
        [MarshalAs(UnmanagedType.LPStr)] string jsonStr);
 
    [DllImport("$($script:DllPath.Replace('\','\\'))", CallingConvention = CallingConvention.Cdecl)]
    public static extern IntPtr DrainCtl_TestNotify();
 
    [DllImport("$($script:DllPath.Replace('\','\\'))", CallingConvention = CallingConvention.Cdecl)]
    public static extern IntPtr DrainCtl_EnableDashboard(int port, [MarshalAs(UnmanagedType.LPStr)] string group);
 
    [DllImport("$($script:DllPath.Replace('\','\\'))", CallingConvention = CallingConvention.Cdecl)]
    public static extern IntPtr DrainCtl_DisableDashboard();
 
    [DllImport("$($script:DllPath.Replace('\','\\'))", CallingConvention = CallingConvention.Cdecl)]
    public static extern void DrainCtl_Free(IntPtr ptr);
 
    /// <summary>
    /// Read a null-terminated UTF-8 string from unmanaged memory.
    /// Works on .NET Framework 4.x (PS 5.1) and .NET 6+ (PS 7+).
    /// </summary>
    public static string PtrToStringUTF8(IntPtr ptr) {
        if (ptr == IntPtr.Zero) return null;
        int len = 0;
        while (Marshal.ReadByte(ptr, len) != 0) len++;
        byte[] buf = new byte[len];
        Marshal.Copy(ptr, buf, 0, len);
        return Encoding.UTF8.GetString(buf);
    }
}
"@


function Invoke-DrainCtlNative {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory)]
        [IntPtr]$Ptr
    )

    try {
        $json = [DrainCtlNative]::PtrToStringUTF8($Ptr)
    } finally {
        [DrainCtlNative]::DrainCtl_Free($Ptr)
    }

    $obj = $json | ConvertFrom-Json

    if ($null -ne ($obj.PSObject.Properties['error'])) {
        $err = $obj.PSObject.Properties['error'].Value
        $exception = [System.InvalidOperationException]::new($err)
        $errorRecord = [System.Management.Automation.ErrorRecord]::new(
            $exception, 'DrainCtlNativeError', [System.Management.Automation.ErrorCategory]::InvalidResult, $null)
        $PSCmdlet.ThrowTerminatingError($errorRecord)
    }

    return $obj
}

function Get-SafeProperty {
    # Reads a property from a PSObject without triggering strict-mode errors
    # when the property doesn't exist (JSON omitempty).
    param($Object, [string]$Name, $Default = $null)
    $prop = $Object.PSObject.Properties[$Name]
    if ($null -ne $prop) { return $prop.Value }
    return $Default
}

function ConvertTo-NativeDateTime {
    # Converts an ISO 8601 string to [datetime]. Returns $null for absent,
    # null, empty, or Go zero-time values.
    param($Value)
    if ($null -eq $Value -or $Value -eq '') { return $null }
    $s = [string]$Value
    # Go zero time
    if ($s -like '0001-01-01*') { return $null }
    return [datetimeoffset]::Parse($s).LocalDateTime
}

# ── Public cmdlets ──────────────────────────────────────────────────────────

function Get-RDSHDrainMode {
    <#
    .SYNOPSIS
    Returns detailed drain mode state with grace period evaluation and audit trail.
 
    .DESCRIPTION
    Reads the TSServerDrainMode registry value, detects state transitions,
    records an audit entry, and evaluates whether drain mode has exceeded
    the grace period.
 
    Returns a rich object with Status, Host, DrainMode, StateSince,
    StateDurationSeconds, transition info, and who changed it.
 
    Status values:
      healthy - connections allowed
      grace - drain mode active but within grace period
      alert - drain mode active beyond grace period
      error - could not read registry
 
    Sets $LASTEXITCODE (0 = healthy/grace, 1 = alert, 2 = error).
 
    .PARAMETER DBPath
    Path to the JSONL audit trail file.
    Default: %ProgramData%\drainctl\audit.jsonl
 
    .PARAMETER GraceMinutes
    Minutes drain mode must persist before alerting. Default: 60.
 
    .PARAMETER RetentionDays
    Days to retain audit records. Default: 90.
 
    .EXAMPLE
    PS> Get-RDSHDrainMode
 
    Host : RDSH01
    DrainMode : ALLOW_ALL_CONNECTIONS
    Status : healthy
    StateDurationSeconds : 3600
 
    .EXAMPLE
    PS> Get-RDSHDrainMode -GraceMinutes 120 | Select-Object Host, Status, DrainMode
 
    .EXAMPLE
    PS> Get-RDSHDrainMode | Where-Object Status -ne 'healthy'
    #>

    [CmdletBinding()]
    [OutputType([PSCustomObject])]
    param(
        [Parameter()]
        [string]$DBPath = '',

        [Parameter()]
        [ValidateRange(0, [int]::MaxValue)]
        [int]$GraceMinutes = 60,

        [Parameter()]
        [ValidateRange(1, 365)]
        [int]$RetentionDays = 90
    )

    $ptr = [DrainCtlNative]::DrainCtl_Check($DBPath, $GraceMinutes, $RetentionDays)
    $raw = Invoke-DrainCtlNative -Ptr $ptr

    $global:LASTEXITCODE = $raw.exit_code

    [PSCustomObject]@{
        PSTypeName           = 'DrainCtl.CheckResult'
        Timestamp            = ConvertTo-NativeDateTime $raw.timestamp
        Host                 = $raw.host
        DrainMode            = $raw.drain_mode
        DrainModeValue       = $raw.drain_mode_value
        StateSince           = ConvertTo-NativeDateTime $raw.state_since
        StateDurationSeconds = if ($null -ne $raw.state_duration_seconds) { [int]$raw.state_duration_seconds } else { $null }
        GracePeriodSeconds   = $raw.grace_period_seconds
        Status               = $raw.status
        ConnectionsAllowed   = $raw.connections_allowed
        Transition           = $raw.transition
        TransitionFrom       = Get-SafeProperty $raw 'transition_from'
        ChangedBy            = Get-SafeProperty $raw 'changed_by'
        Message              = Get-SafeProperty $raw 'message'
        ExitCode             = $raw.exit_code
    }
}

function Test-RDSHDrainMode {
    <#
    .SYNOPSIS
    Tests whether RDSH connections are allowed. Returns $true or $false.
 
    .DESCRIPTION
    Returns $true if the server is accepting connections (drain mode is off
    or within the grace period). Returns $false if drain mode is active
    beyond the grace period or the registry cannot be read.
 
    Also records an audit entry and sets $LASTEXITCODE
    (0 = pass, 1 = alert, 2 = error).
 
    .PARAMETER DBPath
    Path to the JSONL audit trail file.
    Default: %ProgramData%\drainctl\audit.jsonl
 
    .PARAMETER GraceMinutes
    Minutes drain mode must persist before alerting. Default: 60.
 
    .PARAMETER RetentionDays
    Days to retain audit records. Default: 90.
 
    .EXAMPLE
    PS> if (Test-RDSHDrainMode) { 'OK' } else { 'ALERT' }
 
    .EXAMPLE
    PS> Test-RDSHDrainMode -GraceMinutes 120
    True
    #>

    [CmdletBinding()]
    [OutputType([bool])]
    param(
        [Parameter()]
        [string]$DBPath = '',

        [Parameter()]
        [ValidateRange(0, [int]::MaxValue)]
        [int]$GraceMinutes = 60,

        [Parameter()]
        [ValidateRange(1, 365)]
        [int]$RetentionDays = 90
    )

    $ptr = [DrainCtlNative]::DrainCtl_Check($DBPath, $GraceMinutes, $RetentionDays)
    $result = Invoke-DrainCtlNative -Ptr $ptr

    $global:LASTEXITCODE = $result.exit_code

    $result.exit_code -eq 0
}

function Get-RDSHDrainHistory {
    <#
    .SYNOPSIS
    Retrieves the drain mode audit trail.
 
    .DESCRIPTION
    Returns audit records from the JSONL trail, newest first.
    Each record includes the timestamp, host, drain mode, whether
    a state transition occurred, and who made the change.
 
    .PARAMETER DBPath
    Path to the JSONL audit trail file.
    Default: %ProgramData%\drainctl\audit.jsonl
 
    .PARAMETER Limit
    Maximum number of records to return. Default: 50.
 
    .PARAMETER ChangesOnly
    If set, returns only records where a state transition occurred.
 
    .EXAMPLE
    PS> Get-RDSHDrainHistory | Format-Table
 
    .EXAMPLE
    PS> Get-RDSHDrainHistory -ChangesOnly -Limit 10
 
    .EXAMPLE
    PS> Get-RDSHDrainHistory | Where-Object Changed -eq $true
    #>

    [CmdletBinding()]
    [OutputType([PSCustomObject[]])]
    param(
        [Parameter()]
        [string]$DBPath = '',

        [Parameter()]
        [ValidateRange(1, [int]::MaxValue)]
        [int]$Limit = 50,

        [Parameter()]
        [switch]$ChangesOnly
    )

    $co = if ($ChangesOnly) { 1 } else { 0 }
    $ptr = [DrainCtlNative]::DrainCtl_History($DBPath, $Limit, $co)
    $records = Invoke-DrainCtlNative -Ptr $ptr

    if ($null -eq $records -or @($records).Count -eq 0) {
        return
    }

    foreach ($r in $records) {
        [PSCustomObject]@{
            PSTypeName           = 'DrainCtl.AuditRecord'
            Timestamp            = ConvertTo-NativeDateTime $r.timestamp
            Host                 = $r.host
            DrainMode            = $r.drain_mode
            DrainModeValue       = $r.drain_mode_value
            StateDurationSeconds = Get-SafeProperty $r 'state_duration_seconds'
            Changed              = $r.changed
            ChangedBy            = Get-SafeProperty $r 'changed_by'
            ExitCode             = $r.exit_code
        }
    }
}

function Install-RDSHDrainAudit {
    <#
    .SYNOPSIS
    Configures registry auditing for drain mode change attribution.
 
    .DESCRIPTION
    One-time setup that enables Object Access auditing and sets a SACL on
    the Terminal Server registry key so that Windows records Event ID 4657
    whenever TSServerDrainMode is modified. This allows Get-RDSHDrainMode
    and drainctl to attribute changes to specific users.
 
    Must be run elevated (as Administrator or SYSTEM).
 
    On domain-joined machines, Group Policy may override the local auditpol
    settings on the next GP refresh (~90 minutes). The command emits warnings
    with the exact GPO path to configure for persistence.
 
    .EXAMPLE
    PS> Install-RDSHDrainAudit
    #>

    [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'High')]
    [OutputType([void])]
    param()

    if (-not $PSCmdlet.ShouldProcess('Registry auditing for TSServerDrainMode', 'Configure')) {
        return
    }

    $ptr = [DrainCtlNative]::DrainCtl_AuditSetup()
    $result = Invoke-DrainCtlNative -Ptr $ptr

    Write-Verbose 'Registry auditing configured. Event ID 4657 will now record TSServerDrainMode changes.'
}

function Get-RDSHDrainNotification {
    <#
    .SYNOPSIS
    Shows the current notification configuration for DrainCtl.
 
    .DESCRIPTION
    Reads notification settings from config.json and returns them as a
    structured object. Shows webhook URL, ntfy URL, transition/grace
    notification toggles, and repeat interval.
 
    .EXAMPLE
    PS> Get-RDSHDrainNotification
 
    WebhookURL : https://hooks.example.com/drainctl
    NtfyURL :
    OnTransition : True
    OnGraceExceeded : True
    RepeatMinutes : 0
    Enabled : True
    #>

    [CmdletBinding()]
    [OutputType([PSCustomObject])]
    param()

    $ptr = [DrainCtlNative]::DrainCtl_GetNotifyConfig()
    $raw = Invoke-DrainCtlNative -Ptr $ptr

    [PSCustomObject]@{
        PSTypeName      = 'DrainCtl.NotifyConfig'
        WebhookURL      = Get-SafeProperty $raw 'webhook_url' ''
        NtfyURL         = Get-SafeProperty $raw 'ntfy_url' ''
        OnTransition    = [bool](Get-SafeProperty $raw 'on_transition' $true)
        OnGraceExceeded = [bool](Get-SafeProperty $raw 'on_grace_exceeded' $true)
        RepeatMinutes   = [int](Get-SafeProperty $raw 'repeat_minutes' 0)
        Enabled         = [bool](Get-SafeProperty $raw 'enabled' $false)
    }
}

function Set-RDSHDrainNotification {
    <#
    .SYNOPSIS
    Updates notification settings for DrainCtl.
 
    .DESCRIPTION
    Writes notification configuration to config.json. Only specified
    parameters are changed; unspecified parameters retain their current
    values.
 
    .PARAMETER WebhookURL
    Webhook URL for HTTP POST JSON notifications. Set to empty string to disable.
 
    .PARAMETER NtfyURL
    ntfy.sh topic URL (e.g. "https://ntfy.sh/drainctl-alerts"). Set to empty string to disable.
 
    .PARAMETER OnTransition
    Enable or disable notifications on state transitions.
 
    .PARAMETER OnGraceExceeded
    Enable or disable notifications when drain exceeds grace period.
 
    .PARAMETER RepeatMinutes
    Minutes between repeated alert notifications (0 = notify once only).
 
    .EXAMPLE
    PS> Set-RDSHDrainNotification -WebhookURL 'https://hooks.example.com/drainctl'
 
    .EXAMPLE
    PS> Set-RDSHDrainNotification -NtfyURL 'https://ntfy.sh/my-alerts' -RepeatMinutes 30
 
    .EXAMPLE
    PS> Set-RDSHDrainNotification -WebhookURL '' -NtfyURL ''
    #>

    [CmdletBinding(SupportsShouldProcess)]
    [OutputType([void])]
    param(
        [Parameter()]
        [AllowEmptyString()]
        [string]$WebhookURL,

        [Parameter()]
        [AllowEmptyString()]
        [string]$NtfyURL,

        [Parameter()]
        [bool]$OnTransition,

        [Parameter()]
        [bool]$OnGraceExceeded,

        [Parameter()]
        [ValidateRange(0, [int]::MaxValue)]
        [int]$RepeatMinutes
    )

    if (-not $PSCmdlet.ShouldProcess('DrainCtl notification configuration', 'Update')) {
        return
    }

    $payload = @{}
    if ($PSBoundParameters.ContainsKey('WebhookURL'))      { $payload['webhook_url']       = $WebhookURL }
    if ($PSBoundParameters.ContainsKey('NtfyURL'))          { $payload['ntfy_url']          = $NtfyURL }
    if ($PSBoundParameters.ContainsKey('OnTransition'))     { $payload['on_transition']     = $OnTransition }
    if ($PSBoundParameters.ContainsKey('OnGraceExceeded'))  { $payload['on_grace_exceeded'] = $OnGraceExceeded }
    if ($PSBoundParameters.ContainsKey('RepeatMinutes'))    { $payload['repeat_minutes']    = $RepeatMinutes }

    $jsonStr = $payload | ConvertTo-Json -Compress
    $ptr = [DrainCtlNative]::DrainCtl_SetNotifyConfig($jsonStr)
    $null = Invoke-DrainCtlNative -Ptr $ptr

    Write-Verbose 'Notification configuration updated.'
}

function Test-RDSHDrainNotification {
    <#
    .SYNOPSIS
    Sends a test notification to all configured backends.
 
    .DESCRIPTION
    Reads the current notification configuration and sends a test message
    to each configured backend (webhook and/or ntfy). Returns an error
    if no backends are configured or if sending fails.
 
    .EXAMPLE
    PS> Test-RDSHDrainNotification
    #>

    [CmdletBinding()]
    [OutputType([void])]
    param()

    $ptr = [DrainCtlNative]::DrainCtl_TestNotify()
    $null = Invoke-DrainCtlNative -Ptr $ptr

    Write-Host 'Test notification sent successfully.'
}

function Enable-RDSHDrainDashboard {
    <#
    .SYNOPSIS
    Enable the DrainCtl multi-server dashboard on this server.
 
    .PARAMETER Port
    Port the dashboard listens on. Default: 49470.
 
    .PARAMETER Group
    AD group authorized to view the dashboard. Default: Domain Admins.
 
    .EXAMPLE
    Enable-RDSHDrainDashboard -Port 49470 -Group "RDS Admins"
    #>

    [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'Medium')]
    param(
        [int]$Port = 49470,
        [string]$Group = 'Domain Admins'
    )
    if (-not $PSCmdlet.ShouldProcess('DrainCtl Dashboard', 'Enable')) { return }
    $ptr = [DrainCtlNative]::DrainCtl_EnableDashboard($Port, $Group)
    $result = Invoke-DrainCtlNative -Ptr $ptr
    Write-Host "Dashboard enabled on port $Port for group '$Group'."
    Write-Host 'Restart the DrainCtl service to activate: Restart-Service DrainCtl'
}

function Disable-RDSHDrainDashboard {
    <#
    .SYNOPSIS
    Disable the DrainCtl dashboard on this server.
 
    .EXAMPLE
    Disable-RDSHDrainDashboard
    #>

    [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'Medium')]
    param()
    if (-not $PSCmdlet.ShouldProcess('DrainCtl Dashboard', 'Disable')) { return }
    $ptr = [DrainCtlNative]::DrainCtl_DisableDashboard()
    Invoke-DrainCtlNative -Ptr $ptr | Out-Null
    Write-Host 'Dashboard disabled. Restart the DrainCtl service to apply: Restart-Service DrainCtl'
}

Export-ModuleMember -Function @(
    'Get-RDSHDrainMode'
    'Test-RDSHDrainMode'
    'Get-RDSHDrainHistory'
    'Install-RDSHDrainAudit'
    'Get-RDSHDrainNotification'
    'Set-RDSHDrainNotification'
    'Test-RDSHDrainNotification'
    'Enable-RDSHDrainDashboard'
    'Disable-RDSHDrainDashboard'
)

# SIG # Begin signature block
# MIItnAYJKoZIhvcNAQcCoIItjTCCLYkCAQExDzANBglghkgBZQMEAgEFADB5Bgor
# BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG
# KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCAXJ4jv6gEG+EJ9
# BP78ZITPBmoPqjqofw7debqkp7k92KCCJp8wggWNMIIEdaADAgECAhAOmxiO+dAt
# 5+/bUOIIQBhaMA0GCSqGSIb3DQEBDAUAMGUxCzAJBgNVBAYTAlVTMRUwEwYDVQQK
# EwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xJDAiBgNV
# BAMTG0RpZ2lDZXJ0IEFzc3VyZWQgSUQgUm9vdCBDQTAeFw0yMjA4MDEwMDAwMDBa
# Fw0zMTExMDkyMzU5NTlaMGIxCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2Vy
# dCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xITAfBgNVBAMTGERpZ2lD
# ZXJ0IFRydXN0ZWQgUm9vdCBHNDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoC
# ggIBAL/mkHNo3rvkXUo8MCIwaTPswqclLskhPfKK2FnC4SmnPVirdprNrnsbhA3E
# MB/zG6Q4FutWxpdtHauyefLKEdLkX9YFPFIPUh/GnhWlfr6fqVcWWVVyr2iTcMKy
# unWZanMylNEQRBAu34LzB4TmdDttceItDBvuINXJIB1jKS3O7F5OyJP4IWGbNOsF
# xl7sWxq868nPzaw0QF+xembud8hIqGZXV59UWI4MK7dPpzDZVu7Ke13jrclPXuU1
# 5zHL2pNe3I6PgNq2kZhAkHnDeMe2scS1ahg4AxCN2NQ3pC4FfYj1gj4QkXCrVYJB
# MtfbBHMqbpEBfCFM1LyuGwN1XXhm2ToxRJozQL8I11pJpMLmqaBn3aQnvKFPObUR
# WBf3JFxGj2T3wWmIdph2PVldQnaHiZdpekjw4KISG2aadMreSx7nDmOu5tTvkpI6
# nj3cAORFJYm2mkQZK37AlLTSYW3rM9nF30sEAMx9HJXDj/chsrIRt7t/8tWMcCxB
# YKqxYxhElRp2Yn72gLD76GSmM9GJB+G9t+ZDpBi4pncB4Q+UDCEdslQpJYls5Q5S
# UUd0viastkF13nqsX40/ybzTQRESW+UQUOsxxcpyFiIJ33xMdT9j7CFfxCBRa2+x
# q4aLT8LWRV+dIPyhHsXAj6KxfgommfXkaS+YHS312amyHeUbAgMBAAGjggE6MIIB
# NjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBTs1+OC0nFdZEzfLmc/57qYrhwP
# TzAfBgNVHSMEGDAWgBRF66Kv9JLLgjEtUYunpyGd823IDzAOBgNVHQ8BAf8EBAMC
# AYYweQYIKwYBBQUHAQEEbTBrMCQGCCsGAQUFBzABhhhodHRwOi8vb2NzcC5kaWdp
# Y2VydC5jb20wQwYIKwYBBQUHMAKGN2h0dHA6Ly9jYWNlcnRzLmRpZ2ljZXJ0LmNv
# bS9EaWdpQ2VydEFzc3VyZWRJRFJvb3RDQS5jcnQwRQYDVR0fBD4wPDA6oDigNoY0
# aHR0cDovL2NybDMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0QXNzdXJlZElEUm9vdENB
# LmNybDARBgNVHSAECjAIMAYGBFUdIAAwDQYJKoZIhvcNAQEMBQADggEBAHCgv0Nc
# Vec4X6CjdBs9thbX979XB72arKGHLOyFXqkauyL4hxppVCLtpIh3bb0aFPQTSnov
# Lbc47/T/gLn4offyct4kvFIDyE7QKt76LVbP+fT3rDB6mouyXtTP0UNEm0Mh65Zy
# oUi0mcudT6cGAxN3J0TU53/oWajwvy8LpunyNDzs9wPHh6jSTEAZNUZqaVSwuKFW
# juyk1T3osdz9HNj0d1pcVIxv76FQPfx2CWiEn2/K2yCNNWAcAgPLILCsWKAOQGPF
# mCLBsln1VWvPJ6tsds5vIy30fnFqI2si/xK4VC0nftg62fC2h5b9W9FcrBjDTZ9z
# twGpn1eqXijiuZQwggXfMIIEx6ADAgECAhBOQOQ3VO3mjAAAAABR05R/MA0GCSqG
# SIb3DQEBCwUAMIG+MQswCQYDVQQGEwJVUzEWMBQGA1UEChMNRW50cnVzdCwgSW5j
# LjEoMCYGA1UECxMfU2VlIHd3dy5lbnRydXN0Lm5ldC9sZWdhbC10ZXJtczE5MDcG
# A1UECxMwKGMpIDIwMDkgRW50cnVzdCwgSW5jLiAtIGZvciBhdXRob3JpemVkIHVz
# ZSBvbmx5MTIwMAYDVQQDEylFbnRydXN0IFJvb3QgQ2VydGlmaWNhdGlvbiBBdXRo
# b3JpdHkgLSBHMjAeFw0yMTA1MDcxNTQzNDVaFw0zMDExMDcxNjEzNDVaMGkxCzAJ
# BgNVBAYTAlVTMRYwFAYDVQQKDA1FbnRydXN0LCBJbmMuMUIwQAYDVQQDDDlFbnRy
# dXN0IENvZGUgU2lnbmluZyBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5IC0g
# Q1NCUjEwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCngY/3FEW2YkPy
# 2K7TJV5IT1G/xX2fUBw10dZ+YSqUGW0nRqSmGl33VFFqgCLGqGZ1TVSDyV5oG6v2
# W2Swra0gvVTvRmttAudFrnX2joq5Mi6LuHccUk15iF+lOhjJUCyXJy2/2gB9Y3/v
# MuxGh2Pbmp/DWiE2e/mb1cqgbnIs/OHxnnBNCFYVb5Cr+0i6udfBgniFZS5/tcnA
# 4hS3NxFBBuKK4Kj25X62eAUBw2DtTwdBLgoTSeOQm3/dvfqsv2RR0VybtPVc51z/
# O5uloBrXfQmywrf/bhy8yH3m6Sv8crMU6UpVEoScRCV1HfYq8E+lID1oJethl3wP
# 5bY9867DwRG8G47M4EcwXkIAhnHjWKwGymUfe5SmS1dnDH5erXhnW1XjXuvH2OxM
# bobL89z4n4eqclgSD32m+PhCOTs8LOQyTUmM4OEAwjignPqEPkHcblauxhpb9Gdo
# BQHNG7+uh7ydU/Yu6LZr5JnexU+HWKjSZR7IH9Vybu5ZHFc7CXKd18q3kMbNe0WS
# kUIDTH0/yvKquMIOhvMQn0YupGaGaFpoGHApOBGAYGuKQ6NzbOOzazf/5p1nAZKG
# 3y9I0ftQYNVc/iHTAUJj/u9wtBfAj6ju08FLXxLq/f0uDodEYOOp9MIYo+P9zgyE
# Ig3zp3jak/PbOM+5LzPG/wc8Xr5F0wIDAQABo4IBKzCCAScwDgYDVR0PAQH/BAQD
# AgGGMBIGA1UdEwEB/wQIMAYBAf8CAQEwHQYDVR0lBBYwFAYIKwYBBQUHAwMGCCsG
# AQUFBwMIMDsGA1UdIAQ0MDIwMAYEVR0gADAoMCYGCCsGAQUFBwIBFhpodHRwOi8v
# d3d3LmVudHJ1c3QubmV0L3JwYTAzBggrBgEFBQcBAQQnMCUwIwYIKwYBBQUHMAGG
# F2h0dHA6Ly9vY3NwLmVudHJ1c3QubmV0MDAGA1UdHwQpMCcwJaAjoCGGH2h0dHA6
# Ly9jcmwuZW50cnVzdC5uZXQvZzJjYS5jcmwwHQYDVR0OBBYEFIK61j2Xzp/PceiS
# N6/9s7VpNVfPMB8GA1UdIwQYMBaAFGpyJnrQHu995ztpUdRsjZ+QEmarMA0GCSqG
# SIb3DQEBCwUAA4IBAQAfXkEEtoNwJFMsVXMdZTrA7LR7BJheWTgTCaRZlEJeUL9P
# bG4lIJCTWEAN9Rm0Yu4kXsIBWBUCHRAJb6jU+5J+Nzg+LxR9jx1DNmSzZhNfFMyl
# cfdbIUvGl77clfxwfREc0yHd0CQ5KcX+Chqlz3t57jpv3ty/6RHdFoMI0yyNf02o
# FHkvBWFSOOtg8xRofcuyiq3AlFzkJg4sit1Gw87kVlHFVuOFuE2bRXKLB/GK+0m4
# X9HyloFdaVIk8Qgj0tYjD+uL136LwZNr+vFie1jpUJuXbheIDeHGQ5jXgWG2hZ1H
# 7LGerj8gO0Od2KIc4NR8CMKvdgb4YmZ6tvf6yK81MIIGgzCCBGugAwIBAgIQNa+3
# e500H2r8j4RGqzE1KzANBgkqhkiG9w0BAQ0FADBpMQswCQYDVQQGEwJVUzEWMBQG
# A1UECgwNRW50cnVzdCwgSW5jLjFCMEAGA1UEAww5RW50cnVzdCBDb2RlIFNpZ25p
# bmcgUm9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSAtIENTQlIxMB4XDTIxMDUw
# NzE5MTk1MloXDTQwMTIyOTIzNTkwMFowYzELMAkGA1UEBhMCVVMxFjAUBgNVBAoT
# DUVudHJ1c3QsIEluYy4xPDA6BgNVBAMTM0VudHJ1c3QgRXh0ZW5kZWQgVmFsaWRh
# dGlvbiBDb2RlIFNpZ25pbmcgQ0EgLSBFVkNTMjCCAiIwDQYJKoZIhvcNAQEBBQAD
# ggIPADCCAgoCggIBAL69pznJpX3sXWXx9Cuph9DnrRrFGjsYzuGhUY1y+s5YH1y4
# JEIPRtUxl9BKTeObMMm6l6ic/kU2zyeA53u4bsEkt9+ndNyF8qMkWEXMlJQ7AuvE
# jXxG9VxmguOkwdMfrG4MUyMO1Dr62kLxg1RfNTJW8rV4m1cASB6pYWEnDnMDQ7bW
# cJL71IWaMMaz5ppeS+8dKthmqxZG/wvYD6aJSgJRV0E8QThOl8dRMm1njmahXk2f
# NSKv1Wq3f0BfaDXMafrxBfDqhabqMoXLwcHKg2lFSQbcCWy6SWUZjPm3NyeMZJ41
# 4+Xs5wegnahyvG+FOiymFk49nM8I5oL1RH0owL2JrWwv3C94eRHXHHBL3Z0ITF4u
# +o29p91j9n/wUjGEbjrY2VyFRJ5jBmnQhlh4iZuHu1gcpChsxv5pCpwerBFgal7J
# aWUu7UMtafF4tzstNfKqT+If4wFvkEaq1agNBFegtKzjbb2dGyiAJ0bH2qpnlfHR
# h3vHyCXphAyPiTbSvjPhhcAz1aA8GYuvOPLlk4C/xsOre5PEPZ257kV2wNRobzBe
# PLQ2+ddFQuASBoDbpSH85wV6KI20jmB798i1SkesFGaXoFppcjFXa1OEzWG6cwcV
# cDt7AfynP4wtPYeM+wjX5S8Xg36Cq08J8inhflV3ZZQFHVnUCt2TfuMUXeK7AgMB
# AAGjggErMIIBJzASBgNVHRMBAf8ECDAGAQH/AgEAMB0GA1UdDgQWBBTOiU+CUaoV
# ooRiyjEjYdJh+/j+eDAfBgNVHSMEGDAWgBSCutY9l86fz3Hokjev/bO1aTVXzzAz
# BggrBgEFBQcBAQQnMCUwIwYIKwYBBQUHMAGGF2h0dHA6Ly9vY3NwLmVudHJ1c3Qu
# bmV0MDEGA1UdHwQqMCgwJqAkoCKGIGh0dHA6Ly9jcmwuZW50cnVzdC5uZXQvY3Ni
# cjEuY3JsMA4GA1UdDwEB/wQEAwIBhjATBgNVHSUEDDAKBggrBgEFBQcDAzBEBgNV
# HSAEPTA7MDAGBFUdIAAwKDAmBggrBgEFBQcCARYaaHR0cDovL3d3dy5lbnRydXN0
# Lm5ldC9ycGEwBwYFZ4EMAQMwDQYJKoZIhvcNAQENBQADggIBAD4AVLgq849mr2EW
# xFiTZPRBi2RVjRs1M6GbkdirRsqrX7y+fnDk0tcHqJYH14bRVwoI0NB4Tfgq37IE
# 85rh13zwwQB6wUCh34qMt8u0HQFh8piapt24gwXKqSwW3JwtDv6nl+RQqZeVwUsq
# jFHjxALga3w1TVO8S5QTi1MYFl6mCqe4NMFssess5DF9DCzGfOGkVugtdtWyE3Xq
# gwCuAHfGb6k97mMUgVAW/FtPEhkOWw+N6kvOBkyJS64gzI5HpnXWZe4vMOhdNI8f
# gk1cQqbyFExQIJwJonQkXDnYiTKFPK+M5Wqe5gQ6pRP/qh3NR0suAgW0ao/rhU+B
# 7wrbfZ8pj6XCP1I4UkGVO7w+W1QwQiMJY95QjYk1RfqruA+Poq17ehGT8Y8ohHto
# eUdq6GQpTR/0HS9tHsiUhjzTWpl6a3yrNfcrOUtPuT8Wku8pjI2rrAEazHFEOctA
# PiASzghw40f+3IDXCADRC2rqIbV5ZhfpaqpW3c0VeLEDwBStPkcYde0KU0syk83/
# gLGQ1hPl5EF4Iu1BguUO37DOlSFF5osB0xn39CtVrNlWc2MQ4LigbctUlpigmSFR
# BqqmDDorY8t52kO50hLM3o9VeukJ8+Ka0yXBezaS2uDlUmfN4+ZUCqWd1HOj0y9d
# BmSFA3d/YNjCvHTJlZFot7d+YRl1MIIGtDCCBJygAwIBAgIQDcesVwX/IZkuQEMi
# DDpJhjANBgkqhkiG9w0BAQsFADBiMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGln
# aUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29tMSEwHwYDVQQDExhE
# aWdpQ2VydCBUcnVzdGVkIFJvb3QgRzQwHhcNMjUwNTA3MDAwMDAwWhcNMzgwMTE0
# MjM1OTU5WjBpMQswCQYDVQQGEwJVUzEXMBUGA1UEChMORGlnaUNlcnQsIEluYy4x
# QTA/BgNVBAMTOERpZ2lDZXJ0IFRydXN0ZWQgRzQgVGltZVN0YW1waW5nIFJTQTQw
# OTYgU0hBMjU2IDIwMjUgQ0ExMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKC
# AgEAtHgx0wqYQXK+PEbAHKx126NGaHS0URedTa2NDZS1mZaDLFTtQ2oRjzUXMmxC
# qvkbsDpz4aH+qbxeLho8I6jY3xL1IusLopuW2qftJYJaDNs1+JH7Z+QdSKWM06qc
# hUP+AbdJgMQB3h2DZ0Mal5kYp77jYMVQXSZH++0trj6Ao+xh/AS7sQRuQL37QXbD
# hAktVJMQbzIBHYJBYgzWIjk8eDrYhXDEpKk7RdoX0M980EpLtlrNyHw0Xm+nt5pn
# YJU3Gmq6bNMI1I7Gb5IBZK4ivbVCiZv7PNBYqHEpNVWC2ZQ8BbfnFRQVESYOszFI
# 2Wv82wnJRfN20VRS3hpLgIR4hjzL0hpoYGk81coWJ+KdPvMvaB0WkE/2qHxJ0ucS
# 638ZxqU14lDnki7CcoKCz6eum5A19WZQHkqUJfdkDjHkccpL6uoG8pbF0LJAQQZx
# st7VvwDDjAmSFTUms+wV/FbWBqi7fTJnjq3hj0XbQcd8hjj/q8d6ylgxCZSKi17y
# Vp2NL+cnT6Toy+rN+nM8M7LnLqCrO2JP3oW//1sfuZDKiDEb1AQ8es9Xr/u6bDTn
# YCTKIsDq1BtmXUqEG1NqzJKS4kOmxkYp2WyODi7vQTCBZtVFJfVZ3j7OgWmnhFr4
# yUozZtqgPrHRVHhGNKlYzyjlroPxul+bgIspzOwbtmsgY1MCAwEAAaOCAV0wggFZ
# MBIGA1UdEwEB/wQIMAYBAf8CAQAwHQYDVR0OBBYEFO9vU0rp5AZ8esrikFb2L9RJ
# 7MtOMB8GA1UdIwQYMBaAFOzX44LScV1kTN8uZz/nupiuHA9PMA4GA1UdDwEB/wQE
# AwIBhjATBgNVHSUEDDAKBggrBgEFBQcDCDB3BggrBgEFBQcBAQRrMGkwJAYIKwYB
# BQUHMAGGGGh0dHA6Ly9vY3NwLmRpZ2ljZXJ0LmNvbTBBBggrBgEFBQcwAoY1aHR0
# cDovL2NhY2VydHMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0VHJ1c3RlZFJvb3RHNC5j
# cnQwQwYDVR0fBDwwOjA4oDagNIYyaHR0cDovL2NybDMuZGlnaWNlcnQuY29tL0Rp
# Z2lDZXJ0VHJ1c3RlZFJvb3RHNC5jcmwwIAYDVR0gBBkwFzAIBgZngQwBBAIwCwYJ
# YIZIAYb9bAcBMA0GCSqGSIb3DQEBCwUAA4ICAQAXzvsWgBz+Bz0RdnEwvb4LyLU0
# pn/N0IfFiBowf0/Dm1wGc/Do7oVMY2mhXZXjDNJQa8j00DNqhCT3t+s8G0iP5kvN
# 2n7Jd2E4/iEIUBO41P5F448rSYJ59Ib61eoalhnd6ywFLerycvZTAz40y8S4F3/a
# +Z1jEMK/DMm/axFSgoR8n6c3nuZB9BfBwAQYK9FHaoq2e26MHvVY9gCDA/JYsq7p
# GdogP8HRtrYfctSLANEBfHU16r3J05qX3kId+ZOczgj5kjatVB+NdADVZKON/gnZ
# ruMvNYY2o1f4MXRJDMdTSlOLh0HCn2cQLwQCqjFbqrXuvTPSegOOzr4EWj7PtspI
# HBldNE2K9i697cvaiIo2p61Ed2p8xMJb82Yosn0z4y25xUbI7GIN/TpVfHIqQ6Ku
# /qjTY6hc3hsXMrS+U0yy+GWqAXam4ToWd2UQ1KYT70kZjE4YtL8Pbzg0c1ugMZyZ
# Zd/BdHLiRu7hAWE6bTEm4XYRkA6Tl4KSFLFk43esaUeqGkH/wyW4N7OigizwJWeu
# kcyIPbAvjSabnf7+Pu0VrFgoiovRDiyx3zEdmcif/sYQsfch28bZeUz2rtY/9TCA
# 6TD8dC3JE3rYkrhLULy7Dc90G6e8BlqmyIjlgp2+VqsS9/wQD7yFylIz0scmbKvF
# oW2jNrbM1pD2T7m3XDCCBu0wggTVoAMCAQICEAqA7xhLjfEFgtHEdqeVdGgwDQYJ
# KoZIhvcNAQELBQAwaTELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDkRpZ2lDZXJ0LCBJ
# bmMuMUEwPwYDVQQDEzhEaWdpQ2VydCBUcnVzdGVkIEc0IFRpbWVTdGFtcGluZyBS
# U0E0MDk2IFNIQTI1NiAyMDI1IENBMTAeFw0yNTA2MDQwMDAwMDBaFw0zNjA5MDMy
# MzU5NTlaMGMxCzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5EaWdpQ2VydCwgSW5jLjE7
# MDkGA1UEAxMyRGlnaUNlcnQgU0hBMjU2IFJTQTQwOTYgVGltZXN0YW1wIFJlc3Bv
# bmRlciAyMDI1IDEwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDQRqwt
# Esae0OquYFazK1e6b1H/hnAKAd/KN8wZQjBjMqiZ3xTWcfsLwOvRxUwXcGx8AUjn
# i6bz52fGTfr6PHRNv6T7zsf1Y/E3IU8kgNkeECqVQ+3bzWYesFtkepErvUSbf+EI
# YLkrLKd6qJnuzK8Vcn0DvbDMemQFoxQ2Dsw4vEjoT1FpS54dNApZfKY61HAldytx
# NM89PZXUP/5wWWURK+IfxiOg8W9lKMqzdIo7VA1R0V3Zp3DjjANwqAf4lEkTlCDQ
# 0/fKJLKLkzGBTpx6EYevvOi7XOc4zyh1uSqgr6UnbksIcFJqLbkIXIPbcNmA98Os
# kkkrvt6lPAw/p4oDSRZreiwB7x9ykrjS6GS3NR39iTTFS+ENTqW8m6THuOmHHjQN
# C3zbJ6nJ6SXiLSvw4Smz8U07hqF+8CTXaETkVWz0dVVZw7knh1WZXOLHgDvundrA
# tuvz0D3T+dYaNcwafsVCGZKUhQPL1naFKBy1p6llN3QgshRta6Eq4B40h5avMcpi
# 54wm0i2ePZD5pPIssoszQyF4//3DoK2O65Uck5Wggn8O2klETsJ7u8xEehGifgJY
# i+6I03UuT1j7FnrqVrOzaQoVJOeeStPeldYRNMmSF3voIgMFtNGh86w3ISHNm0Ia
# adCKCkUe2LnwJKa8TIlwCUNVwppwn4D3/Pt5pwIDAQABo4IBlTCCAZEwDAYDVR0T
# AQH/BAIwADAdBgNVHQ4EFgQU5Dv88jHt/f3X85FxYxlQQ89hjOgwHwYDVR0jBBgw
# FoAU729TSunkBnx6yuKQVvYv1Ensy04wDgYDVR0PAQH/BAQDAgeAMBYGA1UdJQEB
# /wQMMAoGCCsGAQUFBwMIMIGVBggrBgEFBQcBAQSBiDCBhTAkBggrBgEFBQcwAYYY
# aHR0cDovL29jc3AuZGlnaWNlcnQuY29tMF0GCCsGAQUFBzAChlFodHRwOi8vY2Fj
# ZXJ0cy5kaWdpY2VydC5jb20vRGlnaUNlcnRUcnVzdGVkRzRUaW1lU3RhbXBpbmdS
# U0E0MDk2U0hBMjU2MjAyNUNBMS5jcnQwXwYDVR0fBFgwVjBUoFKgUIZOaHR0cDov
# L2NybDMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0VHJ1c3RlZEc0VGltZVN0YW1waW5n
# UlNBNDA5NlNIQTI1NjIwMjVDQTEuY3JsMCAGA1UdIAQZMBcwCAYGZ4EMAQQCMAsG
# CWCGSAGG/WwHATANBgkqhkiG9w0BAQsFAAOCAgEAZSqt8RwnBLmuYEHs0QhEnmNA
# ciH45PYiT9s1i6UKtW+FERp8FgXRGQ/YAavXzWjZhY+hIfP2JkQ38U+wtJPBVBaj
# YfrbIYG+Dui4I4PCvHpQuPqFgqp1PzC/ZRX4pvP/ciZmUnthfAEP1HShTrY+2DE5
# qjzvZs7JIIgt0GCFD9ktx0LxxtRQ7vllKluHWiKk6FxRPyUPxAAYH2Vy1lNM4kze
# kd8oEARzFAWgeW3az2xejEWLNN4eKGxDJ8WDl/FQUSntbjZ80FU3i54tpx5F/0Kr
# 15zW/mJAxZMVBrTE2oi0fcI8VMbtoRAmaaslNXdCG1+lqvP4FbrQ6IwSBXkZagHL
# hFU9HCrG/syTRLLhAezu/3Lr00GrJzPQFnCEH1Y58678IgmfORBPC1JKkYaEt2Od
# Dh4GmO0/5cHelAK2/gTlQJINqDr6JfwyYHXSd+V08X1JUPvB4ILfJdmL+66Gp3CS
# BXG6IwXMZUXBhtCyIaehr0XkBoDIGMUG1dUtwq1qmcwbdUfcSYCn+OwncVUXf53V
# JUNOaMWMts0VlRYxe5nK+At+DI96HAlXHAL5SlfYxJ7La54i71McVWRP66bW+yER
# NpbJCjyCYG2j+bdpxo/1Cy4uPcU3AWVPGrbn5PhDBf3Froguzzhk++ami+r3Qrx5
# bIbY3TVzgiFI7Gq3zWcwggb3MIIE36ADAgECAhBUqhzmzdht2UDqAdaKxc8tMA0G
# CSqGSIb3DQEBCwUAMGMxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1FbnRydXN0LCBJ
# bmMuMTwwOgYDVQQDEzNFbnRydXN0IEV4dGVuZGVkIFZhbGlkYXRpb24gQ29kZSBT
# aWduaW5nIENBIC0gRVZDUzIwHhcNMjMxMTExMDIzNDE2WhcNMjYxMTExMDIzNDE1
# WjCB0jELMAkGA1UEBhMCVVMxETAPBgNVBAgTCE5ldyBZb3JrMQ8wDQYDVQQHEwZS
# b3NseW4xEzARBgsrBgEEAYI3PAIBAxMCVVMxGTAXBgsrBgEEAYI3PAIBAhMITmV3
# IFlvcmsxHjAcBgNVBAoTFUxJU1MgQ29uc3VsdGluZyBDb3JwLjEdMBsGA1UEDxMU
# UHJpdmF0ZSBPcmdhbml6YXRpb24xEDAOBgNVBAUTBzEyMDM3MDQxHjAcBgNVBAMT
# FUxJU1MgQ29uc3VsdGluZyBDb3JwLjCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCC
# AgoCggIBAKNsPm91CdVgVgztDDAOq1XypBtPfEFiuIjryZikaVh3+iPoixSaa4MA
# MZe8gR//KdBwqwyfKHRLj79VfmtcRQJtcuuzfdRXlmnvZOcfwOuhnl7dp3ZyON2B
# +m/wTxvRulpTfOf7Xa/XD+vseSMZk5Cr3VGs5c8CnfFPxboSGjxPI5iNfEe/hJvI
# BS/aYVL/sZqNdCqarwUCC0YuaVCbOiOlpV1h3hfrVQq9eB5FVI8u7YRh0jetAt96
# LoiYwXxmLdxXtMHAZPhLCfJndTVwOgo6P08j+BFViHtHZGOLgH9gC32OPZvGAM69
# IoessdwAK31fBO/alVk2TBnjjaCMiLD7goDYIP9GzDE+o8rO8pcyse4a1s+uF4By
# DiotV0/3L1XFneFA9llG1PgmpU0P7myHJGa2BTUuNcZ5NVNEdINGCg3rDEb2oRje
# ukOn83iRtsTnV8kdd4BXuEFptjNqj9M6fvk+LJxsZZ7pKaNGlugPH/hb93+2WXd3
# ImzPCLBOQBs9Ms7rgjlGzfZP/cTJibogaYNYhb6mblEHpm5UhBNrJk9ONRNfDjDB
# Lz7eeAWtZGHerL3vpaBHCC4QA1aIKMmolnXjBCAsEhqbJnKZEb/fVjfU7fX5/TQJ
# lu+w6AZ6y4rBITex0QMGUlcYh1pnQf0tTikfyH250Gyr1pBaD1rvAgMBAAGjggE1
# MIIBMTAMBgNVHRMBAf8EAjAAMB0GA1UdDgQWBBS8w1GaCwrKaNGXgF9k+1HbSv8Y
# uTAfBgNVHSMEGDAWgBTOiU+CUaoVooRiyjEjYdJh+/j+eDBnBggrBgEFBQcBAQRb
# MFkwIwYIKwYBBQUHMAGGF2h0dHA6Ly9vY3NwLmVudHJ1c3QubmV0MDIGCCsGAQUF
# BzAChiZodHRwOi8vYWlhLmVudHJ1c3QubmV0L2V2Y3MyLWNoYWluLnA3YzAxBgNV
# HR8EKjAoMCagJKAihiBodHRwOi8vY3JsLmVudHJ1c3QubmV0L2V2Y3MyLmNybDAO
# BgNVHQ8BAf8EBAMCB4AwEwYDVR0lBAwwCgYIKwYBBQUHAwMwIAYDVR0gBBkwFzAH
# BgVngQwBAzAMBgpghkgBhvpsCgECMA0GCSqGSIb3DQEBCwUAA4ICAQBoCI/Q8Bgz
# wIuP21o96o9uMEbhUfRQ6JBB2/1jfNHJewHbMk9D3ftAEYj7nJSWeLpk8TOSPeeg
# tpsG8BEj3KZxDKg08jxWcDMCi0SBh31I3gMQowFh8fD3QjgMpb4gW5r9TZttLn2G
# txzBuoamhesLb3Bfr492InciZbSXgipiaKUa5ocj1mOuo9Y8I/SlN8yhuREULW59
# JsvWwcNDInmTyxNuQ/4HoeBzXn7I3CY+rlm4aXOmnhE3Fbe3jINEFkCIROTOQ+Ps
# gFlOFaz0gGuT8gfmSxiCrMzE90Nfucuay/RxCRsh9Xqu9uxyHCQCuJ4gvvGj431f
# UpCOAzRM/ogk9Udna8Gs22tmCrfMQAT+KNtuewT0EYH4qqpkrAxm9RQwUk7cMG35
# ebua7D3pe4OwKe8TldRibPxKBMWueJll+Ku/jWRIL2urhwD1wqZtguYqoLqXHWQR
# bd7nt60I+VxIusDiK80OyHXK7gAy1ibC7eAlpaOTOcJ92RAX6cIzKmutaxZLNZtl
# u6n/aSBs7saPOb+848VgadEmBXQzOyRspay8JwQ+7C5Tuqa8/S7Qr6yKD7Sosm31
# ZOk8v59Oy+0Q4YiO0gkua/yZpnxGeutJVYteE8t7muhHk3zoGkMmG/K6CvxK3rxz
# LuvDI0xr73Ai+rIuifNtzu4NvT8hBzGkwTGCBlMwggZPAgEBMHcwYzELMAkGA1UE
# BhMCVVMxFjAUBgNVBAoTDUVudHJ1c3QsIEluYy4xPDA6BgNVBAMTM0VudHJ1c3Qg
# RXh0ZW5kZWQgVmFsaWRhdGlvbiBDb2RlIFNpZ25pbmcgQ0EgLSBFVkNTMgIQVKoc
# 5s3YbdlA6gHWisXPLTANBglghkgBZQMEAgEFAKCBhDAYBgorBgEEAYI3AgEMMQow
# CKACgAChAoAAMBkGCSqGSIb3DQEJAzEMBgorBgEEAYI3AgEEMBwGCisGAQQBgjcC
# AQsxDjAMBgorBgEEAYI3AgEVMC8GCSqGSIb3DQEJBDEiBCAPXHDK04irLAUDykNU
# xkX//DpO3DL5OLCxfeNIgfwwajANBgkqhkiG9w0BAQEFAASCAgA2kMdFj47xAWgJ
# Q4kIqQBBQ94MxHgQayQSpeqdNMNn9gqtXq+1ZuTaNkYq93JY7ZF46PEFNIrlb8Et
# 52FJHKDBI75LHrcwVRzC3BeaLshRqztP501AuNQ3ORI4q5AGSojdAp8Vl/eb0T30
# 7lwBl9CN3Q9ZGHwq/FgxgPIF8KTzoH4AoYxHdQQH/sRafSoFqubrPa0aQdV/MrgV
# z1xKIQg54l/r97lzXnM19S7T2jM4UIYPcWG+9FoOe7RKTDXRm30LkBC3r+CwaanU
# whuzNdBlTU3z9tFNZlfqPLox6MuvnCG5dq+n7fysohJb8Oz+Z1+TZ2DmQ0bcKr1z
# bQc1unS7His/OVJRPobU5EGG/jdNtataA1ji0QSQYT126F9AihRjRRhcSasbIRje
# Rr3vgJy/QaYmnZNvGWae7hYipD2F0X4512dQHl0vB/ry3rW702CwXZbYFb/gf/8N
# I7X6ZkyWGpsSJClC1QolWre/XHbY3E0NcEC2SsAwunLLtwpSfKNp5plNYioEFiJi
# l05z6Pzxv+mvk20QfInYCr1NeMfXbjgWutn0gu+uY6UOg5VoZMAa0wqb2X/qo9MT
# zaJFGJ+IfAxqrVwVUXVeJTqYPybiFuAHEO1B9Gx3JgqdMmbmKH5FQy6zE1Hrkh1s
# UBHlqdi5ZLzCaHiXEOVj8xXATK5GYqGCAyYwggMiBgkqhkiG9w0BCQYxggMTMIID
# DwIBATB9MGkxCzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5EaWdpQ2VydCwgSW5jLjFB
# MD8GA1UEAxM4RGlnaUNlcnQgVHJ1c3RlZCBHNCBUaW1lU3RhbXBpbmcgUlNBNDA5
# NiBTSEEyNTYgMjAyNSBDQTECEAqA7xhLjfEFgtHEdqeVdGgwDQYJYIZIAWUDBAIB
# BQCgaTAYBgkqhkiG9w0BCQMxCwYJKoZIhvcNAQcBMBwGCSqGSIb3DQEJBTEPFw0y
# NjA0MDMyMzQ4NTVaMC8GCSqGSIb3DQEJBDEiBCBGdpNXgba1cCP8yLJ6rLhDM0i2
# XVumDROhuc4FdLgo3DANBgkqhkiG9w0BAQEFAASCAgAVns5ToJv3FQ60XV2qk3QU
# MRkTsCdRf1H2KV3zgPeDHHquR7EsNQm875cxZXsslyQHo58Vx1Bns/jcfQJ0MCU0
# ElnYkjNpHRgiabrWnXf/2s3bxDOnvpQX1dOyKAsIYmKq2/D0z/Bl6g0f+Gavi8ov
# HWhuI1cLnE3tLaA2YqVfkz6Buy31uX1yVy4gH38y6KJb5no5CxW6deSD/3RFdj3A
# Q/ct7OzEhfs4N7z8Q+AE02rhDnYIotfEPquNGO4drOcrMAqJ/RoDHZU/w4Flv0zW
# qGQ3q2ApwyGKyaf4fxERfbUUAYqrgT3yuwQPFQ9hkf/nZtuLyjnWigC4mC+sbC00
# Z+fqp/aJQwyCyurX8/Ik/PojnrbQILLTzyfMF9OqMt6DYvYWWKOgMiu26b3lxuK5
# qwddAPWpx5+zf38PFWUc7TOD49ETDEC5WXuyNX6AajYOmqoVRoLsRfWhaOn9C4WI
# c6Plk+AECYmNEy/22SKZKfdKRNbRvvmSDU9uAxZPbS5l0zZGS+Gd/AnNUUS9wnQe
# j6LGb67rQVgLMkCqM1Ppi53/9dV/S8v7wwkVmCjRpnub7reMsE+n+ik1jxPTVFiw
# smX2Uc8blR0TS0Y+D81+aEyFZkio1oqWN0QYo6YzM6YUavMjyulZf069MsukOcwJ
# vgyU3AgmVd/Et57zYreplA==
# SIG # End signature block