@ -72,14 +72,12 @@
. PARAMETER Verbose
. PARAMETER Verbose
Displays diagnostics information .
Displays diagnostics information .
. PARAMETER AzureFeed
. PARAMETER AzureFeed
Default : https : / / dotnetcli. azureedge . net / dotnet
Default : https : / / builds. dotnet . microsoft . com / dotnet
For internal use only .
For internal use only .
Allows using a different storage to download SDK archives from .
Allows using a different storage to download SDK archives from .
This parameter is only used if $NoCdn is false .
. PARAMETER UncachedFeed
. PARAMETER UncachedFeed
For internal use only .
For internal use only .
Allows using a different storage to download SDK archives from .
Allows using a different storage to download SDK archives from .
This parameter is only used if $NoCdn is true .
. PARAMETER ProxyAddress
. PARAMETER ProxyAddress
If set , the installer will use the proxy when making web requests
If set , the installer will use the proxy when making web requests
. PARAMETER ProxyUseDefaultCredentials
. PARAMETER ProxyUseDefaultCredentials
@ -90,14 +88,22 @@
. PARAMETER SkipNonVersionedFiles
. PARAMETER SkipNonVersionedFiles
Default : false
Default : false
Skips installing non-versioned files if they already exist , such as dotnet . exe .
Skips installing non-versioned files if they already exist , such as dotnet . exe .
. PARAMETER NoCdn
Disable downloading from the Azure CDN , and use the uncached feed directly .
. PARAMETER JSonFile
. PARAMETER JSonFile
Determines the SDK version from a user specified global . json file
Determines the SDK version from a user specified global . json file
Note : global . json must have a value for 'SDK:Version'
Note : global . json must have a value for 'SDK:Version'
. PARAMETER DownloadTimeout
. PARAMETER DownloadTimeout
Determines timeout duration in seconds for dowloading of the SDK file
Determines timeout duration in seconds for dowloading of the SDK file
Default : 1200 seconds ( 20 minutes )
Default : 1200 seconds ( 20 minutes )
. PARAMETER KeepZip
If set , downloaded file is kept
. PARAMETER ZipPath
Use that path to store installer , generated by default
. EXAMPLE
dotnet-install . ps1 -Version 7.0 . 401
Installs the . NET SDK version 7.0 . 401
. EXAMPLE
dotnet-install . ps1 -Channel 8.0 -Quality GA
Installs the latest GA ( general availability ) version of the . NET 8.0 SDK
#>
#>
[ cmdletbinding ( ) ]
[ cmdletbinding ( ) ]
param (
param (
@ -120,8 +126,10 @@ param(
[ switch ] $ProxyUseDefaultCredentials ,
[ switch ] $ProxyUseDefaultCredentials ,
[ string[] ] $ProxyBypassList = @ ( ) ,
[ string[] ] $ProxyBypassList = @ ( ) ,
[ switch ] $SkipNonVersionedFiles ,
[ switch ] $SkipNonVersionedFiles ,
[ switch ] $NoCdn ,
[ int ] $DownloadTimeout = 1200 ,
[ int ] $DownloadTimeout = 1200
[ switch ] $KeepZip ,
[ string ] $ZipPath = [ System.IO.Path ] :: combine ( [ System.IO.Path ] :: GetTempPath ( ) , [ System.IO.Path ] :: GetRandomFileName ( ) ) ,
[ switch ] $Help
)
)
Set-StrictMode -Version Latest
Set-StrictMode -Version Latest
@ -173,7 +181,24 @@ function Say-Verbose($str) {
function Measure-Action($name , $block ) {
function Measure-Action($name , $block ) {
$time = Measure-Command $block
$time = Measure-Command $block
$totalSeconds = $time . TotalSeconds
$totalSeconds = $time . TotalSeconds
Say-Verbose " ⏱ Action ' $name ' took $totalSeconds seconds "
Say-Verbose " Action ' $name ' took $totalSeconds seconds "
}
function Get-Remote-File-Size($zipUri ) {
try {
$response = Invoke-WebRequest -Uri $zipUri -Method Head
$fileSize = $response . Headers [ " Content-Length " ]
if ( ( ! [ string ] :: IsNullOrEmpty ( $fileSize ) ) ) {
Say " Remote file $zipUri size is $fileSize bytes. "
return $fileSize
}
}
catch {
Say-Verbose " Content-Length header was not extracted for $zipUri . "
}
return $null
}
}
function Say-Invocation($Invocation ) {
function Say-Invocation($Invocation ) {
@ -219,8 +244,7 @@ function Get-Machine-Architecture() {
try {
try {
if ( ( ( Get-CimInstance -ClassName CIM_OperatingSystem ) . OSArchitecture ) -like " ARM* " ) {
if ( ( ( Get-CimInstance -ClassName CIM_OperatingSystem ) . OSArchitecture ) -like " ARM* " ) {
if ( [ Environment ] :: Is64BitOperatingSystem )
if ( [ Environment ] :: Is64BitOperatingSystem ) {
{
return " arm64 "
return " arm64 "
}
}
return " arm "
return " arm "
@ -249,13 +273,13 @@ function Get-CLIArchitecture-From-Architecture([string]$Architecture) {
}
}
}
}
function ValidateFeedCredential([string ] $FeedCredential )
function ValidateFeedCredential([string ] $FeedCredential ) {
{
if ( $Internal -and [ string ] :: IsNullOrWhitespace ( $FeedCredential ) ) {
if ( $Internal -and [ string ] :: IsNullOrWhitespace ( $FeedCredential ) ) {
$message = " Provide credentials via -FeedCredential parameter. "
$message = " Provide credentials via -FeedCredential parameter. "
if ( $DryRun ) {
if ( $DryRun ) {
Say-Warning " $message "
Say-Warning " $message "
} else {
}
else {
throw " $message "
throw " $message "
}
}
}
}
@ -347,8 +371,7 @@ function Load-Assembly([string] $Assembly) {
}
}
}
}
function GetHTTPResponse([Uri ] $Uri , [ bool ] $HeaderOnly , [ bool ] $DisableRedirect , [ bool ] $DisableFeedCredential )
function GetHTTPResponse([Uri ] $Uri , [ bool ] $HeaderOnly , [ bool ] $DisableRedirect , [ bool ] $DisableFeedCredential ) {
{
$cts = New-Object System . Threading . CancellationTokenSource
$cts = New-Object System . Threading . CancellationTokenSource
$downloadScript = {
$downloadScript = {
@ -366,12 +389,14 @@ function GetHTTPResponse([Uri] $Uri, [bool]$HeaderOnly, [bool]$DisableRedirect,
if ( $DefaultProxy -and ( -not $DefaultProxy . IsBypassed ( $Uri ) ) ) {
if ( $DefaultProxy -and ( -not $DefaultProxy . IsBypassed ( $Uri ) ) ) {
if ( $null -ne $DefaultProxy . GetProxy ( $Uri ) ) {
if ( $null -ne $DefaultProxy . GetProxy ( $Uri ) ) {
$ProxyAddress = $DefaultProxy . GetProxy ( $Uri ) . OriginalString
$ProxyAddress = $DefaultProxy . GetProxy ( $Uri ) . OriginalString
} else {
}
else {
$ProxyAddress = $null
$ProxyAddress = $null
}
}
$ProxyUseDefaultCredentials = $true
$ProxyUseDefaultCredentials = $true
}
}
} catch {
}
catch {
# Eat the exception and move forward as the above code is an attempt
# Eat the exception and move forward as the above code is an attempt
# at resolving the DefaultProxy that may not have been a problem.
# at resolving the DefaultProxy that may not have been a problem.
$ProxyAddress = $null
$ProxyAddress = $null
@ -387,8 +412,7 @@ function GetHTTPResponse([Uri] $Uri, [bool]$HeaderOnly, [bool]$DisableRedirect,
BypassList = $ProxyBypassList ;
BypassList = $ProxyBypassList ;
}
}
}
}
if ( $DisableRedirect )
if ( $DisableRedirect ) {
{
$HttpClientHandler . AllowAutoRedirect = $false
$HttpClientHandler . AllowAutoRedirect = $false
}
}
$HttpClient = New-Object System . Net . Http . HttpClient -ArgumentList $HttpClientHandler
$HttpClient = New-Object System . Net . Http . HttpClient -ArgumentList $HttpClientHandler
@ -422,8 +446,7 @@ function GetHTTPResponse([Uri] $Uri, [bool]$HeaderOnly, [bool]$DisableRedirect,
$DownloadException . Data [ " StatusCode " ] = [ int ] $Response . StatusCode
$DownloadException . Data [ " StatusCode " ] = [ int ] $Response . StatusCode
$DownloadException . Data [ " ErrorMessage " ] = " Unable to download $Uri . Returned HTTP status code: " + $DownloadException . Data [ " StatusCode " ]
$DownloadException . Data [ " ErrorMessage " ] = " Unable to download $Uri . Returned HTTP status code: " + $DownloadException . Data [ " StatusCode " ]
if ( 404 -eq [ int ] $Response . StatusCode )
if ( 404 -eq [ int ] $Response . StatusCode ) {
{
$cts . Cancel ( )
$cts . Cancel ( )
}
}
}
}
@ -462,10 +485,8 @@ function GetHTTPResponse([Uri] $Uri, [bool]$HeaderOnly, [bool]$DisableRedirect,
try {
try {
return Invoke-With -Retry $downloadScript $cts . Token
return Invoke-With -Retry $downloadScript $cts . Token
}
}
finally
finally {
{
if ( $null -ne $cts ) {
if ( $null -ne $cts )
{
$cts . Dispose ( )
$cts . Dispose ( )
}
}
}
}
@ -583,11 +604,9 @@ function Get-Download-Link([string]$AzureFeed, [string]$SpecificVersion, [string
elseif ( $Runtime -eq " windowsdesktop " ) {
elseif ( $Runtime -eq " windowsdesktop " ) {
# The windows desktop runtime is part of the core runtime layout prior to 5.0
# The windows desktop runtime is part of the core runtime layout prior to 5.0
$PayloadURL = " $AzureFeed /Runtime/ $SpecificVersion /windowsdesktop-runtime- $SpecificProductVersion -win- $CLIArchitecture .zip "
$PayloadURL = " $AzureFeed /Runtime/ $SpecificVersion /windowsdesktop-runtime- $SpecificProductVersion -win- $CLIArchitecture .zip "
if ( $SpecificVersion -match '^(\d+)\.(.*)$' )
if ( $SpecificVersion -match '^(\d+)\.(.*)$' ) {
{
$majorVersion = [ int ] $Matches [ 1 ]
$majorVersion = [ int ] $Matches [ 1 ]
if ( $majorVersion -ge 5 )
if ( $majorVersion -ge 5 ) {
{
$PayloadURL = " $AzureFeed /WindowsDesktop/ $SpecificVersion /windowsdesktop-runtime- $SpecificProductVersion -win- $CLIArchitecture .zip "
$PayloadURL = " $AzureFeed /WindowsDesktop/ $SpecificVersion /windowsdesktop-runtime- $SpecificProductVersion -win- $CLIArchitecture .zip "
}
}
}
}
@ -637,8 +656,7 @@ function Get-Product-Version([string]$AzureFeed, [string]$SpecificVersion, [stri
if ( $productVersionResponse . StatusCode -eq 200 ) {
if ( $productVersionResponse . StatusCode -eq 200 ) {
$productVersion = $productVersionResponse . Content . ReadAsStringAsync ( ) . Result . Trim ( )
$productVersion = $productVersionResponse . Content . ReadAsStringAsync ( ) . Result . Trim ( )
if ( $productVersion -ne $SpecificVersion )
if ( $productVersion -ne $SpecificVersion ) {
{
Say " Using alternate version $productVersion found in $ProductVersionTxtURL "
Say " Using alternate version $productVersion found in $ProductVersionTxtURL "
}
}
return $productVersion
return $productVersion
@ -653,8 +671,7 @@ function Get-Product-Version([string]$AzureFeed, [string]$SpecificVersion, [stri
}
}
# Getting the version number with productVersion.txt has failed. Try parsing the download link for a version number.
# Getting the version number with productVersion.txt has failed. Try parsing the download link for a version number.
if ( [ string ] :: IsNullOrEmpty ( $PackageDownloadLink ) )
if ( [ string ] :: IsNullOrEmpty ( $PackageDownloadLink ) ) {
{
Say-Verbose " Using the default value ' $SpecificVersion ' as the product version. "
Say-Verbose " Using the default value ' $SpecificVersion ' as the product version. "
return $SpecificVersion
return $SpecificVersion
}
}
@ -714,16 +731,14 @@ function Get-Product-Version-Url([string]$AzureFeed, [string]$SpecificVersion, [
return $ProductVersionTxtURL
return $ProductVersionTxtURL
}
}
function Get-ProductVersionFromDownloadLink([string]$PackageDownloadLink , [ string ] $SpecificVersion )
function Get-ProductVersionFromDownloadLink([string]$PackageDownloadLink , [ string ] $SpecificVersion ) {
{
Say-Invocation $MyInvocation
Say-Invocation $MyInvocation
#product specific version follows the product name
#product specific version follows the product name
#for filename 'dotnet-sdk-3.1.404-win-x64.zip': the product version is 3.1.400
#for filename 'dotnet-sdk-3.1.404-win-x64.zip': the product version is 3.1.400
$filename = $PackageDownloadLink . Substring ( $PackageDownloadLink . LastIndexOf ( " / " ) + 1 )
$filename = $PackageDownloadLink . Substring ( $PackageDownloadLink . LastIndexOf ( " / " ) + 1 )
$filenameParts = $filename . Split ( '-' )
$filenameParts = $filename . Split ( '-' )
if ( $filenameParts . Length -gt 2 )
if ( $filenameParts . Length -gt 2 ) {
{
$productVersion = $filenameParts [ 2 ]
$productVersion = $filenameParts [ 2 ]
Say-Verbose " Extracted product version ' $productVersion ' from download link ' $PackageDownloadLink '. "
Say-Verbose " Extracted product version ' $productVersion ' from download link ' $PackageDownloadLink '. "
}
}
@ -741,6 +756,9 @@ function Get-User-Share-Path() {
if ( ! $InstallRoot ) {
if ( ! $InstallRoot ) {
$InstallRoot = " $env:LocalAppData \Microsoft\dotnet "
$InstallRoot = " $env:LocalAppData \Microsoft\dotnet "
}
}
elseif ( $InstallRoot -like " $env:ProgramFiles \dotnet\?* " ) {
Say-Warning " The install root specified by the environment variable DOTNET_INSTALL_DIR points to the sub folder of $env:ProgramFiles \dotnet which is the default dotnet install root using .NET SDK installer. It is better to keep aligned with .NET SDK installer. "
}
return $InstallRoot
return $InstallRoot
}
}
@ -753,6 +771,19 @@ function Resolve-Installation-Path([string]$InstallDir) {
return $InstallDir
return $InstallDir
}
}
function Test-User-Write-Access([string]$InstallDir ) {
try {
$tempFileName = [ guid ] :: NewGuid ( ) . ToString ( )
$tempFilePath = Join-Path -Path $InstallDir -ChildPath $tempFileName
New-Item -Path $tempFilePath -ItemType File -Force
Remove-Item $tempFilePath -Force
return $true
}
catch {
return $false
}
}
function Is-Dotnet-Package-Installed([string]$InstallRoot , [ string ] $RelativePathToPackage , [ string ] $SpecificVersion ) {
function Is-Dotnet-Package-Installed([string]$InstallRoot , [ string ] $RelativePathToPackage , [ string ] $SpecificVersion ) {
Say-Invocation $MyInvocation
Say-Invocation $MyInvocation
@ -836,8 +867,7 @@ function Extract-Dotnet-Package([string]$ZipPath, [string]$OutPath) {
}
}
}
}
}
}
catch
catch {
{
Say-Error " Failed to extract package. Exception: $_ "
Say-Error " Failed to extract package. Exception: $_ "
throw ;
throw ;
}
}
@ -869,6 +899,8 @@ function DownloadFile($Source, [string]$OutPath) {
$File = [ System.IO.File ] :: Create ( $OutPath )
$File = [ System.IO.File ] :: Create ( $OutPath )
$Stream . CopyTo ( $File )
$Stream . CopyTo ( $File )
$File . Close ( )
$File . Close ( )
ValidateRemoteLocalFileSizes -LocalFileOutPath $OutPath -SourceUri $Source
}
}
finally {
finally {
if ( $null -ne $Stream ) {
if ( $null -ne $Stream ) {
@ -877,19 +909,40 @@ function DownloadFile($Source, [string]$OutPath) {
}
}
}
}
function ValidateRemoteLocalFileSizes([string]$LocalFileOutPath , $SourceUri ) {
try {
$remoteFileSize = Get-Remote -File -Size -zipUri $SourceUri
$fileSize = [ long ] ( Get-Item $LocalFileOutPath ) . Length
Say " Downloaded file $SourceUri size is $fileSize bytes. "
if ( ( ! [ string ] :: IsNullOrEmpty ( $remoteFileSize ) ) -and ! ( [ string ] :: IsNullOrEmpty ( $fileSize ) ) ) {
if ( $remoteFileSize -ne $fileSize ) {
Say " The remote and local file sizes are not equal. Remote file size is $remoteFileSize bytes and local size is $fileSize bytes. The local package may be corrupted. "
}
else {
Say " The remote and local file sizes are equal. "
}
}
else {
Say " Either downloaded or local package size can not be measured. One of them may be corrupted. "
}
}
catch {
Say " Either downloaded or local package size can not be measured. One of them may be corrupted. "
}
}
function SafeRemoveFile($Path ) {
function SafeRemoveFile($Path ) {
try {
try {
if ( Test-Path $Path ) {
if ( Test-Path $Path ) {
Remove-Item $Path
Remove-Item $Path
Say-Verbose " The temporary file `" $Path `" was removed. "
Say-Verbose " The temporary file `" $Path `" was removed. "
}
}
else
else {
{
Say-Verbose " The temporary file `" $Path `" does not exist, therefore is not removed. "
Say-Verbose " The temporary file `" $Path `" does not exist, therefore is not removed. "
}
}
}
}
catch
catch {
{
Say-Warning " Failed to remove the temporary file: `" $Path `" , remove it manually. "
Say-Warning " Failed to remove the temporary file: `" $Path `" , remove it manually. "
}
}
}
}
@ -901,7 +954,8 @@ function Prepend-Sdk-InstallRoot-To-Path([string]$InstallRoot) {
if ( -Not $env:path . Contains ( $SuffixedBinPath ) ) {
if ( -Not $env:path . Contains ( $SuffixedBinPath ) ) {
Say " Adding to current process PATH: `" $BinPath `" . Note: This change will not be visible if PowerShell was run as a child process. "
Say " Adding to current process PATH: `" $BinPath `" . Note: This change will not be visible if PowerShell was run as a child process. "
$env:path = $SuffixedBinPath + $env:path
$env:path = $SuffixedBinPath + $env:path
} else {
}
else {
Say-Verbose " Current process PATH already contains `" $BinPath `" "
Say-Verbose " Current process PATH already contains `" $BinPath `" "
}
}
}
}
@ -910,8 +964,7 @@ function Prepend-Sdk-InstallRoot-To-Path([string]$InstallRoot) {
}
}
}
}
function PrintDryRunOutput($Invocation , $DownloadLinks )
function PrintDryRunOutput($Invocation , $DownloadLinks ) {
{
Say " Payload URLs: "
Say " Payload URLs: "
for ( $linkIndex = 0 ; $linkIndex -lt $DownloadLinks . count ; $linkIndex + + ) {
for ( $linkIndex = 0 ; $linkIndex -lt $DownloadLinks . count ; $linkIndex + + ) {
@ -934,12 +987,37 @@ function PrintDryRunOutput($Invocation, $DownloadLinks)
$RepeatableCommand + = " -FeedCredential `" <feedCredential> `" "
$RepeatableCommand + = " -FeedCredential `" <feedCredential> `" "
}
}
Say " Repeatable invocation: $RepeatableCommand "
Say " Repeatable invocation: $RepeatableCommand "
if ( $SpecificVersion -ne $EffectiveVersion )
if ( $SpecificVersion -ne $EffectiveVersion ) {
{
Say " NOTE: Due to finding a version manifest with this runtime, it would actually install with version ' $EffectiveVersion ' "
Say " NOTE: Due to finding a version manifest with this runtime, it would actually install with version ' $EffectiveVersion ' "
}
}
}
}
# grab the 'stem' of the redirect and check it against all of our configured feeds,
# if it matches, we can be sure that the redirect is valid and we should use it for
# subsequent processing
function Sanitize-RedirectUrl([string]$url ) {
$urlSegments = ( [ System.Uri ] $url ) . Segments ;
$urlStem = $urlSegments [ 2 . . ( $urlSegments . Length - 1 ) ] -join " " ;
Write-Verbose " Checking configured feeds for the asset at $urlStem "
foreach ( $prospectiveFeed in $feeds ) {
$trialUrl = " $prospectiveFeed / $urlStem " ;
Write-Verbose " Checking $trialUrl "
try {
$trialResponse = Invoke-WebRequest -Uri $trialUrl -Method HEAD
if ( $trialResponse . StatusCode -eq 200 ) {
Write-Verbose " Found a match at $trialUrl "
return $trialUrl ;
}
else {
Write-Verbose " No match at $trialUrl "
}
}
catch {
Write-Verbose " Failed to check $trialUrl "
}
}
}
function Get-AkaMSDownloadLink([string]$Channel , [ string ] $Quality , [ bool ] $Internal , [ string ] $Product , [ string ] $Architecture ) {
function Get-AkaMSDownloadLink([string]$Channel , [ string ] $Quality , [ bool ] $Internal , [ string ] $Product , [ string ] $Architecture ) {
Say-Invocation $MyInvocation
Say-Invocation $MyInvocation
@ -963,8 +1041,7 @@ function Get-AkaMSDownloadLink([string]$Channel, [string]$Quality, [bool]$Intern
Say-Verbose " Constructed aka.ms link: ' $akaMsLink '. "
Say-Verbose " Constructed aka.ms link: ' $akaMsLink '. "
$akaMsDownloadLink = $null
$akaMsDownloadLink = $null
for ( $maxRedirections = 9 ; $maxRedirections -ge 0 ; $maxRedirections - - )
for ( $maxRedirections = 9 ; $maxRedirections -ge 0 ; $maxRedirections - - ) {
{
#get HTTP response
#get HTTP response
#do not pass credentials as a part of the $akaMsLink and do not apply credentials in the GetHTTPResponse function
#do not pass credentials as a part of the $akaMsLink and do not apply credentials in the GetHTTPResponse function
#otherwise the redirect link would have credentials as well
#otherwise the redirect link would have credentials as well
@ -978,8 +1055,7 @@ function Get-AkaMSDownloadLink([string]$Channel, [string]$Quality, [bool]$Intern
}
}
#if HTTP code is 301 (Moved Permanently), the redirect link exists
#if HTTP code is 301 (Moved Permanently), the redirect link exists
if ( $Response . StatusCode -eq 301 )
if ( $Response . StatusCode -eq 301 ) {
{
try {
try {
$akaMsDownloadLink = $Response . Headers . GetValues ( " Location " ) [ 0 ]
$akaMsDownloadLink = $Response . Headers . GetValues ( " Location " ) [ 0 ]
@ -998,9 +1074,13 @@ function Get-AkaMSDownloadLink([string]$Channel, [string]$Quality, [bool]$Intern
return $null
return $null
}
}
}
}
elseif ( ( ( $Response . StatusCode -lt 300 ) -or ( $Response . StatusCode -ge 400 ) ) -and ( -not [ string ] :: IsNullOrEmpty ( $akaMsDownloadLink ) ) )
elseif ( ( ( $Response . StatusCode -lt 300 ) -or ( $Response . StatusCode -ge 400 ) ) -and ( -not [ string ] :: IsNullOrEmpty ( $akaMsDownloadLink ) ) ) {
{
# Redirections have ended.
# Redirections have ended.
$actualRedirectUrl = Sanitize-RedirectUrl $akaMsDownloadLink
if ( $null -ne $actualRedirectUrl ) {
$akaMsDownloadLink = $actualRedirectUrl
}
return $akaMsDownloadLink
return $akaMsDownloadLink
}
}
@ -1049,27 +1129,21 @@ function Get-AkaMsLink-And-Version([string] $NormalizedChannel, [string] $Normal
}
}
}
}
function Get-Feeds-To-Use ( )
function Get-Feeds-To-Use ( ) {
{
$feeds = @ (
$feeds = @ (
" https://dotnetcli.azureedge.net/dotnet " ,
" https://builds.dotnet.microsoft.com/dotnet "
" https://dotnetbuilds.azureedge .net/public"
" https://ci.dot .net/public"
)
)
if ( -not [ string ] :: IsNullOrEmpty ( $AzureFeed ) ) {
if ( -not [ string ] :: IsNullOrEmpty ( $AzureFeed ) ) {
$feeds = @ ( $AzureFeed )
$feeds = @ ( $AzureFeed )
}
}
if ( $NoCdn ) {
$feeds = @ (
" https://dotnetcli.blob.core.windows.net/dotnet " ,
" https://dotnetbuilds.blob.core.windows.net/public "
)
if ( -not [ string ] :: IsNullOrEmpty ( $UncachedFeed ) ) {
if ( -not [ string ] :: IsNullOrEmpty ( $UncachedFeed ) ) {
$feeds = @ ( $UncachedFeed )
$feeds = @ ( $UncachedFeed )
}
}
}
Write-Verbose " Initialized feeds: $feeds "
return $feeds
return $feeds
}
}
@ -1100,6 +1174,13 @@ function Resolve-AssetName-And-RelativePath([string] $Runtime) {
}
}
function Prepare-Install-Directory {
function Prepare-Install-Directory {
$diskSpaceWarning = " Failed to check the disk space. Installation will continue, but it may fail if you do not have enough disk space. " ;
if ( $PSVersionTable . PSVersion . Major -lt 7 ) {
Say-Verbose $diskSpaceWarning
return
}
New-Item -ItemType Directory -Force -Path $InstallRoot | Out-Null
New-Item -ItemType Directory -Force -Path $InstallRoot | Out-Null
$installDrive = $ ( ( Get-Item $InstallRoot -Force ) . PSDrive . Name ) ;
$installDrive = $ ( ( Get-Item $InstallRoot -Force ) . PSDrive . Name ) ;
@ -1108,14 +1189,20 @@ function Prepare-Install-Directory {
$diskInfo = Get-PSDrive -Name $installDrive
$diskInfo = Get-PSDrive -Name $installDrive
}
}
catch {
catch {
Say-Warning " Failed to check the disk space. Installation will continue, but it may fail if you do not have enough disk space. "
Say-Warning $diskSpaceWarning
}
}
# The check is relevant for PS version >= 7, the result can be irrelevant for older versions. See https://github.com/PowerShell/PowerShell/issues/12442.
if ( ( $null -ne $diskInfo ) -and ( $diskInfo . Free / 1 MB -le 100 ) ) {
if ( ( $null -ne $diskInfo ) -and ( $diskInfo . Free / 1 MB -le 100 ) ) {
throw " There is not enough disk space on drive ${installDrive} : "
throw " There is not enough disk space on drive ${installDrive} : "
}
}
}
}
if ( $Help ) {
Get-Help $PSCommandPath -Examples
exit
}
Say-Verbose " Note that the intended use of this script is for Continuous Integration (CI) scenarios, where: "
Say-Verbose " Note that the intended use of this script is for Continuous Integration (CI) scenarios, where: "
Say-Verbose " - The SDK needs to be installed without user interaction and without admin rights. "
Say-Verbose " - The SDK needs to be installed without user interaction and without admin rights. "
Say-Verbose " - The SDK installation doesn't need to persist across multiple CI runs. "
Say-Verbose " - The SDK installation doesn't need to persist across multiple CI runs. "
@ -1139,6 +1226,10 @@ Measure-Action "Product discovery" {
}
}
$InstallRoot = Resolve-Installation -Path $InstallDir
$InstallRoot = Resolve-Installation -Path $InstallDir
if ( -not ( Test-User -Write -Access $InstallRoot ) ) {
Say-Error " The current user doesn't have write access to the installation root ' $InstallRoot ' to install .NET. Please try specifying a different installation directory using the -InstallDir parameter, or ensure the selected directory has the appropriate permissions. "
throw
}
Say-Verbose " InstallRoot: $InstallRoot "
Say-Verbose " InstallRoot: $InstallRoot "
$ScriptName = $MyInvocation . MyCommand . Name
$ScriptName = $MyInvocation . MyCommand . Name
( $assetName , $dotnetPackageRelativePath ) = Resolve-AssetName -And -RelativePath -Runtime $Runtime
( $assetName , $dotnetPackageRelativePath ) = Resolve-AssetName -And -RelativePath -Runtime $Runtime
@ -1160,8 +1251,7 @@ if ([string]::IsNullOrEmpty($JSonFile) -and ($Version -eq "latest")) {
if ( -Not $DryRun ) {
if ( -Not $DryRun ) {
Say-Verbose " Checking if the version $EffectiveVersion is already installed "
Say-Verbose " Checking if the version $EffectiveVersion is already installed "
if ( Is-Dotnet -Package -Installed -InstallRoot $InstallRoot -RelativePathToPackage $dotnetPackageRelativePath -SpecificVersion $EffectiveVersion )
if ( Is-Dotnet -Package -Installed -InstallRoot $InstallRoot -RelativePathToPackage $dotnetPackageRelativePath -SpecificVersion $EffectiveVersion ) {
{
Say " $assetName with version ' $EffectiveVersion ' is already installed. "
Say " $assetName with version ' $EffectiveVersion ' is already installed. "
Prepend-Sdk -InstallRoot -To -Path -InstallRoot $InstallRoot
Prepend-Sdk -InstallRoot -To -Path -InstallRoot $InstallRoot
return
return
@ -1172,8 +1262,7 @@ if ([string]::IsNullOrEmpty($JSonFile) -and ($Version -eq "latest")) {
# Primary and legacy links cannot be used if a quality was specified.
# Primary and legacy links cannot be used if a quality was specified.
# If we already have an aka.ms link, no need to search the blob feeds.
# If we already have an aka.ms link, no need to search the blob feeds.
if ( [ string ] :: IsNullOrEmpty ( $NormalizedQuality ) -and 0 -eq $DownloadLinks . count )
if ( [ string ] :: IsNullOrEmpty ( $NormalizedQuality ) -and 0 -eq $DownloadLinks . count ) {
{
foreach ( $feed in $feeds ) {
foreach ( $feed in $feeds ) {
try {
try {
$SpecificVersion = Get-Specific -Version -From -Version -AzureFeed $feed -Channel $Channel -Version $Version -JSonFile $JSonFile
$SpecificVersion = Get-Specific -Version -From -Version -AzureFeed $feed -Channel $Channel -Version $Version -JSonFile $JSonFile
@ -1190,16 +1279,14 @@ if ([string]::IsNullOrEmpty($NormalizedQuality) -and 0 -eq $DownloadLinks.count)
if ( -Not $DryRun ) {
if ( -Not $DryRun ) {
Say-Verbose " Checking if the version $EffectiveVersion is already installed "
Say-Verbose " Checking if the version $EffectiveVersion is already installed "
if ( Is-Dotnet -Package -Installed -InstallRoot $InstallRoot -RelativePathToPackage $dotnetPackageRelativePath -SpecificVersion $EffectiveVersion )
if ( Is-Dotnet -Package -Installed -InstallRoot $InstallRoot -RelativePathToPackage $dotnetPackageRelativePath -SpecificVersion $EffectiveVersion ) {
{
Say " $assetName with version ' $EffectiveVersion ' is already installed. "
Say " $assetName with version ' $EffectiveVersion ' is already installed. "
Prepend-Sdk -InstallRoot -To -Path -InstallRoot $InstallRoot
Prepend-Sdk -InstallRoot -To -Path -InstallRoot $InstallRoot
return
return
}
}
}
}
}
}
catch
catch {
{
Say-Verbose " Failed to acquire download links from feed $feed . Exception: $_ "
Say-Verbose " Failed to acquire download links from feed $feed . Exception: $_ "
}
}
}
}
@ -1216,15 +1303,13 @@ if ($DryRun) {
Measure-Action " Installation directory preparation " { Prepare-Install -Directory }
Measure-Action " Installation directory preparation " { Prepare-Install -Directory }
$ZipPath = [ System.IO.Path ] :: combine ( [ System.IO.Path ] :: GetTempPath ( ) , [ System.IO.Path ] :: GetRandomFileName ( ) )
Say-Verbose " Zip path: $ZipPath "
Say-Verbose " Zip path: $ZipPath "
$DownloadSucceeded = $false
$DownloadSucceeded = $false
$DownloadedLink = $null
$DownloadedLink = $null
$ErrorMessages = @ ( )
$ErrorMessages = @ ( )
foreach ( $link in $DownloadLinks )
foreach ( $link in $DownloadLinks ) {
{
Say-Verbose " Downloading `" $( $link . type ) `" link $( $link . downloadLink ) "
Say-Verbose " Downloading `" $( $link . type ) `" link $( $link . downloadLink ) "
try {
try {
@ -1244,7 +1329,8 @@ foreach ($link in $DownloadLinks)
if ( $PSItem . Exception . Data . Contains ( " ErrorMessage " ) ) {
if ( $PSItem . Exception . Data . Contains ( " ErrorMessage " ) ) {
$ErrorMessage = $PSItem . Exception . Data [ " ErrorMessage " ]
$ErrorMessage = $PSItem . Exception . Data [ " ErrorMessage " ]
} else {
}
else {
$ErrorMessage = $PSItem . Exception . Message
$ErrorMessage = $PSItem . Exception . Message
}
}
@ -1289,51 +1375,54 @@ if (!$isAssetInstalled) {
throw " `" $assetName `" with version = $( $DownloadedLink . effectiveVersion ) failed to install with an unknown error. "
throw " `" $assetName `" with version = $( $DownloadedLink . effectiveVersion ) failed to install with an unknown error. "
}
}
if ( -not $KeepZip ) {
SafeRemoveFile -Path $ZipPath
SafeRemoveFile -Path $ZipPath
}
Measure-Action " Setting up shell environment " { Prepend-Sdk -InstallRoot -To -Path -InstallRoot $InstallRoot }
Measure-Action " Setting up shell environment " { Prepend-Sdk -InstallRoot -To -Path -InstallRoot $InstallRoot }
Say " Note that the script does not resolve dependencies during installation."
Say " Note that the script does not ensure your Windows version is supported during the installation."
Say " To check the list of dependencies, go to https://learn.microsoft.com/dotnet/core/install/windows#dependencie s"
Say " To check the list of supported versions, go to https://learn.microsoft.com/dotnet/core/install/windows#supported-version s"
Say " Installed version is $( $DownloadedLink . effectiveVersion ) "
Say " Installed version is $( $DownloadedLink . effectiveVersion ) "
Say " Installation finished "
Say " Installation finished "
# SIG # Begin signature block
# SIG # Begin signature block
# MII nvwYJKoZIhvcNAQcCoIInsDCCJ6w CAQExDzANBglghkgBZQMEAgEFADB5Bgor
# MII oRgYJKoZIhvcNAQcCoIIoNzCCKDM CAQExDzANBglghkgBZQMEAgEFADB5Bgor
# BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG
# BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG
# KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABC BhfTi3SRn7+vyy
# KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABC AA6hOL3sfG/4jH
# uCXKPjhiawegWZ493EcaOEycbgkZcKCCDXYwggX0MIID3KADAgECAhMzAAACy7d1
# iO4VqZoOTVqC+yp2rOhb1M2cc+ic7KCCDXYwggX0MIID3KADAgECAhMzAAAEBGx0
# OfsCcUI2AAAAAALL MA0GCSqGSIb3DQEBCwUAMH4xCzAJBgNVBAYTAlVTMRMwEQYD
# Bv9XKydyAAAAAAQE MA0GCSqGSIb3DQEBCwUAMH4xCzAJBgNVBAYTAlVTMRMwEQYD
# VQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNy
# VQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNy
# b3NvZnQgQ29ycG9yYXRpb24xKDAmBgNVBAMTH01pY3Jvc29mdCBDb2RlIFNpZ25p
# b3NvZnQgQ29ycG9yYXRpb24xKDAmBgNVBAMTH01pY3Jvc29mdCBDb2RlIFNpZ25p
# bmcgUENBIDIwMTEwHhcNMj IwNTEyMjA0NTU5WhcNMjMwNTExMjA0NTU5 WjB0MQsw
# bmcgUENBIDIwMTEwHhcNMj QwOTEyMjAxMTE0WhcNMjUwOTExMjAxMTE0 WjB0MQsw
# CQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9u
# CQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9u
# ZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMR4wHAYDVQQDExVNaWNy
# ZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMR4wHAYDVQQDExVNaWNy
# b3NvZnQgQ29ycG9yYXRpb24wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB
# b3NvZnQgQ29ycG9yYXRpb24wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB
# AQC 3sN0WcdGpGXPZIb5iNfFB0xZ8rnJvYnxD6Uf2BHXglpbTEfoe+mO//oLWkRxA
# AQC 0KDfaY50MDqsEGdlIzDHBd6CqIMRQWW9Af1LHDDTuFjfDsvna0nEuDSYJmNyz
# wppditsSVOD0oglKbtnh9Wp2DARLcxbGaW4YanOWSB1LyLRpHnnQ5POlh2U5trg4
# NB10jpbg0lhvkT1AzfX2TLITSXwS8D+mBzGCWMM/wTpciWBV/pbjSazbzoKvRrNo
# 3gQjvlNZlQB3lL+zrPtbNvMA7E0Wkmo+Z6YFnsf7aek+KGzaGboAeFO4uKZjQXY5
# DV/u9omOM2Eawyo5JJJdNkM2d8qzkQ0bRuRd4HarmGunSouyb9NY7egWN5E5lUc3
# RmMzE70Bwaz7hvA05jDURdRKH0i/1yK96TDuP7JyRFLOvA3UXNWz00R9w7ppMDcN
# a2AROzAdHdYpObpCOdeAY2P5XqtJkk79aROpzw16wCjdSn8qMzCBzR7rvH2WVkvF
# lXtrmbPigv3xE9FfpfmJRtiOZQKd73K72Wujmj6/Su3+DBTpOq7NgdntW2lJfX3X
# HLIxZQET1yhPb6lRmpgBQNnzidHV2Ocxjc8wNiIDzgbDkmlx54QPfw7RwQi8p1fy
# a6oe4F9Pk9xRhkwHsk7Ju9E/ AgMBAAGjggFzMIIBbzAfBgNVHSUEGDAWBgorBgEE
# 4byhBrTjv568x8NGv3gwb0Rb AgMBAAGjggFzMIIBbzAfBgNVHSUEGDAWBgorBgEE
# AYI3TAgBBggrBgEFBQcDAzAdBgNVHQ4EFgQU rg/nt/gj+BBLd1jZWYhok7v5/w 4w
# AYI3TAgBBggrBgEFBQcDAzAdBgNVHQ4EFgQU 8huhNbETDU+ZWllL4DNMPCijEU 4w
# RQYDVR0RBD4wPKQ6MDgxHjAcBgNVBAsTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEW
# RQYDVR0RBD4wPKQ6MDgxHjAcBgNVBAsTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEW
# MBQGA1UEBRMNMjMwMDEyKz Q3MDUyOD AfBgNVHSMEGDAWgBRIbmTlUAXTgqoXNzci
# MBQGA1UEBRMNMjMwMDEyKz UwMjkyMz AfBgNVHSMEGDAWgBRIbmTlUAXTgqoXNzci
# tW2oynUClTBUBgNVHR8ETTBLMEmgR6BFhkNodHRwOi8vd3d3Lm1pY3Jvc29mdC5j
# tW2oynUClTBUBgNVHR8ETTBLMEmgR6BFhkNodHRwOi8vd3d3Lm1pY3Jvc29mdC5j
# b20vcGtpb3BzL2NybC9NaWNDb2RTaWdQQ0EyMDExXzIwMTEtMDctMDguY3JsMGEG
# b20vcGtpb3BzL2NybC9NaWNDb2RTaWdQQ0EyMDExXzIwMTEtMDctMDguY3JsMGEG
# CCsGAQUFBwEBBFUwUzBRBggrBgEFBQcwAoZFaHR0cDovL3d3dy5taWNyb3NvZnQu
# CCsGAQUFBwEBBFUwUzBRBggrBgEFBQcwAoZFaHR0cDovL3d3dy5taWNyb3NvZnQu
# Y29tL3BraW9wcy9jZXJ0cy9NaWNDb2RTaWdQQ0EyMDExXzIwMTEtMDctMDguY3J0
# Y29tL3BraW9wcy9jZXJ0cy9NaWNDb2RTaWdQQ0EyMDExXzIwMTEtMDctMDguY3J0
# MAwGA1UdEwEB/wQCMAAwDQYJKoZIhvcNAQELBQADggIBA JL5t6pVjIRlQ8j4dAFJ
# MAwGA1UdEwEB/wQCMAAwDQYJKoZIhvcNAQELBQADggIBA IjmD9IpQVvfB1QehvpC
# ZnMke3rRHeQDOPFxswM47HRvgQa2E1jea2aYiMk1WmdqWnYw1bal4IzRlSVf4czf
# Ge7QeTQkKQ7j3bmDMjwSqFL4ri6ae9IFTdpywn5smmtSIyKYDn3/nHtaEn0X1NBj
# zx2vjOIOiaGllW2ByHkfKApngOzJmAQ8F15xSHPRvNMmvpC3PFLvKMf3y5SyPJxh
# L5oP0BjAy1sqxD+uy35B+V8wv5GrxhMDJP8l2QjLtH/UglSTIhLqyt8bUAqVfyfp
# 922TTq0q5epJv1SgZDWlUlHL/Ex1nX8kzBRhHvc6D6F5la+oAO4A3o/ZC05OOgm4
# h4COMRvwwjTvChtCnUXXACuCXYHWalOoc0OU2oGN+mPJIJJxaNQc1sjBsMbGIWv3
# EJxZP9MqUi5iid2dw4Jg/HvtDpCcLj1GLIhCDaebKegajCJlMhhxnDXrGFLJfX8j
# cmgSHkCEmrMv7yaidpePt6V+yPMik+eXw3IfZ5eNOiNgL1rZzgSJfTnvUqiaEQ0X
# 7k7LUvrZDsQniJZ3D66K+3SZTLhvwK7dMGVFuUUJUfDifrlCTjKG9mxsPDllfyck
# dG1HbkDv9fv6CTq6m4Ty3IzLiwGSXYxRIXTxT4TYs5VxHy2uFjFXWVSL0J2ARTYL
# 4zGnRZv8Jw9RgE1zAghnU14L0vVUNOzi/4bE7wIsiRyIcCcVoXRneBA3n/frLXvd
# E4Oyl1wXDF1PX4bxg1yDMfKPHcE1Ijic5lx1KdK1SkaEJdto4hd++05J9Bf9TAmi
# jDsbb2lpGu78+s1zbO5N0bhHWq4j5WMutrspBxEhqG2PSBjC5Ypi+jhtfu3+x76N
# u6EK6C9Oe5vRadroJCK26uCUI4zIjL/qG7mswW+qT0CW0gnR9JHkXCWNbo8ccMk1
# mBvsyKuxx9+Hm/ALnlzKxr4KyMR3/z4IRMzA1QyppNk65Ui+jB14g+w4vole33M1
# sJatmRoSAifbgzaYbUz8+lv+IXy5GFuAmLnNbGjacB3IMGpa+lbFgih57/fIhamq
# pVqVckrmSebUkmjnCshCiH12IFgHZF7gRwE4YZrJ7QjxZeoZqHaKsQLRMp653beB
# 5VhxgaEmn/UjWyr+cPiAFWuTVIpfsOjbEAww75wURNM1Imp9NJKye1O24EspEHmb
# fHfeva9zJPhBSdVcCW7x9q0c2HVPLJHX9YCUU714I+qtLpDGrdbZxD9mikPqL/To
# DmqCUcq7NqkOKIG4PVm3hDDED/WQpzJDkvu4FrIbvyTGVU01vKsg4UfcdiZ0fQ+/
# /1lDZ0ch8FtePhME7houuoPc MIIHejCCBWKgAwIBAgIKYQ6Q0gAAAAAAAzANBgkq
# V0hf8yrtq9CkB8iIuk5bBxuP MIIHejCCBWKgAwIBAgIKYQ6Q0gAAAAAAAzANBgkq
# hkiG9w0BAQsFADCBiDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24x
# hkiG9w0BAQsFADCBiDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24x
# EDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlv
# EDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlv
# bjEyMDAGA1UEAxMpTWljcm9zb2Z0IFJvb3QgQ2VydGlmaWNhdGUgQXV0aG9yaXR5
# bjEyMDAGA1UEAxMpTWljcm9zb2Z0IFJvb3QgQ2VydGlmaWNhdGUgQXV0aG9yaXR5
@ -1373,142 +1462,144 @@ Say "Installation finished"
# XJbYANahRr1Z85elCUtIEJmAH9AAKcWxm6U/RXceNcbSoqKfenoi+kiVH6v7RyOA
# XJbYANahRr1Z85elCUtIEJmAH9AAKcWxm6U/RXceNcbSoqKfenoi+kiVH6v7RyOA
# 9Z74v2u3S5fi63V4GuzqN5l5GEv/1rMjaHXmr/r8i+sLgOppO6/8MO0ETI7f33Vt
# 9Z74v2u3S5fi63V4GuzqN5l5GEv/1rMjaHXmr/r8i+sLgOppO6/8MO0ETI7f33Vt
# Y5E90Z1WTk+/gFcioXgRMiF670EKsT/7qMykXcGhiJtXcVZOSEXAQsmbdlsKgEhr
# Y5E90Z1WTk+/gFcioXgRMiF670EKsT/7qMykXcGhiJtXcVZOSEXAQsmbdlsKgEhr
# /Xmfwb1tbWrJUnMTDXpQzTGCG Z8wghmb AgEBMIGVMH4xCzAJBgNVBAYTAlVTMRMw
# /Xmfwb1tbWrJUnMTDXpQzTGCG iYwghoi AgEBMIGVMH4xCzAJBgNVBAYTAlVTMRMw
# EQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVN
# EQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVN
# aWNyb3NvZnQgQ29ycG9yYXRpb24xKDAmBgNVBAMTH01pY3Jvc29mdCBDb2RlIFNp
# aWNyb3NvZnQgQ29ycG9yYXRpb24xKDAmBgNVBAMTH01pY3Jvc29mdCBDb2RlIFNp
# Z25pbmcgUENBIDIwMTECEzMAAA LLt3U5+wJxQjYAAAAAAss wDQYJYIZIAWUDBAIB
# Z25pbmcgUENBIDIwMTECEzMAAA QEbHQG/1crJ3IAAAAABAQ wDQYJYIZIAWUDBAIB
# BQCgga4wGQYJKoZIhvcNAQkDMQwGCisGAQQBgjcCAQQwHAYKKwYBBAGCNwIBCzEO
# BQCgga4wGQYJKoZIhvcNAQkDMQwGCisGAQQBgjcCAQQwHAYKKwYBBAGCNwIBCzEO
# MAwGCisGAQQBgjcCARUwLwYJKoZIhvcNAQkEMSIEI FmuaTXYQ37AFvsEol24fdW+
# MAwGCisGAQQBgjcCARUwLwYJKoZIhvcNAQkEMSIEI L7Zm9jjqasUipeS7XNbT5Gz
# nRqHcc1fr+VQVdqhXc/v MEIGCisGAQQBgjcCAQwxNDAyoBSAEgBNAGkAYwByAG8A
# uhEwSf09z2Ab+694mR/3 MEIGCisGAQQBgjcCAQwxNDAyoBSAEgBNAGkAYwByAG8A
# cwBvAGYAdKEagBhodHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20wDQYJKoZIhvcNAQEB
# cwBvAGYAdKEagBhodHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20wDQYJKoZIhvcNAQEB
# BQAEggEA jY5XW5Ly7TJ1OTbeIR98xU+2dmtw7L71ws+ICnQCGhj2xJDUK+5yrTfO
# BQAEggEA fTNcpMwgkFxkb0hBch2MCvTb1mGCFv8rZWTkR/aRZTyzuAIEb2GfL4qB
# 8C98l/P4ynFi33Dl8z2YElqUCuqEXbiCzz06lIL4NuibC5DV/X80ZmICR/NYd2v1
# rPycLC2+q4gaksj1Cv+mRTEq+ysl0aWbXgPiRNiijlnuWKRPZ4nlcGkeXu5zxJ1W
# ww7IH+7dpsHAowBBindCYpVwQ3Ea3kDWgsjPAinAysFFushSOnNWFvrF6vi2smrs
# uUOCIe03s6eJCUZseRZkNHB1/CqIlk/YB5yqB38cfq6ct+lWKoSCbSwRVh3Du6am
# smbrAAhEhSfLd1Pxxdw73hQ0YjM/D3F3opaybMQ0blpHhOaqtbiyYzvk0doIzBEc
# jxnQRa4njduu1xywcKZYp9NGGeAgRDpMNbvFKF4Qf3krbTAn3vIVDBay6oeiHo2I
# trSH4NDIc3yLNj5VbjSczpexE+hyQNY4xCtwco4bVtXhONUihv08AIKR8+sIaI7A
# x1RLrRC/CEYZ7oJ8tyc3SUE2/Jd00M4EKax+z3xTIkOmyMBZjEe1el92WVcUWukT
# mM/SWrrwGYSSSxydKqDei7biKG4jDqGCFykwghclBgorBgEEAYI3AwMBMYIXFT CC
# ACoQjF5jPyXnfYGH7rjevjpI5u2T66GCF7AwghesBgorBgEEAYI3AwMBMYIXnD CC
# F xEGCSqGSIb3DQEHAqCCFwIwghb+AgEDMQ8wDQYJYIZIAWUDBAIBBQAwggFZ Bgsq
# F 5gGCSqGSIb3DQEHAqCCF4kwgheFAgEDMQ8wDQYJYIZIAWUDBAIBBQAwggFa Bgsq
# hkiG9w0BCRABBKCCAU gEggFEMIIBQA IBAQYKKwYBBAGEWQoDATAxMA0GCWCGSAFl
# hkiG9w0BCRABBKCCAU kEggFFMIIBQQ IBAQYKKwYBBAGEWQoDATAxMA0GCWCGSAFl
# AwQCAQUABCB 6Hzt2gUb/WZK8fvVnOocriE4rYr6mscZi3gZnBCpiigIGZBr2iMZU
# AwQCAQUABCB jHcYL0Rw5C6IE3Lyb3B0i9qsTzN6j8bzChm+bMp97RgIGZ2Ld17Jt
# GBMyMDI zMDMzMTE1MjEwNi41MTZaMASAAgH0oIHYpIHVMIHS MQswCQYDVQQGEwJV
# GBMyMDI 1MDExMjAwNDMxNy4yNTZaMASAAgH0oIHZpIHWMIHT MQswCQYDVQQGEwJV
# UzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UE
# UzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UE
# ChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMS0wKwYDVQQLEyRNaWNyb3NvZnQgSXJl
# ChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMS0wKwYDVQQLEyRNaWNyb3NvZnQgSXJl
# bGFuZCBPcGVyYXRpb25zIExpbWl0ZWQxJjAkBgNVBAsTHVRoYWxlcyBUU1MgRVNO
# bGFuZCBPcGVyYXRpb25zIExpbWl0ZWQxJzAlBgNVBAsTHm5TaGllbGQgVFNTIEVT
# OjA4NDItNEJFNi1DMjlBMSUwIwYDVQQDExxNaWNyb3NvZnQgVGltZS1TdGFtcCBT
# Tjo0MzFBLTA1RTAtRDk0NzElMCMGA1UEAxMcTWljcm9zb2Z0IFRpbWUtU3RhbXAg
# ZXJ2aWNloIIReDCCBycwggUPoAMCAQICEzMAAAGybkADf26plJIAAQAAAbIwDQYJ
# U2VydmljZaCCEf4wggcoMIIFEKADAgECAhMzAAAB+vs7RNN3M8bTAAEAAAH6MA0G
# KoZIhvcNAQELBQAwfDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24x
# CSqGSIb3DQEBCwUAMHwxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9u
# EDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlv
# MRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRp
# bjEmMCQGA1UEAxMdTWljcm9zb2Z0IFRpbWUtU3RhbXAgUENBIDIwMTAwHhcNMjIw
# b24xJjAkBgNVBAMTHU1pY3Jvc29mdCBUaW1lLVN0YW1wIFBDQSAyMDEwMB4XDTI0
# OTIwMjAyMjAxWhcNMjMxMjE0MjAyMjAxWjCB0jELMAkGA1UEBhMCVVMxEzARBgNV
# MDcyNTE4MzExMVoXDTI1MTAyMjE4MzExMVowgdMxCzAJBgNVBAYTAlVTMRMwEQYD
# BAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jv
# VQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNy
# c29mdCBDb3Jwb3JhdGlvbjEtMCsGA1UECxMkTWljcm9zb2Z0IElyZWxhbmQgT3Bl
# b3NvZnQgQ29ycG9yYXRpb24xLTArBgNVBAsTJE1pY3Jvc29mdCBJcmVsYW5kIE9w
# cmF0aW9ucyBMaW1pdGVkMSYwJAYDVQQLEx1UaGFsZXMgVFNTIEVTTjowODQyLTRC
# ZXJhdGlvbnMgTGltaXRlZDEnMCUGA1UECxMeblNoaWVsZCBUU1MgRVNOOjQzMUEt
# RTYtQzI5QTElMCMGA1UEAxMcTWljcm9zb2Z0IFRpbWUtU3RhbXAgU2VydmljZTCC
# MDVFMC1EOTQ3MSUwIwYDVQQDExxNaWNyb3NvZnQgVGltZS1TdGFtcCBTZXJ2aWNl
# AiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMqiZTIde/lQ4rC+Bml5f/Wu
# MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAyhZVBM3PZcBfEpAf7fII
# q/xKTxrfbG23HofmQ+qZAN4GyO73PF3y9OAfpt7Qf2jcldWOGUB+HzBuwllYyP3f
# hygwYVVP64USeZbSlRR3pvJebva0LQCDW45yOrtpwIpGyDGX+EbCbHhS5Td4J0Yl
# x4MY8zvuAuB37FvoytnNC2DKnVrVlHOVcGUL9CnmhDNMA2/nskjIf2IoiG9J0qLY
# c83ztLEbbQD7M6kqR0Xj+n82cGse/QnMH0WRZLnwggJdenpQ6UciM4nMYZvdQjyb
# r8duvHdQJ9Li2Pq9guySb9mvUL60ogslCO9gkh6FiEDwMrwUr8Wja6jFpUTny8tg
# A4qejOe9Y073JlXv3VIbdkQH2JGyT8oB/LsvPL/kAnJ45oQIp7Sx57RPQ/0O6qay
# 0N0cnCN2w4fKkp5qZcbUYFYicLSb/6A7pHCtX6xnjqwhmJoib3vkKJyVxbuFLRhV
# J2SJrwcjA8auMdAnZKOixFlzoooh7SyycI7BENHTpkVKrRV5YelRvWNTg1pH4EC2
# XxH95b0LHeNhifn3jvo2j+/4QV10jEpXVW+iC9BsTtR69xvTjU51ZgP7BR4YDEWq
# KO2bxsBN23btMeTvZFieGIr+D8mf1lQQs0Ht/tMOVdah14t7Yk+xl5P4Tw3xfAGg
# 7JsylSOv5B5THTDXRf184URzFhTyb8OZQKY7mqMh7c8J8w1sEM4XDUF2UZNy829N
# Hsvsa6ugrxwmKTTX1kqXH5XCdw3TVeKCax6JV+ygM5i1NroJKwBCW11Pwi0z/ki9
# VCzG2tfdEXZaHxF8RmxpQYBxyhZwY1rotuIS+gfN2eq+hkAT3ipGn8/KmDwDtzAb
# 0ZeO6XfEE9mCnJm76Qcxi3tnW/Y/3ZumKQ6X/iVIJo7Lk0Z/pATRwAINqwdvzpdt
# nfuXjApgeZqwgcYJ8pDJ+y/xU6ouzJz1Bve5TTihkiA7wQsQe6R60Zk9dPdNzw0M
# X2hOJib4GR8is2bpKks04GurfweWPn9z6jY7GBC+js8pSwGewrffwgAbNKm82ZDF
# K5niRzuQZAt4GI96FhjhlUWcUZOCkv/JXM/OGu/rgSplYwdmPLzzfDtXyuy/GCU5
# vqBGQQVJwIHSXpjkS+G39eyYOG2rcILBIDlzUzMFFJbNh5tDv3GeJ3EKvC4vNSAx
# I4l08g6iifXypMgoYkkceOAAz4vx1x0BOnZWfI3fSwqNUvoN7ncTT+MB4Vpvf1QB
# tGfaG/mQhK43YjevsB72LouU78rxtNhuMXSzaHq5fFiG3zcsYHaa4+w+YmMrhTEz
# ppjBAQUuvui6eCG0MCVNAgMBAAGjggFJMIIBRTAdBgNVHQ4EFgQUmfIngFzZEZlP
# D4SAish35BjoXP1P1Ct4Va0CAwEAAaOCAUkwggFFMB0GA1UdDgQWBBRjjHKbL5WV
# kjDOVluBSDDaanEwHwYDVR0jBBgwFoAUn6cVXQBeYl2D9OXSZacbUzUZ6XIwXwYD
# 6kd06KocQHphK9U/vzAfBgNVHSMEGDAWgBSfpxVdAF5iXYP05dJlpxtTNRnpcjBf
# VR0fBFgwVjBUoFKgUIZOaHR0cDovL3d3dy5taWNyb3NvZnQuY29tL3BraW9wcy9j
# BgNVHR8EWDBWMFSgUqBQhk5odHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20vcGtpb3Bz
# cmwvTWljcm9zb2Z0JTIwVGltZS1TdGFtcCUyMFBDQSUyMDIwMTAoMSkuY3JsMGwG
# L2NybC9NaWNyb3NvZnQlMjBUaW1lLVN0YW1wJTIwUENBJTIwMjAxMCgxKS5jcmww
# CCsGAQUFBwEBBGAwXjBcBggrBgEFBQcwAoZQaHR0cDovL3d3dy5taWNyb3NvZnQu
# bAYIKwYBBQUHAQEEYDBeMFwGCCsGAQUFBzAChlBodHRwOi8vd3d3Lm1pY3Jvc29m
# Y29tL3BraW9wcy9jZXJ0cy9NaWNyb3NvZnQlMjBUaW1lLVN0YW1wJTIwUENBJTIw
# dC5jb20vcGtpb3BzL2NlcnRzL01pY3Jvc29mdCUyMFRpbWUtU3RhbXAlMjBQQ0El
# MjAxMCgxKS5jcnQwDAYDVR0TAQH/BAIwADAWBgNVHSUBAf8EDDAKBggrBgEFBQcD
# MjAyMDEwKDEpLmNydDAMBgNVHRMBAf8EAjAAMBYGA1UdJQEB/wQMMAoGCCsGAQUF
# CDAOBgNVHQ8BAf8EBAMCB4AwDQYJKoZIhvcNAQELBQADggIBANxHtu3FzIabaDbW
# BwMIMA4GA1UdDwEB/wQEAwIHgDANBgkqhkiG9w0BAQsFAAOCAgEAuFbCorFrvodG
# qswdKBlAhKXRCN+5CSMiv2TYa4i2QuWIm+99piwAhDhADfbqor1zyLi95Y6GQnvI
# +ZNJH3Y+Nz5QpUytQVObOyYFrgcGrxq6MUa4yLmxN4xWdL1kygaW5BOZ3xBlPY7V
# WUgdeC7oL1ZtZye92zYK+EIfwYZmhS+CH4infAzUvscHZF3wlrJUfPUIDGVP0lCY
# puf5b5eaXP7qRq61xeOrX3f64kGiSWoRi9EJawJWCzJfUQRThDL4zxI2pYc1wnPp
# Vse9mguvG0dqkY4ayQPEHOvJubgZZaOdg/N8dInd6fGeOc+0DoGzB+LieObJ2Q0A
# 7Q695bHqwZ02eaOBudh/IfEkGe0Ofj6IS3oyZsJP1yatcm4kBqIH6db1+weM4q46
# tEt3XN3iX8Cp6+dZTX8xwE/LvhRwPpb/+nKshO7TVuvenwdTwqB/LT6CNPaElwFe
# NhAfAf070zF6F+IpUHyhtMbQg5+QHfOuyBzrt67CiMJSKcJ3nMVyfNlnv6yvttYz
# KxKrqRTPMbHeg+i+KnBLfwmhEXsMg2s1QX7JIxfvT96md0eiMjiMEO22LbOzmLMN
# LK3wS+0QwJUibLYJMI6FGcSuRxKlq6RjOhK9L3QOjh0VCM11rHM11ZmN0euJbbBC
# d3LINowAnRBAJtX+3/e390B9sMGMHp+a1V+hgs62AopBl0p/00li30DN5wEQ5If3
# VfQEufOLNkG88MFCUNE10SSbM/Og/CbTko0M5wbVvQJ6CqLKjtHSoeoAGPeeX24f
# 5Zk7b/T6pEx6rJUDYCti7zCbikjKTanBnOc99zGMlej5X+fC/k5ExUCrOs3/VzGR
# 5cPYyTcKlbM6LoUdO2P5JSdI5s1JF/On6LiUT50adpRstZajbYEeX/N7RvSbkn0d
# CZt5LvVQSdWqq/QMzTEmim4sbzASK9imEkjNtZZyvC1CsUcD1voFktld4mKMjE+u
# jD3BvT2Of3Wf9gIeaQIHbv1J2O/P5QOPQiVo8+0AKm6M0TKOduihhKxAt/6Yyk17
# DEV3IddD+DrRk94nVzNPSuZXewfVOnXHSeqG7xM3V7fl2aL4v1OhL2+JwO1Tx3B0
# Fv3RIdjT6wiL2qRIEsgOJp3fILw4mQRPu3spRfakSoQe5N0e4HWFf8WW2ZL0+c83
# irO1O9qbNdJk355bntd1RSVKgM22KFBHnoL7Js7pRhBiaKmVTQGoOb+j1Qa7q+ci
# Qzh3VtEPI6Y2e2BO/eWhTYbIbHpqYDfAtAYtaYIde87ZymXG3MO2wUjhL9HvSQzj
# xGo48Vh9k35BDsJS/DLoXFSPDl4mMIIHcTCCBVmgAwIBAgITMwAAABXF52ueAptJ
# oquq+OoUmvfBUcB2e5L6QCHO6qTO7WowggdxMIIFWaADAgECAhMzAAAAFcXna54C
# mQAAAAAAFTANBgkqhkiG9w0BAQsFADCBiDELMAkGA1UEBhMCVVMxEzARBgNVBAgT
# m0mZAAAAAAAVMA0GCSqGSIb3DQEBCwUAMIGIMQswCQYDVQQGEwJVUzETMBEGA1UE
# CBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9z
# b2Z0IENvcnBvcmF0aW9uMTIwMAYDVQQDEylNaWNyb3NvZnQgUm9vdCBDZXJ0aWZp
# Y2F0ZSBBdXRob3JpdHkgMjAxMDAeFw0yMTA5MzAxODIyMjVaFw0zMDA5MzAxODMy
# MjVaMHwxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQH
# EwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xJjAkBgNV
# BAMTHU1pY3Jvc29mdCBUaW1lLVN0YW1wIFBDQSAyMDEwMIICIjANBgkqhkiG9w0B
# AQEFAAOCAg8AMIICCgKCAgEA5OGmTOe0ciELeaLL1yR5vQ7VgtP97pwHB9KpbE51
# yMo1V/YBf2xK4OK9uT4XYDP/XE/HZveVU3Fa4n5KWv64NmeFRiMMtY0Tz3cywBAY
# 6GB9alKDRLemjkZrBxTzxXb1hlDcwUTIcVxRMTegCjhuje3XD9gmU3w5YQJ6xKr9
# cmmvHaus9ja+NSZk2pg7uhp7M62AW36MEBydUv626GIl3GoPz130/o5Tz9bshVZN
# 7928jaTjkY+yOSxRnOlwaQ3KNi1wjjHINSi947SHJMPgyY9+tVSP3PoFVZhtaDua
# Rr3tpK56KTesy+uDRedGbsoy1cCGMFxPLOJiss254o2I5JasAUq7vnGpF1tnYN74
# kpEeHT39IM9zfUGaRnXNxF803RKJ1v2lIH1+/NmeRd+2ci/bfV+AutuqfjbsNkz2
# K26oElHovwUDo9Fzpk03dJQcNIIP8BDyt0cY7afomXw/TNuvXsLz1dhzPUNOwTM5
# TI4CvEJoLhDqhFFG4tG9ahhaYQFzymeiXtcodgLiMxhy16cg8ML6EgrXY28MyTZk
# i1ugpoMhXV8wdJGUlNi5UPkLiWHzNgY1GIRH29wb0f2y1BzFa/ZcUlFdEtsluq9Q
# BXpsxREdcu+N+VLEhReTwDwV2xo3xwgVGD94q0W29R6HXtqPnhZyacaue7e3Pmri
# Lq0CAwEAAaOCAd0wggHZMBIGCSsGAQQBgjcVAQQFAgMBAAEwIwYJKwYBBAGCNxUC
# BBYEFCqnUv5kxJq+gpE8RjUpzxD/LwTuMB0GA1UdDgQWBBSfpxVdAF5iXYP05dJl
# pxtTNRnpcjBcBgNVHSAEVTBTMFEGDCsGAQQBgjdMg30BATBBMD8GCCsGAQUFBwIB
# FjNodHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20vcGtpb3BzL0RvY3MvUmVwb3NpdG9y
# eS5odG0wEwYDVR0lBAwwCgYIKwYBBQUHAwgwGQYJKwYBBAGCNxQCBAweCgBTAHUA
# YgBDAEEwCwYDVR0PBAQDAgGGMA8GA1UdEwEB/wQFMAMBAf8wHwYDVR0jBBgwFoAU
# 1fZWy4/oolxiaNE9lJBb186aGMQwVgYDVR0fBE8wTTBLoEmgR4ZFaHR0cDovL2Ny
# bC5taWNyb3NvZnQuY29tL3BraS9jcmwvcHJvZHVjdHMvTWljUm9vQ2VyQXV0XzIw
# MTAtMDYtMjMuY3JsMFoGCCsGAQUFBwEBBE4wTDBKBggrBgEFBQcwAoY+aHR0cDov
# L3d3dy5taWNyb3NvZnQuY29tL3BraS9jZXJ0cy9NaWNSb29DZXJBdXRfMjAxMC0w
# Ni0yMy5jcnQwDQYJKoZIhvcNAQELBQADggIBAJ1VffwqreEsH2cBMSRb4Z5yS/yp
# b+pcFLY+TkdkeLEGk5c9MTO1OdfCcTY/2mRsfNB1OW27DzHkwo/7bNGhlBgi7ulm
# ZzpTTd2YurYeeNg2LpypglYAA7AFvonoaeC6Ce5732pvvinLbtg/SHUB2RjebYIM
# 9W0jVOR4U3UkV7ndn/OOPcbzaN9l9qRWqveVtihVJ9AkvUCgvxm2EhIRXT0n4ECW
# OKz3+SmJw7wXsFSFQrP8DJ6LGYnn8AtqgcKBGUIZUnWKNsIdw2FzLixre24/LAl4
# FOmRsqlb30mjdAy87JGA0j3mSj5mO0+7hvoyGtmW9I/2kQH2zsZ0/fZMcm8Qq3Uw
# xTSwethQ/gpY3UA8x1RtnWN0SCyxTkctwRQEcb9k+SS+c23Kjgm9swFXSVRk2XPX
# fx5bRAGOWhmRaw2fpCjcZxkoJLo4S5pu+yFUa2pFEUep8beuyOiJXk+d0tBMdrVX
# VAmxaQFEfnyhYWxz/gq77EFmPWn9y8FBSX5+k77L+DvktxW/tM4+pTFRhLy/AsGC
# onsXHRWJjXD+57XQKBqJC4822rpM+Zv/Cuk0+CQ1ZyvgDbjmjJnW4SLq8CdCPSWU
# 5nR0W2rRnj7tfqAxM328y+l7vzhwRNGQ8cirOoo6CGJ/2XBjU02N7oJtpQUQwXEG
# ahC0HVUzWLOhcGbyoYIDWTCCAkECAQEwggEBoYHZpIHWMIHTMQswCQYDVQQGEwJV
# UzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UE
# ChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMS0wKwYDVQQLEyRNaWNyb3NvZnQgSXJl
# bGFuZCBPcGVyYXRpb25zIExpbWl0ZWQxJzAlBgNVBAsTHm5TaGllbGQgVFNTIEVT
# Tjo0MzFBLTA1RTAtRDk0NzElMCMGA1UEAxMcTWljcm9zb2Z0IFRpbWUtU3RhbXAg
# U2VydmljZaIjCgEBMAcGBSsOAwIaAxUA94Z+bUJn+nKwBvII6sg0Ny7aPDaggYMw
# gYCkfjB8MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UE
# BxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSYwJAYD
# VQQDEx1NaWNyb3NvZnQgVGltZS1TdGFtcCBQQ0EgMjAxMDANBgkqhkiG9w0BAQsF
# AAIFAOss/ykwIhgPMjAyNTAxMTExNDMxMDVaGA8yMDI1MDExMjE0MzEwNVowdzA9
# BgorBgEEAYRZCgQBMS8wLTAKAgUA6yz/KQIBADAKAgEAAgIpggIB/zAHAgEAAgIT
# XjAKAgUA6y5QqQIBADA2BgorBgEEAYRZCgQCMSgwJjAMBgorBgEEAYRZCgMCoAow
# CAIBAAIDB6EgoQowCAIBAAIDAYagMA0GCSqGSIb3DQEBCwUAA4IBAQCHE6DSGdY4
# KF25iAsxQP9F9Lz6ye/vrWGv+j0aSzSbjHVM3kMcEmX9278XgAKgAYII/f16uDtE
# 7VlEwnKGXujGF249I864U50QFt9hIxqCeuvrshDq8a4Q4KVmuDTosYjS114IJeBK
# LMOBRgLQCIC+wmvdP4EeYH1tnMIEASFvptE+XBro44/A5pmx5UiDJRL1AG4+aO3x
# 13psQu7H3thmbGy7Sf0Azjx0PZ+1QUVI7jWNk9DWjGd18G4SQD8Uxeh0v73/dQx1
# XsFhsyvnrw6uUrxkoAdurif9kyKS+ppo4j9ZkPXzzuc95s1bPcPAyjXCu07Tlunj
# sXttGVEPQIeXMYIEDTCCBAkCAQEwgZMwfDELMAkGA1UEBhMCVVMxEzARBgNVBAgT
# Cldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29m
# Cldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29m
# dCBDb3Jwb3JhdGlvbjEyMDAGA1UEAxMpTWljcm9zb2Z0IFJvb3QgQ2VydGlmaWNh
# dCBDb3Jwb3JhdGlvbjEmMCQGA1UEAxMdTWljcm9zb2Z0IFRpbWUtU3RhbXAgUENB
# dGUgQXV0aG9yaXR5IDIwMTAwHhcNMjEwOTMwMTgyMjI1WhcNMzAwOTMwMTgzMjI1
# IDIwMTACEzMAAAH6+ztE03czxtMAAQAAAfowDQYJYIZIAWUDBAIBBQCgggFKMBoG
# WjB8MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMH
# CSqGSIb3DQEJAzENBgsqhkiG9w0BCRABBDAvBgkqhkiG9w0BCQQxIgQgxenDb/df
# UmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSYwJAYDVQQD
# q8XJS+q7Oxyca1ryDMmDRA0I3mtr+xYHGZQwgfoGCyqGSIb3DQEJEAIvMYHqMIHn
# Ex1NaWNyb3NvZnQgVGltZS1TdGFtcCBQQ0EgMjAxMDCCAiIwDQYJKoZIhvcNAQEB
# MIHkMIG9BCB98n8tya8+B2jjU/dpJRIwHwHHpco5ogNStYocbkOeVjCBmDCBgKR+
# BQADggIPADCCAgoCggIBAOThpkzntHIhC3miy9ckeb0O1YLT/e6cBwfSqWxOdcjK
# MHwxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdS
# NVf2AX9sSuDivbk+F2Az/1xPx2b3lVNxWuJ+Slr+uDZnhUYjDLWNE893MsAQGOhg
# ZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xJjAkBgNVBAMT
# fWpSg0S3po5GawcU88V29YZQ3MFEyHFcUTE3oAo4bo3t1w/YJlN8OWECesSq/XJp
# HU1pY3Jvc29mdCBUaW1lLVN0YW1wIFBDQSAyMDEwAhMzAAAB+vs7RNN3M8bTAAEA
# rx2rrPY2vjUmZNqYO7oaezOtgFt+jBAcnVL+tuhiJdxqD89d9P6OU8/W7IVWTe/d
# AAH6MCIEIC8gtQ6HRW7jzwlpg15qoYopXwF01KaO1EM5tYzqJwx/MA0GCSqGSIb3
# vI2k45GPsjksUZzpcGkNyjYtcI4xyDUoveO0hyTD4MmPfrVUj9z6BVWYbWg7mka9
# DQEBCwUABIICAIsSn8x3zVS870Zf4pa+jfZjdOq++5dHpeLg46sujQ3w+xj3RyhB
# 7aSueik3rMvrg0XnRm7KMtXAhjBcTyziYrLNueKNiOSWrAFKu75xqRdbZ2De+JKR
# nRa3kjWyU9nNF6hrt0Q+ILOxUt3jCd3hbB1ZuspwbXdoRtRLfuLPvGiSmINdgFR4
# Hh09/SDPc31BmkZ1zcRfNN0Sidb9pSB9fvzZnkXftnIv231fgLrbqn427DZM9itu
# LD/jXLrq9USAHYXHzhuYhaVLIpn7M87TbFuGFVaByjmohZRcPCE8y8b7/RIlGm7B
# qBJR6L8FA6PRc6ZNN3SUHDSCD/AQ8rdHGO2n6Jl8P0zbr17C89XYcz1DTsEzOUyO
# wgx0thZA4lHWFyj8j7CwjmueOJSSZ9an4P9VHFKJ63kYub4J1VxbeApGAeeS32SD
# ArxCaC4Q6oRRRuLRvWoYWmEBc8pnol7XKHYC4jMYctenIPDC+hIK12NvDMk2ZItb
# oI3zDdC+iI+IetR9BUHGcR3Vg7j7c0T+NcrIoPPNb4Ff90Ue24h5RDJMQWrM56ak
# oKaDIV1fMHSRlJTYuVD5C4lh8zYGNRiER9vcG9H9stQcxWv2XFJRXRLbJbqvUAV6
# VEWgVlzhf8CeyeO7/ButBUZu8VLkH0DQraK9UKptZFKOXMELoi/oZL6IJftHp5vU
# bMURHXLvjflSxIUXk8A8FdsaN8cIFRg/eKtFtvUeh17aj54WcmnGrnu3tz5q4i6t
# +sPpF3NuuXw8Z5eL9jZ7A1y+H7nMhdXP2pojHDN213VZqeoUoOZlbFl6spDF1hFP
# AgMBAAGjggHdMIIB2TASBgkrBgEEAYI3FQEEBQIDAQABMCMGCSsGAQQBgjcVAgQW
# 44Fu7TPGEwUNS213Pwln2SJ8SayeVUxsreo4pTvhDl/xZ+B7WNuLL7hatWFGrcf3
# BBQqp1L+ZMSavoKRPEY1Kc8Q/y8E7jAdBgNVHQ4EFgQUn6cVXQBeYl2D9OXSZacb
# w/HiVCoTfsY49SaN6zAK6akS3KI6KZHfzjaxDw+4LHo8gL68Ik1HZe4W1jaLYaED
# UzUZ6XIwXAYDVR0gBFUwUzBRBgwrBgEEAYI3TIN9AQEwQTA/BggrBgEFBQcCARYz
# LWvKIinaH2vwU0J4a+oX+64eSh0tI9Ef3aM6jn9LgqubY36TzptUTWcsM3vv3YGB
# aHR0cDovL3d3dy5taWNyb3NvZnQuY29tL3BraW9wcy9Eb2NzL1JlcG9zaXRvcnku
# Dnf7LPxSt4/s5bUgAHvkWTjESdtIbt6Pxqz4BRha+ckPYBj968t3mSh6
# aHRtMBMGA1UdJQQMMAoGCCsGAQUFBwMIMBkGCSsGAQQBgjcUAgQMHgoAUwB1AGIA
# QwBBMAsGA1UdDwQEAwIBhjAPBgNVHRMBAf8EBTADAQH/MB8GA1UdIwQYMBaAFNX2
# VsuP6KJcYmjRPZSQW9fOmhjEMFYGA1UdHwRPME0wS6BJoEeGRWh0dHA6Ly9jcmwu
# bWljcm9zb2Z0LmNvbS9wa2kvY3JsL3Byb2R1Y3RzL01pY1Jvb0NlckF1dF8yMDEw
# LTA2LTIzLmNybDBaBggrBgEFBQcBAQROMEwwSgYIKwYBBQUHMAKGPmh0dHA6Ly93
# d3cubWljcm9zb2Z0LmNvbS9wa2kvY2VydHMvTWljUm9vQ2VyQXV0XzIwMTAtMDYt
# MjMuY3J0MA0GCSqGSIb3DQEBCwUAA4ICAQCdVX38Kq3hLB9nATEkW+Geckv8qW/q
# XBS2Pk5HZHixBpOXPTEztTnXwnE2P9pkbHzQdTltuw8x5MKP+2zRoZQYIu7pZmc6
# U03dmLq2HnjYNi6cqYJWAAOwBb6J6Gngugnue99qb74py27YP0h1AdkY3m2CDPVt
# I1TkeFN1JFe53Z/zjj3G82jfZfakVqr3lbYoVSfQJL1AoL8ZthISEV09J+BAljis
# 9/kpicO8F7BUhUKz/AyeixmJ5/ALaoHCgRlCGVJ1ijbCHcNhcy4sa3tuPywJeBTp
# kbKpW99Jo3QMvOyRgNI95ko+ZjtPu4b6MhrZlvSP9pEB9s7GdP32THJvEKt1MMU0
# sHrYUP4KWN1APMdUbZ1jdEgssU5HLcEUBHG/ZPkkvnNtyo4JvbMBV0lUZNlz138e
# W0QBjloZkWsNn6Qo3GcZKCS6OEuabvshVGtqRRFHqfG3rsjoiV5PndLQTHa1V1QJ
# sWkBRH58oWFsc/4Ku+xBZj1p/cvBQUl+fpO+y/g75LcVv7TOPqUxUYS8vwLBgqJ7
# Fx0ViY1w/ue10CgaiQuPNtq6TPmb/wrpNPgkNWcr4A245oyZ1uEi6vAnQj0llOZ0
# dFtq0Z4+7X6gMTN9vMvpe784cETRkPHIqzqKOghif9lwY1NNje6CbaUFEMFxBmoQ
# tB1VM1izoXBm8qGCAtQwggI9AgEBMIIBAKGB2KSB1TCB0jELMAkGA1UEBhMCVVMx
# EzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoT
# FU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEtMCsGA1UECxMkTWljcm9zb2Z0IElyZWxh
# bmQgT3BlcmF0aW9ucyBMaW1pdGVkMSYwJAYDVQQLEx1UaGFsZXMgVFNTIEVTTjow
# ODQyLTRCRTYtQzI5QTElMCMGA1UEAxMcTWljcm9zb2Z0IFRpbWUtU3RhbXAgU2Vy
# dmljZaIjCgEBMAcGBSsOAwIaAxUAjhJ+EeySRfn2KCNsjn9cF9AUSTqggYMwgYCk
# fjB8MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMH
# UmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSYwJAYDVQQD
# Ex1NaWNyb3NvZnQgVGltZS1TdGFtcCBQQ0EgMjAxMDANBgkqhkiG9w0BAQUFAAIF
# AOfRUdUwIhgPMjAyMzAzMzEyMDM0MjlaGA8yMDIzMDQwMTIwMzQyOVowdDA6Bgor
# BgEEAYRZCgQBMSwwKjAKAgUA59FR1QIBADAHAgEAAgIKJDAHAgEAAgIRLzAKAgUA
# 59KjVQIBADA2BgorBgEEAYRZCgQCMSgwJjAMBgorBgEEAYRZCgMCoAowCAIBAAID
# B6EgoQowCAIBAAIDAYagMA0GCSqGSIb3DQEBBQUAA4GBAJlOESCa/uRR1x6GunE8
# K/WgHWTpSE31EITDOfTMvDcF4ptngCS5aOc4gfzmhNNehWfP6EOrgoSQzJYZ4YCh
# fYbHNMk56f18sq8t7y2hgR7KixcEo/4HVzeSdaOclHNc4Gn7kCGpMvpT3Xz9Lzc7
# UKWDZ0zkNKnbS8TZLNueVQwfMYIEDTCCBAkCAQEwgZMwfDELMAkGA1UEBhMCVVMx
# EzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoT
# FU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEmMCQGA1UEAxMdTWljcm9zb2Z0IFRpbWUt
# U3RhbXAgUENBIDIwMTACEzMAAAGybkADf26plJIAAQAAAbIwDQYJYIZIAWUDBAIB
# BQCgggFKMBoGCSqGSIb3DQEJAzENBgsqhkiG9w0BCRABBDAvBgkqhkiG9w0BCQQx
# IgQgXhJRuHCXk3arJvifIY3DBe9Ce9EmlP1y6U4XkgL31DkwgfoGCyqGSIb3DQEJ
# EAIvMYHqMIHnMIHkMIG9BCBTeM485+E+t4PEVieUoFKX7PVyLo/nzu+htJPCG04+
# NTCBmDCBgKR+MHwxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAw
# DgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24x
# JjAkBgNVBAMTHU1pY3Jvc29mdCBUaW1lLVN0YW1wIFBDQSAyMDEwAhMzAAABsm5A
# A39uqZSSAAEAAAGyMCIEIGGWlnNnYHrB5HguWG0/nJd/WvSrCogze+QCpenu3IM5
# MA0GCSqGSIb3DQEBCwUABIICADVOLTuNxeEnBOfZpb7Nv4uf91W/Ho5i99zenDSJ
# x5QHVs+bKXmgc3a7/SSsliAT3zygHc7cH4zARbCZePLTivByKmeG08Ka35eyR+FK
# awSNrI/X+eVIC6nw/egCwviBC1NAG8jHGkuScbHeiiGajvS6lp3ORML7UexMuE4w
# 9SEumoghljCLZMwCSvw+3WxhQoBEZroR8u+PID2RdD0vi85FjKPWcZZijVLqHeFi
# TnuFqwRCLTV0MV+dDCbjwXneIqV+AVlnqb9iDMr3ZhISlRcy9XJNpY5vQBj/wqUW
# vefrmpdz0LNkdtXYThPkyl3mha2KsoQi5SA9zSjlAjFgY3ppmXvi3Frbfqk+iL+f
# l/Qc4+B71jG4t28lTWKteJiHqo+6AUXK2rlAl0d74yvhO6N8lMMtXhdJc8JABYn1
# v2/KKZn5RvPFF8QP7Ac1saIe1+gUFNcsYOLaMm/xl8E6kefWwZnm5Rhm606g1AC/
# N5Wo08aAs0ymTPH91dEbmOURXLbA3vCyG7kbfgnhCs/j7oQHWaFDzEYuXDIA4ICT
# dxPUTltbq3OWdp0PAS8JSEKPQFaOoQEnPa4adrXWxMvOmel8IGqJiQ+BPOaLQG64
# Qu2tMkH/5szb1fsEnCe8SJmy5ESF+kmpnLBtJ17Y9o+9nJHF5ddFmvzy+LUaIqDN
# cOfH
# SIG # End signature block
# SIG # End signature block