DownloadSdk.psm1

#########################################################################################
#
# Copyright (c) Microsoft Corporation. All rights reserved.
#
# Download SDK Module
#
#########################################################################################

#requires -runasadministrator

#region Module Constants

$script:moduleName   = "DownloadSdk"
$script:eventLogName = "AKSHCI"
$script:logFilePath  = $($env:ProgramData + "\downloadsdk\downloadsdk.log")

#endregion

Import-LocalizedData -BindingVariable "DwldSdkLocMessage" -FileName DownloadSdkLocalizationMessages

#region Exported Functions

function Get-DownloadSdkCatalog
{
    <#
    .DESCRIPTION
        Calls into the Download SDK to retrieve the requested catalog.
        Returns the catalog content.
 
    .PARAMETER Provider
        Provider to use for the download (e.g. sfs).
 
    .PARAMETER Name
        Content name to retrieve the catalog for.
 
    .PARAMETER Audience
        Audience/ring to retrieve the catalog for. Default/empty is public.
 
    .PARAMETER Endpoint
        Specifies custom endpoint information for the download.
 
    .PARAMETER Timeout
        Timeout for the API call.
    #>


    [CmdletBinding()]
    param(
        [Parameter()]
        [String] $Provider = "sfs",

        [Parameter(Mandatory=$true)]
        [String] $Name,

        [String] $Audience = "",

        [Parameter()]
        [String] $Endpoint = "",

        [Parameter()]
        [String] $Timeout = "10m"
    )

    trap
    {  
        Write-ErrorEvent -message $_
        if ($ErrorActionPreference -ne [System.Management.Automation.ActionPreference]::SilentlyContinue) { throw $_ }
    }

    $verboseOutput = ($VerbosePreference -eq [System.Management.Automation.ActionPreference]::Continue)

    $encoding = [system.Text.Encoding]::UTF8
    $providerBytes = $encoding.GetBytes($Provider)
    $timeoutBytes = $encoding.GetBytes($Timeout)
    $nameBytes = $encoding.GetBytes($Name)
    $audienceBytes = $encoding.GetBytes($Audience)
    $endpointBytes = $encoding.GetBytes($Endpoint)
    $output = New-Object System.IntPtr

    Write-Event -message $("Invoking GetCatalog - Name: $Name , Audience: $Audience , Endpoint: $Endpoint , Timeout: $Timeout")
    $err = [DownloadSDK.API]::GetCatalog($providerBytes, $nameBytes, $audienceBytes, $endpointBytes, $timeoutBytes, $verboseOutput, [ref]$output)

    return (Test-DownloadSDKResponse -Context "GetCatalog" -Err $err -Output $output)
}

function Get-DownloadSdkRelease
{
    <#
    .DESCRIPTION
        Calls into the Download SDK to download a release (e.g. images, packages, generic files, etc).
        Returns information about the downloaded release.
 
    .PARAMETER Provider
        Provider to use for the download (e.g. sfs).
 
    .PARAMETER Name
        Release name to be downloaded.
 
    .PARAMETER Version
        Release version to be downloaded. If unspecified, the latest version is downloaded.
 
    .PARAMETER Destination
        Destination directory to place the downloaded release files.
 
    .PARAMETER CatalogName
        Catalog name
 
    .PARAMETER Audience
        Audience/ring
 
    .PARAMETER Endpoint
        Specifies custom endpoint information for the download.
 
    .PARAMETER Parts
        How many parts to segment content downloads into (causes concurrent connections to the hosting server).
 
    .PARAMETER Timeout
        Timeout for the API call (e.g. 2h).
    #>


    [CmdletBinding()]
    param(
        [Parameter()]
        [String] $Provider = "sfs",

        [Parameter(Mandatory=$true)]
        [String] $Name,

        [Parameter()]
        [String] $Version,

        [Parameter()]
        [String] $Destination,

        [Parameter()]
        [String] $CatalogName,

        [Parameter()]
        [String] $Audience,

        [Parameter()]
        [String] $Endpoint = "",

        [Parameter()]
        [ValidateRange(1,10)]
        [Int] $Parts = 3,

        [Parameter()]
        [String] $Timeout = "2h"
    )

    trap
    {  
        Write-ErrorEvent -message $_
        if ($ErrorActionPreference -ne [System.Management.Automation.ActionPreference]::SilentlyContinue) { throw $_ }
    }

    $verboseOutput = ($VerbosePreference -eq [System.Management.Automation.ActionPreference]::Continue)
    
    $encoding = [system.Text.Encoding]::UTF8
    $providerBytes = $encoding.GetBytes($Provider)
    $timeoutBytes = $encoding.GetBytes($Timeout)
    $nameBytes = $encoding.GetBytes($Name)
    $versionBytes = $encoding.GetBytes($Version)
    $destinationBytes = $encoding.GetBytes($Destination)
    $catalogBytes = $encoding.GetBytes($CatalogName)
    $audienceBytes = $encoding.GetBytes($Audience)
    $endpointBytes = $encoding.GetBytes($Endpoint)
    $output = New-Object System.IntPtr

    Write-Event -message $("Invoking GetRelease - Name: $Name , Version: $Version , Destination: $Destination , Catalog: $CatalogName , Audience: $Audience , Endpoint: $Endpoint , Parts: $Parts , Timeout: $Timeout")
    $err = [DownloadSDK.API]::GetRelease($providerBytes, $nameBytes, $versionBytes, $destinationBytes, $catalogBytes, $audienceBytes, $endpointBytes, $Parts, $timeoutBytes, $verboseOutput, [ref]$output)

    return (Test-DownloadSDKResponse -Context "GetRelease" -Err $err -Output $output)
}

function Set-DownloadSdkProxy
{
    <#
    .DESCRIPTION
        Configures the environment with proxy server settings
 
    .PARAMETER Http
        HTTP proxy server configuration
 
    .PARAMETER Https
        HTTPS proxy server configuration
 
    .PARAMETER NoProxy
        Proxy server exemption list (no_proxy)
 
    .PARAMETER CerticateFile
        Path to a CA certificate file used to establish trust with a HTTPS proxy server
 
    .PARAMETER CertificateStore
        The certificate store to use for import of the CertificateFile. Default is "Cert:\CurrentUser\Root"
 
    .PARAMETER Scope
        The scope of the proxy settings, i.e. current process vs. current user vs. machine wide. Default is current process.
    #>


    [CmdletBinding()]
    param (
        [Parameter()]
        [String] $Http,

        [Parameter()]
        [String] $Https,

        [Parameter()]
        [String] $NoProxy,

        [Parameter()]
        [String] $CertificateFile,

        [Parameter()]
        [String] $CertificateStore = "Cert:\CurrentUser\Root",

        [Parameter()]
        [EnvironmentVariableTarget] $Scope = [EnvironmentVariableTarget]::Process
    )

    trap
    {  
        Write-ErrorEvent -message $_
        if ($ErrorActionPreference -ne [System.Management.Automation.ActionPreference]::SilentlyContinue) { throw $_ }
    }

    if ($CertificateFile -and $CertificateStore)
    {
        if (-not (Test-Path -Path $CertificateFile))
        {
            throw $([System.String]::Format([System.Globalization.CultureInfo]::InvariantCulture, $DwldSdkLocMessage.downloadsdk_certificate_not_found, $CertificateFile))
        }

        Import-Certificate -FilePath $CertificateFile -CertStoreLocation $CertificateStore | Out-Null
    }

    [Environment]::SetEnvironmentVariable("http_proxy", $Http, $Scope)
    [Environment]::SetEnvironmentVariable("https_proxy", $Https, $Scope)
    [Environment]::SetEnvironmentVariable("no_proxy", $NoProxy, $Scope)
}

function Get-DownloadSdkEventLog
{
    <#
    .DESCRIPTION
        Returns all of the DownloadSdk module events
    #>


    [CmdletBinding()]
    param()

    Get-WinEvent -ProviderName $script:moduleName -ErrorAction SilentlyContinue
}

function Get-DownloadSdkLogs
{
    <#
    .DESCRIPTION
        Collects DownloadSdk logs
 
    .PARAMETER Path
        Path to store the logs
    #>


    [CmdletBinding()]
    param (
        [Parameter()]
        [String] $Path
    )

    $logDir = [io.Path]::Combine($Path, $script:moduleName)
    New-Item -ItemType Directory -Force -Path $logDir | Out-Null

    Get-Command -Module $script:moduleName | Sort-Object -Property Source > $($logDir+"\moduleinfo.txt")

    if (Test-Path -Path $script:logFilePath)
    {
        Copy-Item -Path $script:logFilePath -Destination $logDir
    }
}

#endregion

#region Helper functions

function Write-ErrorEvent
{
    <#
    .DESCRIPTION
        Writes an error event to the Download SDK event log.
 
    .PARAMETER message
        The error message to be logged.
    #>


    param (
        [Parameter(Mandatory=$true)]
        [String] $message
    )

    Write-EventLog -LogName $script:eventLogName -Source $script:moduleName -EventID 100 -EntryType Error -Message $message
}

function Write-Event
{
    <#
    .DESCRIPTION
        Writes a regular event to the Download SDK event log.
 
    .PARAMETER message
        The message to be logged.
    #>


    param (
        [Parameter(Mandatory=$true)]
        [String] $message
    )

    Write-EventLog -LogName $script:eventLogName -Source $script:moduleName -EventID 1 -EntryType Information -Message $message
}

#endregion

#region Download SDK Wrappers

function Initialize-DownloadSDK
{
    <#
    .DESCRIPTION
        Initialize the DownloadSDK types (once per session).
    #>


    try
    {
        [DownloadSDK.API] | Out-Null
    }
    catch
    {
        $path = $($MyInvocation.PSScriptRoot) -replace [RegEx]::Escape("\"), "\\"

        $signature = @"
[DllImport("$path\\downloadsdk.dll")]
public static extern IntPtr GetCatalog([In] byte[] provider, [In] byte[] name, [In] byte[] audience, [In] byte[] endpoint, [In] byte[] timeout, [In] bool verbose, ref IntPtr output);
 
[DllImport("$path\\downloadsdk.dll")]
public static extern IntPtr GetRelease([In] byte[] provider, [In] byte[] name, [In] byte[] version, [In] byte[] destination, [In] byte[] catalog, [In] byte[] audience, [In] byte[] endpoint, [In] byte parts, [In] byte[] timeout, [In] bool verbose, ref IntPtr output);
 
[DllImport("kernel32.dll", SetLastError = true)]
public static extern bool SetHandleInformation(IntPtr hObject, uint dwMask, uint dwFlags);
 
[DllImport("kernel32.dll", SetLastError = true)]
public static extern IntPtr GetStdHandle(int nStdHandle);
"@


        Add-Type -MemberDefinition $signature -Name API -Namespace DownloadSDK
    }
}

function Test-DownloadSDKResponse
{
    <#
    .DESCRIPTION
        Tests the response from a DownloadSDK API call to see if it failed or succeeded.
        In the success case, it will return an object from the response JSON output.
        In the failure case, it will throw an exception with relevant information.
 
    .PARAMETER Err
        The Err response returned by a API call.
 
    .PARAMETER Output
        The JSON output returned by a API call.
 
    .PARAMETER Context
        A context string to provide context to our event logging of which API call failed.
    #>


    param(
        [System.IntPtr] $Err,
        [System.IntPtr] $Output,
        [String] $Context
    )

    # Temporary call, see notes in function description
    Repair-Console

    if ($Err -ne [System.Intptr]::Zero)
    {
        $errString = [System.Runtime.InteropServices.Marshal]::PtrToStringAnsi($Err)
        throw $([System.String]::Format([System.Globalization.CultureInfo]::InvariantCulture, $DwldSdkLocMessage.downloadsdk_error_by_api_call, $Context, $errString))        
    }
    
    if ($Output -ne [System.Intptr]::Zero)
    {
        $outputStr = [System.Runtime.InteropServices.Marshal]::PtrToStringAnsi($Output)
        return ($outputStr | ConvertFrom-Json)
    }

    return $null
}

function Repair-Console
{
    <#
    .DESCRIPTION
        Repairs console stdin/stdout/stderr after a DownloadSDK API call.
        This is a known golang c-shared bug which is being fixed in Go: https://github.com/golang/go/issues/44876
    #>


    try
    {
        [DownloadSDK.API]::SetHandleInformation([DownloadSDK.API]::GetStdHandle(-10), 1, 1) | Out-Null
        [DownloadSDK.API]::SetHandleInformation([DownloadSDK.API]::GetStdHandle(-11), 1, 1) | Out-Null
        [DownloadSDK.API]::SetHandleInformation([DownloadSDK.API]::GetStdHandle(-12), 1, 1) | Out-Null
    }
    catch {}
}

#endregion

#region Module initialization

New-EventLog -LogName $script:eventLogName -Source $script:moduleName -ErrorAction Ignore

Initialize-DownloadSDK

#endregion

# SIG # Begin signature block
# MIInoQYJKoZIhvcNAQcCoIInkjCCJ44CAQExDzANBglghkgBZQMEAgEFADB5Bgor
# BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG
# KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCD2duL58kRv3xCY
# bhS2RZl0asWsJqKpWVrODer7/o13u6CCDYEwggX/MIID56ADAgECAhMzAAACUosz
# qviV8znbAAAAAAJSMA0GCSqGSIb3DQEBCwUAMH4xCzAJBgNVBAYTAlVTMRMwEQYD
# VQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNy
# b3NvZnQgQ29ycG9yYXRpb24xKDAmBgNVBAMTH01pY3Jvc29mdCBDb2RlIFNpZ25p
# bmcgUENBIDIwMTEwHhcNMjEwOTAyMTgzMjU5WhcNMjIwOTAxMTgzMjU5WjB0MQsw
# CQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9u
# ZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMR4wHAYDVQQDExVNaWNy
# b3NvZnQgQ29ycG9yYXRpb24wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB
# AQDQ5M+Ps/X7BNuv5B/0I6uoDwj0NJOo1KrVQqO7ggRXccklyTrWL4xMShjIou2I
# sbYnF67wXzVAq5Om4oe+LfzSDOzjcb6ms00gBo0OQaqwQ1BijyJ7NvDf80I1fW9O
# L76Kt0Wpc2zrGhzcHdb7upPrvxvSNNUvxK3sgw7YTt31410vpEp8yfBEl/hd8ZzA
# v47DCgJ5j1zm295s1RVZHNp6MoiQFVOECm4AwK2l28i+YER1JO4IplTH44uvzX9o
# RnJHaMvWzZEpozPy4jNO2DDqbcNs4zh7AWMhE1PWFVA+CHI/En5nASvCvLmuR/t8
# q4bc8XR8QIZJQSp+2U6m2ldNAgMBAAGjggF+MIIBejAfBgNVHSUEGDAWBgorBgEE
# AYI3TAgBBggrBgEFBQcDAzAdBgNVHQ4EFgQUNZJaEUGL2Guwt7ZOAu4efEYXedEw
# UAYDVR0RBEkwR6RFMEMxKTAnBgNVBAsTIE1pY3Jvc29mdCBPcGVyYXRpb25zIFB1
# ZXJ0byBSaWNvMRYwFAYDVQQFEw0yMzAwMTIrNDY3NTk3MB8GA1UdIwQYMBaAFEhu
# ZOVQBdOCqhc3NyK1bajKdQKVMFQGA1UdHwRNMEswSaBHoEWGQ2h0dHA6Ly93d3cu
# bWljcm9zb2Z0LmNvbS9wa2lvcHMvY3JsL01pY0NvZFNpZ1BDQTIwMTFfMjAxMS0w
# Ny0wOC5jcmwwYQYIKwYBBQUHAQEEVTBTMFEGCCsGAQUFBzAChkVodHRwOi8vd3d3
# Lm1pY3Jvc29mdC5jb20vcGtpb3BzL2NlcnRzL01pY0NvZFNpZ1BDQTIwMTFfMjAx
# MS0wNy0wOC5jcnQwDAYDVR0TAQH/BAIwADANBgkqhkiG9w0BAQsFAAOCAgEAFkk3
# uSxkTEBh1NtAl7BivIEsAWdgX1qZ+EdZMYbQKasY6IhSLXRMxF1B3OKdR9K/kccp
# kvNcGl8D7YyYS4mhCUMBR+VLrg3f8PUj38A9V5aiY2/Jok7WZFOAmjPRNNGnyeg7
# l0lTiThFqE+2aOs6+heegqAdelGgNJKRHLWRuhGKuLIw5lkgx9Ky+QvZrn/Ddi8u
# TIgWKp+MGG8xY6PBvvjgt9jQShlnPrZ3UY8Bvwy6rynhXBaV0V0TTL0gEx7eh/K1
# o8Miaru6s/7FyqOLeUS4vTHh9TgBL5DtxCYurXbSBVtL1Fj44+Od/6cmC9mmvrti
# yG709Y3Rd3YdJj2f3GJq7Y7KdWq0QYhatKhBeg4fxjhg0yut2g6aM1mxjNPrE48z
# 6HWCNGu9gMK5ZudldRw4a45Z06Aoktof0CqOyTErvq0YjoE4Xpa0+87T/PVUXNqf
# 7Y+qSU7+9LtLQuMYR4w3cSPjuNusvLf9gBnch5RqM7kaDtYWDgLyB42EfsxeMqwK
# WwA+TVi0HrWRqfSx2olbE56hJcEkMjOSKz3sRuupFCX3UroyYf52L+2iVTrda8XW
# esPG62Mnn3T8AuLfzeJFuAbfOSERx7IFZO92UPoXE1uEjL5skl1yTZB3MubgOA4F
# 8KoRNhviFAEST+nG8c8uIsbZeb08SeYQMqjVEmkwggd6MIIFYqADAgECAgphDpDS
# AAAAAAADMA0GCSqGSIb3DQEBCwUAMIGIMQswCQYDVQQGEwJVUzETMBEGA1UECBMK
# V2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0
# IENvcnBvcmF0aW9uMTIwMAYDVQQDEylNaWNyb3NvZnQgUm9vdCBDZXJ0aWZpY2F0
# ZSBBdXRob3JpdHkgMjAxMTAeFw0xMTA3MDgyMDU5MDlaFw0yNjA3MDgyMTA5MDla
# MH4xCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdS
# ZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xKDAmBgNVBAMT
# H01pY3Jvc29mdCBDb2RlIFNpZ25pbmcgUENBIDIwMTEwggIiMA0GCSqGSIb3DQEB
# AQUAA4ICDwAwggIKAoICAQCr8PpyEBwurdhuqoIQTTS68rZYIZ9CGypr6VpQqrgG
# OBoESbp/wwwe3TdrxhLYC/A4wpkGsMg51QEUMULTiQ15ZId+lGAkbK+eSZzpaF7S
# 35tTsgosw6/ZqSuuegmv15ZZymAaBelmdugyUiYSL+erCFDPs0S3XdjELgN1q2jz
# y23zOlyhFvRGuuA4ZKxuZDV4pqBjDy3TQJP4494HDdVceaVJKecNvqATd76UPe/7
# 4ytaEB9NViiienLgEjq3SV7Y7e1DkYPZe7J7hhvZPrGMXeiJT4Qa8qEvWeSQOy2u
# M1jFtz7+MtOzAz2xsq+SOH7SnYAs9U5WkSE1JcM5bmR/U7qcD60ZI4TL9LoDho33
# X/DQUr+MlIe8wCF0JV8YKLbMJyg4JZg5SjbPfLGSrhwjp6lm7GEfauEoSZ1fiOIl
# XdMhSz5SxLVXPyQD8NF6Wy/VI+NwXQ9RRnez+ADhvKwCgl/bwBWzvRvUVUvnOaEP
# 6SNJvBi4RHxF5MHDcnrgcuck379GmcXvwhxX24ON7E1JMKerjt/sW5+v/N2wZuLB
# l4F77dbtS+dJKacTKKanfWeA5opieF+yL4TXV5xcv3coKPHtbcMojyyPQDdPweGF
# RInECUzF1KVDL3SV9274eCBYLBNdYJWaPk8zhNqwiBfenk70lrC8RqBsmNLg1oiM
# CwIDAQABo4IB7TCCAekwEAYJKwYBBAGCNxUBBAMCAQAwHQYDVR0OBBYEFEhuZOVQ
# BdOCqhc3NyK1bajKdQKVMBkGCSsGAQQBgjcUAgQMHgoAUwB1AGIAQwBBMAsGA1Ud
# DwQEAwIBhjAPBgNVHRMBAf8EBTADAQH/MB8GA1UdIwQYMBaAFHItOgIxkEO5FAVO
# 4eqnxzHRI4k0MFoGA1UdHwRTMFEwT6BNoEuGSWh0dHA6Ly9jcmwubWljcm9zb2Z0
# LmNvbS9wa2kvY3JsL3Byb2R1Y3RzL01pY1Jvb0NlckF1dDIwMTFfMjAxMV8wM18y
# Mi5jcmwwXgYIKwYBBQUHAQEEUjBQME4GCCsGAQUFBzAChkJodHRwOi8vd3d3Lm1p
# Y3Jvc29mdC5jb20vcGtpL2NlcnRzL01pY1Jvb0NlckF1dDIwMTFfMjAxMV8wM18y
# Mi5jcnQwgZ8GA1UdIASBlzCBlDCBkQYJKwYBBAGCNy4DMIGDMD8GCCsGAQUFBwIB
# FjNodHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20vcGtpb3BzL2RvY3MvcHJpbWFyeWNw
# cy5odG0wQAYIKwYBBQUHAgIwNB4yIB0ATABlAGcAYQBsAF8AcABvAGwAaQBjAHkA
# XwBzAHQAYQB0AGUAbQBlAG4AdAAuIB0wDQYJKoZIhvcNAQELBQADggIBAGfyhqWY
# 4FR5Gi7T2HRnIpsLlhHhY5KZQpZ90nkMkMFlXy4sPvjDctFtg/6+P+gKyju/R6mj
# 82nbY78iNaWXXWWEkH2LRlBV2AySfNIaSxzzPEKLUtCw/WvjPgcuKZvmPRul1LUd
# d5Q54ulkyUQ9eHoj8xN9ppB0g430yyYCRirCihC7pKkFDJvtaPpoLpWgKj8qa1hJ
# Yx8JaW5amJbkg/TAj/NGK978O9C9Ne9uJa7lryft0N3zDq+ZKJeYTQ49C/IIidYf
# wzIY4vDFLc5bnrRJOQrGCsLGra7lstnbFYhRRVg4MnEnGn+x9Cf43iw6IGmYslmJ
# aG5vp7d0w0AFBqYBKig+gj8TTWYLwLNN9eGPfxxvFX1Fp3blQCplo8NdUmKGwx1j
# NpeG39rz+PIWoZon4c2ll9DuXWNB41sHnIc+BncG0QaxdR8UvmFhtfDcxhsEvt9B
# xw4o7t5lL+yX9qFcltgA1qFGvVnzl6UJS0gQmYAf0AApxbGbpT9Fdx41xtKiop96
# eiL6SJUfq/tHI4D1nvi/a7dLl+LrdXga7Oo3mXkYS//WsyNodeav+vyL6wuA6mk7
# r/ww7QRMjt/fdW1jkT3RnVZOT7+AVyKheBEyIXrvQQqxP/uozKRdwaGIm1dxVk5I
# RcBCyZt2WwqASGv9eZ/BvW1taslScxMNelDNMYIZdjCCGXICAQEwgZUwfjELMAkG
# A1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQx
# HjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEoMCYGA1UEAxMfTWljcm9z
# b2Z0IENvZGUgU2lnbmluZyBQQ0EgMjAxMQITMwAAAlKLM6r4lfM52wAAAAACUjAN
# BglghkgBZQMEAgEFAKCBrjAZBgkqhkiG9w0BCQMxDAYKKwYBBAGCNwIBBDAcBgor
# BgEEAYI3AgELMQ4wDAYKKwYBBAGCNwIBFTAvBgkqhkiG9w0BCQQxIgQg0iSSD9H2
# nU93ygEUq8TNV7bSwlGLzdMnvu6ugINB9gIwQgYKKwYBBAGCNwIBDDE0MDKgFIAS
# AE0AaQBjAHIAbwBzAG8AZgB0oRqAGGh0dHA6Ly93d3cubWljcm9zb2Z0LmNvbTAN
# BgkqhkiG9w0BAQEFAASCAQABTcfh8yXDYLb69Lgkfw3KwLmi9smoR8V4+2BzleAt
# xRs4Zhm1sW5yfbIAh8UtiFRwtt3Ri7+0tltyabacXs9WIzZdxejj2yb2VKjIASQB
# Mr4n8Ye7a0viFwMAJhTQLsP5RsgSwWpvLKXaEWSHji7TgfzHvQiYM882RzxM5oCN
# FiiEpkhotkoD1uOm5NP9IFSRiuBzD1uW1gXyKS75VcLLfamzHZlYMLnYBSurs2nU
# 3DERvwYL0PfzUeYd2XFeEMxTsCienbrTlusj1X3DUbSZuJZP4J1+Y9xmUvxBtqeM
# cb8z0ODdUg3ugcisgtPF81BH1PbABNwqkNOWJx0ZV491oYIXADCCFvwGCisGAQQB
# gjcDAwExghbsMIIW6AYJKoZIhvcNAQcCoIIW2TCCFtUCAQMxDzANBglghkgBZQME
# AgEFADCCAVEGCyqGSIb3DQEJEAEEoIIBQASCATwwggE4AgEBBgorBgEEAYRZCgMB
# MDEwDQYJYIZIAWUDBAIBBQAEIAWpFXaM66ScVIiYYCdjL4ml2CnQV1kBTRV/o0J+
# EUIkAgZiFl+Pg0gYEzIwMjIwMzIyMDAyMjQxLjY3MVowBIACAfSggdCkgc0wgcox
# CzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRt
# b25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xJTAjBgNVBAsTHE1p
# Y3Jvc29mdCBBbWVyaWNhIE9wZXJhdGlvbnMxJjAkBgNVBAsTHVRoYWxlcyBUU1Mg
# RVNOOkU1QTYtRTI3Qy01OTJFMSUwIwYDVQQDExxNaWNyb3NvZnQgVGltZS1TdGFt
# cCBTZXJ2aWNloIIRVzCCBwwwggT0oAMCAQICEzMAAAGVt/wN1uM3MSUAAQAAAZUw
# DQYJKoZIhvcNAQELBQAwfDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0
# b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3Jh
# dGlvbjEmMCQGA1UEAxMdTWljcm9zb2Z0IFRpbWUtU3RhbXAgUENBIDIwMTAwHhcN
# MjExMjAyMTkwNTEyWhcNMjMwMjI4MTkwNTEyWjCByjELMAkGA1UEBhMCVVMxEzAR
# BgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1p
# Y3Jvc29mdCBDb3Jwb3JhdGlvbjElMCMGA1UECxMcTWljcm9zb2Z0IEFtZXJpY2Eg
# T3BlcmF0aW9uczEmMCQGA1UECxMdVGhhbGVzIFRTUyBFU046RTVBNi1FMjdDLTU5
# MkUxJTAjBgNVBAMTHE1pY3Jvc29mdCBUaW1lLVN0YW1wIFNlcnZpY2UwggIiMA0G
# CSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCfbUEMZ7ZLOz9aoRCeJL4hhT9Q8JZB
# 2xaVlMNCt3bwhcTI5GLPrt2e93DAsmlqOzw1cFiPPg6S5sLCXz7LbbUQpLha8S4v
# 2qccMtTokEaDQS+QJErnAsl6VSmRvAy0nlj+C/PaZuLb3OzY0ARw7UeCZLpyWPPH
# +k5MdYj6NUDTNoXqbzQHCuPs+fgIoro5y3DHoO077g6Ir2THIx1yfVFEt5zDcFPO
# YMg4yBi4A6Xc3hm9tZ6w849nBvVKwm5YALfH3y/f3n4LnN61b1wzAx3ZCZjf13UK
# bpE7p6DYJrHRB/+pwFjG99TwHH6uXzDeZT6/r6qH7AABwn8fpYc1TmleFY8YRuVz
# zjp9VkPHV8VzvzLL7QK2kteeXLL/Y4lvjL6hzyOmE+1LVD3lEbYho1zCt+F7bU+F
# pjyBfTC4i/wHsptb218YlbkQt1i1B6llmJwVFwCLX7gxQ48QIGUacMy8kp1+zczY
# +SxlpaEgNmQkfc1raPh9y5sMa6X48+x0K7B8OqDoXcTiECIjJetxwtuBlQseJ05H
# RfisfgFm09kG7vdHEo3NbUuMMBFikc4boN9Ufm0iUhq/JtqV0Kwrv9Cv3ayDgdNw
# EWiL2a65InEWSpRTYfsCQ03eqEh5A3rwV/KfUFcit+DrP+9VcDpjWRsCokZv4tgn
# 5qAXNMtHa8NiqQIDAQABo4IBNjCCATIwHQYDVR0OBBYEFKuX02ICFFdXgrcCBmDJ
# fH5v/KkXMB8GA1UdIwQYMBaAFJ+nFV0AXmJdg/Tl0mWnG1M1GelyMF8GA1UdHwRY
# MFYwVKBSoFCGTmh0dHA6Ly93d3cubWljcm9zb2Z0LmNvbS9wa2lvcHMvY3JsL01p
# Y3Jvc29mdCUyMFRpbWUtU3RhbXAlMjBQQ0ElMjAyMDEwKDEpLmNybDBsBggrBgEF
# BQcBAQRgMF4wXAYIKwYBBQUHMAKGUGh0dHA6Ly93d3cubWljcm9zb2Z0LmNvbS9w
# a2lvcHMvY2VydHMvTWljcm9zb2Z0JTIwVGltZS1TdGFtcCUyMFBDQSUyMDIwMTAo
# MSkuY3J0MAwGA1UdEwEB/wQCMAAwEwYDVR0lBAwwCgYIKwYBBQUHAwgwDQYJKoZI
# hvcNAQELBQADggIBAOCzNt4fJ+jOvQuq0Itn37IZrYNBGswAi+IAFM3YGK/wGQlE
# ncgjmNBuac95W2fAL6xtFVfMfkeqSLMLqoidVsU9Bm4DEBjaWNOT9uX/tcYiJSfF
# QM0rDbrl8V4nM88RZF56G/qJW9g5dIqOSoimzKUt/Q7WH6VByW0sar5wGvgovK3q
# FadwKShzRYcEqTkHH2zip5e73jezPHx2+taYqJG5xJzdDErZ1nMixRjaHs3Kpcsm
# ZYuxsIRfBYOJvAFGymTGRv5PuwsNps9Ech1Aasq84H/Y/8xN3GQj4P3MiDn8izUB
# DCuXIfHYk39bqnaAmFbUiCby+WWpuzdk4oDKz/sWwrnsoQ72uEGVEN7+kyw9+HSo
# 5i8l8Zg1Ymj9tUgDpVUGjAduoLyHQ7XqknKmS9kJSBKk4okEDg0Id6LeKLQwH1e4
# aVeTyUYwcBX3wg7pLJQWvR7na2SGrtl/23YGQTudmWOryhx9lnU7KBGV/aNvz0tT
# pcsucsK+cZFKDEkWB/oUFVrtyun6ND5pYZNj0CgRup5grVACq/Agb+EOGLCD+zEt
# GNop4tfKvsYb64257NJ9XrMHgpCib76WT34RPmCBByxLUkHxHq5zCyYNu0IFXAt1
# AVicw14M+czLYIVM7NOyVpFdcB1B9MiJik7peSii0XTRdl5/V/KscTaCBFz3MIIH
# cTCCBVmgAwIBAgITMwAAABXF52ueAptJmQAAAAAAFTANBgkqhkiG9w0BAQsFADCB
# iDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1Jl
# ZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEyMDAGA1UEAxMp
# TWljcm9zb2Z0IFJvb3QgQ2VydGlmaWNhdGUgQXV0aG9yaXR5IDIwMTAwHhcNMjEw
# OTMwMTgyMjI1WhcNMzAwOTMwMTgzMjI1WjB8MQswCQYDVQQGEwJVUzETMBEGA1UE
# CBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9z
# b2Z0IENvcnBvcmF0aW9uMSYwJAYDVQQDEx1NaWNyb3NvZnQgVGltZS1TdGFtcCBQ
# Q0EgMjAxMDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAOThpkzntHIh
# C3miy9ckeb0O1YLT/e6cBwfSqWxOdcjKNVf2AX9sSuDivbk+F2Az/1xPx2b3lVNx
# WuJ+Slr+uDZnhUYjDLWNE893MsAQGOhgfWpSg0S3po5GawcU88V29YZQ3MFEyHFc
# UTE3oAo4bo3t1w/YJlN8OWECesSq/XJprx2rrPY2vjUmZNqYO7oaezOtgFt+jBAc
# nVL+tuhiJdxqD89d9P6OU8/W7IVWTe/dvI2k45GPsjksUZzpcGkNyjYtcI4xyDUo
# veO0hyTD4MmPfrVUj9z6BVWYbWg7mka97aSueik3rMvrg0XnRm7KMtXAhjBcTyzi
# YrLNueKNiOSWrAFKu75xqRdbZ2De+JKRHh09/SDPc31BmkZ1zcRfNN0Sidb9pSB9
# fvzZnkXftnIv231fgLrbqn427DZM9ituqBJR6L8FA6PRc6ZNN3SUHDSCD/AQ8rdH
# GO2n6Jl8P0zbr17C89XYcz1DTsEzOUyOArxCaC4Q6oRRRuLRvWoYWmEBc8pnol7X
# KHYC4jMYctenIPDC+hIK12NvDMk2ZItboKaDIV1fMHSRlJTYuVD5C4lh8zYGNRiE
# R9vcG9H9stQcxWv2XFJRXRLbJbqvUAV6bMURHXLvjflSxIUXk8A8FdsaN8cIFRg/
# eKtFtvUeh17aj54WcmnGrnu3tz5q4i6tAgMBAAGjggHdMIIB2TASBgkrBgEEAYI3
# FQEEBQIDAQABMCMGCSsGAQQBgjcVAgQWBBQqp1L+ZMSavoKRPEY1Kc8Q/y8E7jAd
# BgNVHQ4EFgQUn6cVXQBeYl2D9OXSZacbUzUZ6XIwXAYDVR0gBFUwUzBRBgwrBgEE
# AYI3TIN9AQEwQTA/BggrBgEFBQcCARYzaHR0cDovL3d3dy5taWNyb3NvZnQuY29t
# L3BraW9wcy9Eb2NzL1JlcG9zaXRvcnkuaHRtMBMGA1UdJQQMMAoGCCsGAQUFBwMI
# MBkGCSsGAQQBgjcUAgQMHgoAUwB1AGIAQwBBMAsGA1UdDwQEAwIBhjAPBgNVHRMB
# Af8EBTADAQH/MB8GA1UdIwQYMBaAFNX2VsuP6KJcYmjRPZSQW9fOmhjEMFYGA1Ud
# HwRPME0wS6BJoEeGRWh0dHA6Ly9jcmwubWljcm9zb2Z0LmNvbS9wa2kvY3JsL3By
# b2R1Y3RzL01pY1Jvb0NlckF1dF8yMDEwLTA2LTIzLmNybDBaBggrBgEFBQcBAQRO
# MEwwSgYIKwYBBQUHMAKGPmh0dHA6Ly93d3cubWljcm9zb2Z0LmNvbS9wa2kvY2Vy
# dHMvTWljUm9vQ2VyQXV0XzIwMTAtMDYtMjMuY3J0MA0GCSqGSIb3DQEBCwUAA4IC
# AQCdVX38Kq3hLB9nATEkW+Geckv8qW/qXBS2Pk5HZHixBpOXPTEztTnXwnE2P9pk
# bHzQdTltuw8x5MKP+2zRoZQYIu7pZmc6U03dmLq2HnjYNi6cqYJWAAOwBb6J6Gng
# ugnue99qb74py27YP0h1AdkY3m2CDPVtI1TkeFN1JFe53Z/zjj3G82jfZfakVqr3
# lbYoVSfQJL1AoL8ZthISEV09J+BAljis9/kpicO8F7BUhUKz/AyeixmJ5/ALaoHC
# gRlCGVJ1ijbCHcNhcy4sa3tuPywJeBTpkbKpW99Jo3QMvOyRgNI95ko+ZjtPu4b6
# MhrZlvSP9pEB9s7GdP32THJvEKt1MMU0sHrYUP4KWN1APMdUbZ1jdEgssU5HLcEU
# BHG/ZPkkvnNtyo4JvbMBV0lUZNlz138eW0QBjloZkWsNn6Qo3GcZKCS6OEuabvsh
# VGtqRRFHqfG3rsjoiV5PndLQTHa1V1QJsWkBRH58oWFsc/4Ku+xBZj1p/cvBQUl+
# fpO+y/g75LcVv7TOPqUxUYS8vwLBgqJ7Fx0ViY1w/ue10CgaiQuPNtq6TPmb/wrp
# NPgkNWcr4A245oyZ1uEi6vAnQj0llOZ0dFtq0Z4+7X6gMTN9vMvpe784cETRkPHI
# qzqKOghif9lwY1NNje6CbaUFEMFxBmoQtB1VM1izoXBm8qGCAs4wggI3AgEBMIH4
# oYHQpIHNMIHKMQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4G
# A1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSUw
# IwYDVQQLExxNaWNyb3NvZnQgQW1lcmljYSBPcGVyYXRpb25zMSYwJAYDVQQLEx1U
# aGFsZXMgVFNTIEVTTjpFNUE2LUUyN0MtNTkyRTElMCMGA1UEAxMcTWljcm9zb2Z0
# IFRpbWUtU3RhbXAgU2VydmljZaIjCgEBMAcGBSsOAwIaAxUA0Y+CyLezGgVHWFNm
# KI1LuE/hY6uggYMwgYCkfjB8MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGlu
# Z3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBv
# cmF0aW9uMSYwJAYDVQQDEx1NaWNyb3NvZnQgVGltZS1TdGFtcCBQQ0EgMjAxMDAN
# BgkqhkiG9w0BAQUFAAIFAOXjJDkwIhgPMjAyMjAzMjIwMDIwMDlaGA8yMDIyMDMy
# MzAwMjAwOVowdzA9BgorBgEEAYRZCgQBMS8wLTAKAgUA5eMkOQIBADAKAgEAAgIj
# mQIB/zAHAgEAAgISJDAKAgUA5eR1uQIBADA2BgorBgEEAYRZCgQCMSgwJjAMBgor
# BgEEAYRZCgMCoAowCAIBAAIDB6EgoQowCAIBAAIDAYagMA0GCSqGSIb3DQEBBQUA
# A4GBAMkByFKxw1mqUkYuZcitrKwsWI24L32wdgDWKe9s5MM1p1SMn2OeBjiAv5SD
# +BnwPf4Elh/s4o85JjknMi/oiRJGy4QT00j4uJidYErHdx3JQdweRekHA4TKYmdh
# JxQ3F4qdpQ+AyjwEoAy5aa58UVi4c42A7B3awd9xt8moAb72MYIEDTCCBAkCAQEw
# gZMwfDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcT
# B1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEmMCQGA1UE
# AxMdTWljcm9zb2Z0IFRpbWUtU3RhbXAgUENBIDIwMTACEzMAAAGVt/wN1uM3MSUA
# AQAAAZUwDQYJYIZIAWUDBAIBBQCgggFKMBoGCSqGSIb3DQEJAzENBgsqhkiG9w0B
# CRABBDAvBgkqhkiG9w0BCQQxIgQgQP/m/inIR2CSpdjTJjURKxVG4jVxxuysmi8S
# Zu4nQTQwgfoGCyqGSIb3DQEJEAIvMYHqMIHnMIHkMIG9BCBc5kvhjZALe2mhIz/Q
# d7keVOmA/cC1dzKZT4ybLEkCxzCBmDCBgKR+MHwxCzAJBgNVBAYTAlVTMRMwEQYD
# VQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNy
# b3NvZnQgQ29ycG9yYXRpb24xJjAkBgNVBAMTHU1pY3Jvc29mdCBUaW1lLVN0YW1w
# IFBDQSAyMDEwAhMzAAABlbf8DdbjNzElAAEAAAGVMCIEIGQkNGTlTiRSCtEmM5eP
# OSqpNzav3e6hYhsMaBikCukgMA0GCSqGSIb3DQEBCwUABIICAJbcDXmeWyXGJpRa
# SXisWOMdEzaGHvfrmbhdmq+LldVXBYkghJHfZJXCjSvm+GMa03bmrhBtRUNZP6xN
# HE8r2x3wkLF8XdIC86H3FkYrj2bcsv/OK0skA2XD/aSHYmM4C+2mDE7BpYhB3eL7
# D6U30nwNdR6jir/W4ukI8RLHe6TcBHREeibTncnq7IajDspXeW4s7e7zpoR+AP0T
# t6PgENILA5/aTfFVKG7oiLooNkMr287a2wulOH8E0Jp8OS1ED/QUrauL70WUI+yh
# 0KhXNyDno1c52xs7tRfP8CMHRsIuEAMd8t3Ng4tP334ZqpEWK7uDiC0sc1Vjt0ea
# qmhPKJdZOEMfjTGgsXahw06GJVpvQAWGHFToRUd4fcDpd2nv17yuTbLYixBvoMH4
# 7YwN/9CWvfUvLIr3XA0LTGf0WJwmeU9B1YX8Lm7yT4Fi+5okPjXa+dtrsjS3UeFf
# CP1AGdEsmcnayHDF3BBApOJlhiry01MrP2/VY/zL/IjTSVG9o0b8j7PWq0bNtfmK
# lnYNdACKGF04Cd8LKFOV4NK+3EoqGmws2zqqB7KpqPyllFDZijKXmY1cc5EpsKD/
# R1fnwKAdQ+K9GHSyUwml7MAqPVx8uYwt3j2k5b6MqU9Gq8v2ur3E6hzhSO2Rhzq/
# PkT6/cbeDvkJOyzylhuIVvnmnkpm
# SIG # End signature block