modules/AzStack.Utilities/AzStack.Utilities.psm1
<################################################################
# # # Copyright (C) Microsoft Corporation. All rights reserved. # # # ################################################################> Using module .\AzStack.Utilities.Helper.psm1 Import-Module $PSScriptRoot\AzStack.Utilities.Helper.psm1 Import-LocalizedData -BindingVariable 'msg' -BaseDirectory "$PSScriptRoot\locale" -UICulture "en-US" # due to issue with how snap-ins work with JEA endpoints # need to remove it, then re-import the Microsoft.PowerShell.Utility module so we can expose the functions such as Import-PowerShellDataFile if (Get-PSSnapin | Where-Object { $_.Name -eq 'Microsoft.PowerShell.Utility' }) { Remove-PSSnapin -Name Microsoft.PowerShell.Utility } Import-Module Microsoft.PowerShell.Utility <# ┌──────────────────────────────────────────────────────────────────────────┐ │ Here our HCI helper functions start. │ └──────────────────────────────────────────────────────────────────────────┘ #> function Trace-CheckResult() { param ( [string] $checkName, [CheckStatus] $checkState, [string] $desc, [string] $details, [string] $url ) $color = [ConsoleColor]::DarkBlue # we now support different states (besides pass / fail) switch ($checkState) { ([CheckStatus]::Pass) { $color = [ConsoleColor]::Green; $details = "Validation successfull" } ([CheckStatus]::Fail) { $color = [ConsoleColor]::Red } ([CheckStatus]::Info) { $color = [ConsoleColor]::Gray } Default {} } Write-Host -NoNewline ("[" + $checkState + "] ") -ForegroundColor $color Write-Host ("[" + $checkName + "]") -ForegroundColor Gray Write-Host $desc -ForegroundColor White Write-Host -NoNewline "Details: " -ForegroundColor Gray Write-Host $details -ForegroundColor White # we only post the URL when it is specified. Some error messages dont have public docs. if(($null -ne $url) -and ($url -ne "")) { Write-Host -NoNewline "Documentation: " -ForegroundColor Gray Write-Host $url -ForegroundColor Yellow } Write-Host # Blank line for spacing. } <# ┌──────────────────────────────────────────────────────────────────────────┐ │ THIS HAS BEEN PORTED OVER FROM HUB. WE DONT KNOW WHAT WE TRULY NEED. TO │ │ BE CLEANED UP IN THE │ │ FUTURE │ └──────────────────────────────────────────────────────────────────────────┘ #> # create a script variable that is accessible to all the functions within the AzStack.Utilities.psm1 module # this can be used to store configuration type data, only available within the script scope $configurationData = Import-PowerShellDataFile -Path "$PSScriptRoot\AzStack.Utilities.Config.psd1" New-Variable -Name 'AzsSupport_Utilities' -Scope Local -Force -Value @{ Cache = @{ FilesExcludedFromCleanup = @() TraceFilePath = $null WorkingDirectory = $null } Config = $configurationData } function New-WorkingDirectory { [CmdletBinding()] param ( [Parameter(Mandatory = $false)] [System.String]$Path = $script:AzsSupport_Utilities.Config.WorkingDirectory ) try { # create the working directory and set the global cache if (-NOT (Test-Path -Path $Path -PathType Container)) { $null = New-Item -Path $Path -ItemType Directory -Force } # create the trace file New-TraceOutputFile } catch { "{0}`n{1}" -f $_.Exception, $_.ScriptStackTrace | Trace-Output -Level:Error } } function Get-FormattedDateTimeUTC { param ( [Switch]$NoSpace ) if ($NoSpace) { return ([DateTime]::UtcNow.ToString('yyyyMMddHHmmss')) } return ([DateTime]::UtcNow.ToString('yyyyMMdd-HHmmss')) } # Copyright (c) Microsoft Corporation. # Licensed under the MIT License. function Get-TraceOutputFile { return [System.String]$script:AzsSupport_Utilities.Config.TraceFilePath } function Get-AzsSupportTraceEvent { <# .SYNOPSIS Gets the trace events from Get-AzsSupportTraceFilePath. .PARAMETER FunctionName The function name that you want to filter on .PARAMETER Level The log level you want to filter on .PARAMETER IncludeDebugEvents Return debug level events from trace file .EXAMPLE Get-AzsSupportTraceEvent -FunctionName 'New-AzsSupportTraceFilePath' -Level Verbose #> [CmdletBinding()] param ( [Parameter(Mandatory = $false)] [ArgumentCompleter({ param($commandName, $parameterName, $wordToComplete, $commandAst, $FakeboundParameter) $possibleValues = (Import-Csv -Path (Get-TraceOutputFile)).FunctionName | Select-Object -Unique | Sort-Object $possibleValues | Where-Object { $_ -like "$wordToComplete*" } })] [string]$FunctionName, [Parameter(Mandatory = $false)] [ArgumentCompleter({ param($commandName, $parameterName, $wordToComplete, $commandAst, $FakeboundParameter) $possibleValues = (Import-Csv -Path (Get-TraceOutputFile)).Level | Select-Object -Unique | Sort-Object $possibleValues | Where-Object { $_ -like "$wordToComplete*" } })] [string]$Level, [Parameter(Mandatory = $false)] [switch]$IncludeDebugEvents ) try { $traceEvents = Import-Csv -Path (Get-TraceOutputFile) if ($PSBoundParameters['FunctionName']) { $traceEvents = $traceEvents | Where-Object { $_.FunctionName -eq $FunctionName } } if ($PSBoundParameters['Level']) { $traceEvents = $traceEvents | Where-Object { $_.Level -eq $Level } } if (!$IncludeDebugEvents) { $traceEvents = $traceEvents | Where-Object { $_.Level -ne 'Debug' } } return $traceEvents } catch { "{0}`n{1}" -f $_.Exception, $_.ScriptStackTrace | Trace-Output -Level:Error } } function Get-FormattedException { <# .SYNOPSIS Extracts details from an exception that is used to format the error message in a consistent manner. Does not capture the exception message as this might contain PII .PARAMETER Exception An exception thrown by the CLR or with the throw keyword .EXAMPLE PS> try { 10 / 0 } catch { Get-FormattedException -Exception $_.Exception | Write-Host -ForegroundColor Red } .NOTES We return compressed json as this information sent to the telemetry endpoint if customer has it enabled #> param( [System.Exception]$Exception ) $outerTypeName = $null $innerTypeName = $null if ($null -ne $Exception) { $outerTypeName = ($Exception | Get-Member)[0].TypeName } if ($null -ne $Exception.InnerException) { $innerTypeName = ($Exception.InnerException | Get-Member)[0].TypeName } return (@{ ErrorRecord = @{ ScriptStackTrace = $Exception.ErrorRecord.ScriptStackTrace InvocationInfo = $Exception.ErrorRecord.InvocationInfo } OuterException = @{ TypeName = $outerTypeName Source = $Exception.Source StackTrace = $Exception.StackTrace } InnerException = @{ TypeName = $innerTypeName Source = $Exception.InnerException.Source StackTrace = $Exception.InnerException.StackTrace } }) | ConvertTo-Json -Depth 3 -Compress } function Trace-Output { [CmdletBinding()] param ( [Parameter(Mandatory = $true, ValueFromPipeline = $true)] [System.String]$Message, [Parameter(Mandatory = $false)] [TraceLevel]$Level ) begin { if (!$PSBoundParameters.ContainsKey('Level')) { $Level = [TraceLevel]::Information } $traceFile = (Get-TraceOutputFile) if ([string]::IsNullOrEmpty($traceFile)) { New-WorkingDirectory $traceFile = (Get-TraceOutputFile) } } process { # create custom object for formatting purposes $traceEvent = [PSCustomObject]@{ Computer = $env:COMPUTERNAME.ToUpper().ToString() TimestampUtc = [DateTime]::UtcNow.ToString('yyyy-MM-dd HH-mm-ss') FunctionName = (Get-PSCallStack)[1].Command Level = $Level.ToString() Message = $Message } # write the message to the console switch ($Level) { 'Error' { "{0}" -f $traceEvent.Message | Write-Error } 'Exception' { "{0}" -f $traceEvent.Message | Write-Host -ForegroundColor:Red } 'Success' { "{0}" -f $traceEvent.Message | Write-Host -ForegroundColor:Green } 'Verbose' { if ($VerbosePreference -ne [System.Management.Automation.ActionPreference]::SilentlyContinue) { "{0}" -f $traceEvent.Message | Write-Verbose } } 'Warning' { "{0}" -f $traceEvent.Message | Write-Warning } 'Important' { "====[ {0} ]====" -f $traceEvent.Message | Write-Host -ForegroundColor:White -BackgroundColor:DarkGray } 'Detail' { "{0}" -f $traceEvent.Message | Write-Host -ForegroundColor:White } 'Unknown' { "{0}" -f $traceEvent.Message | Write-Host -ForegroundColor:DarkGray } default { "{0}" -f $traceEvent.Message | Write-Host -ForegroundColor:DarkCyan } } # write the event to trace file to be used for debugging purposes $mutexInstance = Wait-OnMutex -MutexId 'AzsSupport_TraceLogging' -ErrorAction Continue if ($mutexInstance) { $traceEvent | Export-Csv -Append -NoTypeInformation -Path $traceFile } } end { if ($mutexInstance) { $mutexInstance.ReleaseMutex() } } } function Wait-OnMutex { param ( [Parameter(Mandatory = $true)] [System.String]$MutexId ) try { $MutexInstance = New-Object System.Threading.Mutex($false, $MutexId) if ($MutexInstance.WaitOne(3000)) { return $MutexInstance } else { throw New-Object -TypeName System.TimeoutException($msg.TimeoutException) } } catch [System.Threading.AbandonedMutexException] { $MutexInstance = New-Object System.Threading.Mutex($false, $MutexId) return (Wait-OnMutex -MutexId $MutexId) } catch { $MutexInstance.ReleaseMutex() $_ | Write-Error } } function Get-WorkingDirectory { # check to see if the working directory has been configured into cache # otherwise set the cache based on what we have defined within our configuration file if ([String]::IsNullOrEmpty($script:AzsSupport_Utilities.Cache.WorkingDirectory)) { $script:AzsSupport_Utilities.Cache.WorkingDirectory = ($script:AzsSupport_Utilities.Config.WorkingDirectory -f (Get-FormattedDateTimeUTC -NoSpace)) } return [System.String]$script:AzsSupport_Utilities.Cache.WorkingDirectory } function Get-AzsSupportWorkingDirectory { return (Get-WorkingDirectory) } function New-TraceOutputFile { try { # make sure that directory path exists, else create the folder structure required $workingDir = Get-WorkingDirectory if (-NOT (Test-Path -Path $workingDir -PathType Container)) { $null = New-Item -Path $workingDir -ItemType Directory -Force } # build the trace file path and set global variable [System.String]$fileName = "AzsSupport_TraceOutput_{0}.csv" -f (Get-Date).ToString('yyyyMMdd') [System.IO.FileInfo]$filePath = Join-Path -Path $workingDir -ChildPath $fileName Set-TraceOutputFile -Path $filePath.FullName # configure the cache to not cleanup the trace file $script:AzsSupport_Utilities.Cache.FilesExcludedFromCleanup += $filePath.Name "TraceFile: {0}" -f $filePath.FullName | Trace-Output -Level:Verbose } catch { $_.Exception | Write-Error } } function Get-TraceOutputFile { return [System.String]$script:AzsSupport_Utilities.Cache.TraceFilePath } function Set-TraceOutputFile { [CmdletBinding()] param ( [Parameter(Mandatory = $true)] [System.String]$Path ) $script:AzsSupport_Utilities.Cache.TraceFilePath = $Path } function Convert-FileSystemPathToUNC { <# .SYNOPSIS Converts a local file path to a computer specific admin UNC path, such as C:\temp\myfile.txt to \\$COMPUTERNAME\c$\temp\myfile.txt .PARAMETER ComputerName The computer name to inject into the unc path .PARAMETER Path The local file system path, such as C:\temp\myfile.txt #> param( [String]$ComputerName, [String]$Path ) $newPath = $path.Replace((Split-Path $Path), (Split-Path $Path).Replace(':', '$')) return ("\\{0}\{1}" -f $ComputerName, $newPath) } function Get-UserInputValues { <# .SYNOPSIS Used to capture information from user and generate a psobject .PARAMETER Properties The psobject properties you want to prompt user to provide .EXAMPLE PS> $results = Get-UserInputValues -Properties "Destination,Port,RetryAttempts" Destination: microsoft.com Port: 80 RetryAttempts: 3 PS> $results Destination Port RetryAttempts ----------- ---- ------------- microsoft.com 80 3 #> param ( [Parameter(Mandatory = $true)] [string]$Properties ) $object = foreach ($property in ($Properties.Split(','))) { $property = $property.Trim() New-Object -TypeName PSCustomObject -Property @{ $($property) = (Get-UserInput -Message "$($property): ").Trim() } } return $object } function Clear-AzsSupportDirectory { <# .SYNOPSIS Clears the contents of the directory .PARAMETER ComputerName Type the NetBIOS name, an IP address, or a fully qualified domain name of one or more remote computers. .PARAMETER Credential Specifies a user account that has permission to perform this action. The default is the current user. Type a user name, such as User01 or Domain01\User01, or enter a PSCredential object generated by the Get-Credential cmdlet. If you type a user name, you're prompted to enter the password. .PARAMETER Path Specifies a path of the items being removed. Wildcard characters are permitted. If ommitted, defaults to (Get-WorkingDirectory). .PARAMETER Recurse Indicates that this cmdlet deletes the items in the specified locations and in all child items of the locations. .PARAMETER Force Forces the cmdlet to remove items that cannot otherwise be changed, such as hidden or read-only files or read-only aliases or variables. .EXAMPLE PS> Clear-AzsSupportDirectory .EXAMPLE PS> Clear-AzsSupportDirectory -ComputerName PREFIX-NC01 -Path 'C:\Temp\SDN2' .EXAMPLE PS> Clear-AzsSupportDirectory -ComputerName PREFIX-NC01,PREFIX-SLB01 -Credential (Get-Credential) .EXAMPLE PS> Clear-AzsSupportDirectory -Force -Recurse .EXAMPLE PS> Clear-AzsSupportDirectory -Path 'C:\Temp\Azs.Support\Path1','C:\Temp\Azs.Support\Path2' -Force -Recurse #> [CmdletBinding(DefaultParameterSetName = 'Local')] param ( [Parameter(Mandatory = $false, ParameterSetName = 'Remote')] [System.String[]]$ComputerName, [Parameter(Mandatory = $false, ParameterSetName = 'Remote')] [System.Management.Automation.PSCredential] [System.Management.Automation.Credential()] $Credential = [System.Management.Automation.PSCredential]::Empty, [Parameter(Mandatory = $false, ParameterSetName = 'Remote')] [Parameter(Mandatory = $false, ParameterSetName = 'Local')] [System.String[]]$Path = (Get-WorkingDirectory), [Parameter(Mandatory = $false, ParameterSetName = 'Remote')] [Parameter(Mandatory = $false, ParameterSetName = 'Local')] [Switch]$Recurse, [Parameter(Mandatory = $false, ParameterSetName = 'Remote')] [Parameter(Mandatory = $false, ParameterSetName = 'Local')] [Switch]$Force ) function Clear-WorkingDirectory { [CmdletBinding()] param ( [System.String[]]$Path, [bool]$Recurse, [bool]$Force ) $filteredPaths = @() foreach ($obj in $Path) { # if the path does not exist, lets skip if (-NOT (Test-Path -Path $obj)) { continue } # enumerate through the allowed folder paths for cleanup to make sure the paths specified can be cleaned up foreach ($allowedFolderPath in $Script:AzsSupport_Utilities.Config.FolderPathsAllowedForCleanup) { if ($obj -ilike $allowedFolderPath) { $filteredPaths += $obj } } } if ($filteredPaths) { $msg.FileSystemRemove -f ($filteredPaths -join ', ') | Trace-Output -Level:Verbose Remove-Item -Path $filteredPaths -Exclude $Script:AzsSupport_Utilities.Cache.FilesExcludedFromCleanup -Force:$Force -Recurse:$Recurse -ErrorAction Continue } } $params = @{ Path = $Path Recurse = $Recurse.IsPresent Force = $Force.IsPresent } try { if ($PSCmdlet.ParameterSetName -eq 'Remote') { Invoke-PSRemoteCommand -ComputerName $ComputerName -Credential $Credential -ScriptBlock { param([Parameter(Position = 1)]$Path, [Parameter(Position = 2)]$Recurse, [Parameter(Position = 3)]$Force) Clear-SdnWorkingDirectory -Path $Path -Recurse:$Recurse -Force:$Force } -ArgumentList @($params.Path, $params.Recurse, $params.Force) } else { Clear-WorkingDirectory @params } } catch { "{0}`n{1}" -f $_.Exception, $_.ScriptStackTrace | Trace-Output -Level:Error } } function Test-Is23H2Version() { if(($null -ne $Global:AzsSupport.EnvironmentInfo) -AND ($Global:AzsSupport.EnvironmentInfo.WindowsProductName -eq "Azure Stack HCI") -AND ($Global:AzsSupport.EnvironmentInfo.OSDisplayVersion -eq "23H2")) { return $true } else { return $false } } # SIG # Begin signature block # MIIoRgYJKoZIhvcNAQcCoIIoNzCCKDMCAQExDzANBglghkgBZQMEAgEFADB5Bgor # BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG # KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCCyU9xXixEV4XAh # spYnWhwvG7nkRK44iivFJVJPMYDgKKCCDXYwggX0MIID3KADAgECAhMzAAAEBGx0 # Bv9XKydyAAAAAAQEMA0GCSqGSIb3DQEBCwUAMH4xCzAJBgNVBAYTAlVTMRMwEQYD # VQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNy # b3NvZnQgQ29ycG9yYXRpb24xKDAmBgNVBAMTH01pY3Jvc29mdCBDb2RlIFNpZ25p # bmcgUENBIDIwMTEwHhcNMjQwOTEyMjAxMTE0WhcNMjUwOTExMjAxMTE0WjB0MQsw # CQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9u # ZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMR4wHAYDVQQDExVNaWNy # b3NvZnQgQ29ycG9yYXRpb24wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB # AQC0KDfaY50MDqsEGdlIzDHBd6CqIMRQWW9Af1LHDDTuFjfDsvna0nEuDSYJmNyz # NB10jpbg0lhvkT1AzfX2TLITSXwS8D+mBzGCWMM/wTpciWBV/pbjSazbzoKvRrNo # DV/u9omOM2Eawyo5JJJdNkM2d8qzkQ0bRuRd4HarmGunSouyb9NY7egWN5E5lUc3 # a2AROzAdHdYpObpCOdeAY2P5XqtJkk79aROpzw16wCjdSn8qMzCBzR7rvH2WVkvF # HLIxZQET1yhPb6lRmpgBQNnzidHV2Ocxjc8wNiIDzgbDkmlx54QPfw7RwQi8p1fy # 4byhBrTjv568x8NGv3gwb0RbAgMBAAGjggFzMIIBbzAfBgNVHSUEGDAWBgorBgEE # AYI3TAgBBggrBgEFBQcDAzAdBgNVHQ4EFgQU8huhNbETDU+ZWllL4DNMPCijEU4w # RQYDVR0RBD4wPKQ6MDgxHjAcBgNVBAsTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEW # MBQGA1UEBRMNMjMwMDEyKzUwMjkyMzAfBgNVHSMEGDAWgBRIbmTlUAXTgqoXNzci # tW2oynUClTBUBgNVHR8ETTBLMEmgR6BFhkNodHRwOi8vd3d3Lm1pY3Jvc29mdC5j # b20vcGtpb3BzL2NybC9NaWNDb2RTaWdQQ0EyMDExXzIwMTEtMDctMDguY3JsMGEG # CCsGAQUFBwEBBFUwUzBRBggrBgEFBQcwAoZFaHR0cDovL3d3dy5taWNyb3NvZnQu # Y29tL3BraW9wcy9jZXJ0cy9NaWNDb2RTaWdQQ0EyMDExXzIwMTEtMDctMDguY3J0 # MAwGA1UdEwEB/wQCMAAwDQYJKoZIhvcNAQELBQADggIBAIjmD9IpQVvfB1QehvpC # Ge7QeTQkKQ7j3bmDMjwSqFL4ri6ae9IFTdpywn5smmtSIyKYDn3/nHtaEn0X1NBj # L5oP0BjAy1sqxD+uy35B+V8wv5GrxhMDJP8l2QjLtH/UglSTIhLqyt8bUAqVfyfp # h4COMRvwwjTvChtCnUXXACuCXYHWalOoc0OU2oGN+mPJIJJxaNQc1sjBsMbGIWv3 # cmgSHkCEmrMv7yaidpePt6V+yPMik+eXw3IfZ5eNOiNgL1rZzgSJfTnvUqiaEQ0X # dG1HbkDv9fv6CTq6m4Ty3IzLiwGSXYxRIXTxT4TYs5VxHy2uFjFXWVSL0J2ARTYL # E4Oyl1wXDF1PX4bxg1yDMfKPHcE1Ijic5lx1KdK1SkaEJdto4hd++05J9Bf9TAmi # u6EK6C9Oe5vRadroJCK26uCUI4zIjL/qG7mswW+qT0CW0gnR9JHkXCWNbo8ccMk1 # sJatmRoSAifbgzaYbUz8+lv+IXy5GFuAmLnNbGjacB3IMGpa+lbFgih57/fIhamq # 5VhxgaEmn/UjWyr+cPiAFWuTVIpfsOjbEAww75wURNM1Imp9NJKye1O24EspEHmb # DmqCUcq7NqkOKIG4PVm3hDDED/WQpzJDkvu4FrIbvyTGVU01vKsg4UfcdiZ0fQ+/ # V0hf8yrtq9CkB8iIuk5bBxuPMIIHejCCBWKgAwIBAgIKYQ6Q0gAAAAAAAzANBgkq # hkiG9w0BAQsFADCBiDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24x # EDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlv # bjEyMDAGA1UEAxMpTWljcm9zb2Z0IFJvb3QgQ2VydGlmaWNhdGUgQXV0aG9yaXR5 # IDIwMTEwHhcNMTEwNzA4MjA1OTA5WhcNMjYwNzA4MjEwOTA5WjB+MQswCQYDVQQG # EwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwG # A1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSgwJgYDVQQDEx9NaWNyb3NvZnQg # Q29kZSBTaWduaW5nIFBDQSAyMDExMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIIC # CgKCAgEAq/D6chAcLq3YbqqCEE00uvK2WCGfQhsqa+laUKq4BjgaBEm6f8MMHt03 # a8YS2AvwOMKZBrDIOdUBFDFC04kNeWSHfpRgJGyvnkmc6Whe0t+bU7IKLMOv2akr # rnoJr9eWWcpgGgXpZnboMlImEi/nqwhQz7NEt13YxC4Ddato88tt8zpcoRb0Rrrg # OGSsbmQ1eKagYw8t00CT+OPeBw3VXHmlSSnnDb6gE3e+lD3v++MrWhAfTVYoonpy # 4BI6t0le2O3tQ5GD2Xuye4Yb2T6xjF3oiU+EGvKhL1nkkDstrjNYxbc+/jLTswM9 # sbKvkjh+0p2ALPVOVpEhNSXDOW5kf1O6nA+tGSOEy/S6A4aN91/w0FK/jJSHvMAh # dCVfGCi2zCcoOCWYOUo2z3yxkq4cI6epZuxhH2rhKEmdX4jiJV3TIUs+UsS1Vz8k # A/DRelsv1SPjcF0PUUZ3s/gA4bysAoJf28AVs70b1FVL5zmhD+kjSbwYuER8ReTB # w3J64HLnJN+/RpnF78IcV9uDjexNSTCnq47f7Fufr/zdsGbiwZeBe+3W7UvnSSmn # Eyimp31ngOaKYnhfsi+E11ecXL93KCjx7W3DKI8sj0A3T8HhhUSJxAlMxdSlQy90 # lfdu+HggWCwTXWCVmj5PM4TasIgX3p5O9JawvEagbJjS4NaIjAsCAwEAAaOCAe0w # ggHpMBAGCSsGAQQBgjcVAQQDAgEAMB0GA1UdDgQWBBRIbmTlUAXTgqoXNzcitW2o # ynUClTAZBgkrBgEEAYI3FAIEDB4KAFMAdQBiAEMAQTALBgNVHQ8EBAMCAYYwDwYD # VR0TAQH/BAUwAwEB/zAfBgNVHSMEGDAWgBRyLToCMZBDuRQFTuHqp8cx0SOJNDBa # BgNVHR8EUzBRME+gTaBLhklodHRwOi8vY3JsLm1pY3Jvc29mdC5jb20vcGtpL2Ny # bC9wcm9kdWN0cy9NaWNSb29DZXJBdXQyMDExXzIwMTFfMDNfMjIuY3JsMF4GCCsG # AQUFBwEBBFIwUDBOBggrBgEFBQcwAoZCaHR0cDovL3d3dy5taWNyb3NvZnQuY29t # L3BraS9jZXJ0cy9NaWNSb29DZXJBdXQyMDExXzIwMTFfMDNfMjIuY3J0MIGfBgNV # HSAEgZcwgZQwgZEGCSsGAQQBgjcuAzCBgzA/BggrBgEFBQcCARYzaHR0cDovL3d3 # dy5taWNyb3NvZnQuY29tL3BraW9wcy9kb2NzL3ByaW1hcnljcHMuaHRtMEAGCCsG # AQUFBwICMDQeMiAdAEwAZQBnAGEAbABfAHAAbwBsAGkAYwB5AF8AcwB0AGEAdABl # AG0AZQBuAHQALiAdMA0GCSqGSIb3DQEBCwUAA4ICAQBn8oalmOBUeRou09h0ZyKb # C5YR4WOSmUKWfdJ5DJDBZV8uLD74w3LRbYP+vj/oCso7v0epo/Np22O/IjWll11l # hJB9i0ZQVdgMknzSGksc8zxCi1LQsP1r4z4HLimb5j0bpdS1HXeUOeLpZMlEPXh6 # I/MTfaaQdION9MsmAkYqwooQu6SpBQyb7Wj6aC6VoCo/KmtYSWMfCWluWpiW5IP0 # wI/zRive/DvQvTXvbiWu5a8n7dDd8w6vmSiXmE0OPQvyCInWH8MyGOLwxS3OW560 # STkKxgrCxq2u5bLZ2xWIUUVYODJxJxp/sfQn+N4sOiBpmLJZiWhub6e3dMNABQam # ASooPoI/E01mC8CzTfXhj38cbxV9Rad25UAqZaPDXVJihsMdYzaXht/a8/jyFqGa # J+HNpZfQ7l1jQeNbB5yHPgZ3BtEGsXUfFL5hYbXw3MYbBL7fQccOKO7eZS/sl/ah # XJbYANahRr1Z85elCUtIEJmAH9AAKcWxm6U/RXceNcbSoqKfenoi+kiVH6v7RyOA # 9Z74v2u3S5fi63V4GuzqN5l5GEv/1rMjaHXmr/r8i+sLgOppO6/8MO0ETI7f33Vt # Y5E90Z1WTk+/gFcioXgRMiF670EKsT/7qMykXcGhiJtXcVZOSEXAQsmbdlsKgEhr # /Xmfwb1tbWrJUnMTDXpQzTGCGiYwghoiAgEBMIGVMH4xCzAJBgNVBAYTAlVTMRMw # EQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVN # aWNyb3NvZnQgQ29ycG9yYXRpb24xKDAmBgNVBAMTH01pY3Jvc29mdCBDb2RlIFNp # Z25pbmcgUENBIDIwMTECEzMAAAQEbHQG/1crJ3IAAAAABAQwDQYJYIZIAWUDBAIB # BQCgga4wGQYJKoZIhvcNAQkDMQwGCisGAQQBgjcCAQQwHAYKKwYBBAGCNwIBCzEO # MAwGCisGAQQBgjcCARUwLwYJKoZIhvcNAQkEMSIEIIAyQuSiUyuWhP+z1ykwDBRv # xp74c2RUhvKQ3tutm19jMEIGCisGAQQBgjcCAQwxNDAyoBSAEgBNAGkAYwByAG8A # cwBvAGYAdKEagBhodHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20wDQYJKoZIhvcNAQEB # BQAEggEAQxddt8WXvnRSe6JurHXOjQoRLmxSpDPlzscCX9ZUz10WxyK1pR5TBaqf # Qt7wQRUIJqXFLx/uxlSCNboCoyyROiCA6c+PC48KMNMsGNU94MXDGx0Fd5sh8LAc # YeCpbqj5iw2Gq48DX7entsxOytC0if2Faum+HZa37yPkWa/q7cdmMXCirXqJgQNl # yOOtA9P+mNIqu56SoCF2E8Z6S0GdXpzLaBtO8sdtYh4zfthrDuSfVDyzbYsHDe9V # wzbFl5nqZ34q6SojoafJydwGlmaCN58fkv3fBqEOwSXuyCSEKN4FI2+lxAVrk+HA # ZQTi8NbCpxKb4JGysydjbWGmjJxHnaGCF7AwghesBgorBgEEAYI3AwMBMYIXnDCC # F5gGCSqGSIb3DQEHAqCCF4kwgheFAgEDMQ8wDQYJYIZIAWUDBAIBBQAwggFaBgsq # hkiG9w0BCRABBKCCAUkEggFFMIIBQQIBAQYKKwYBBAGEWQoDATAxMA0GCWCGSAFl # AwQCAQUABCCu1XTPfUoZaHGF22S+Tx/dMgLPopNWWZMoMaWs6cS1fAIGZusqy3G+ # GBMyMDI0MTExNTEwMTQ0OC4xMjdaMASAAgH0oIHZpIHWMIHTMQswCQYDVQQGEwJV # UzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UE # ChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMS0wKwYDVQQLEyRNaWNyb3NvZnQgSXJl # bGFuZCBPcGVyYXRpb25zIExpbWl0ZWQxJzAlBgNVBAsTHm5TaGllbGQgVFNTIEVT # TjozMjFBLTA1RTAtRDk0NzElMCMGA1UEAxMcTWljcm9zb2Z0IFRpbWUtU3RhbXAg # U2VydmljZaCCEf4wggcoMIIFEKADAgECAhMzAAAB+KOhJgwMQEj+AAEAAAH4MA0G # CSqGSIb3DQEBCwUAMHwxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9u # MRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRp # b24xJjAkBgNVBAMTHU1pY3Jvc29mdCBUaW1lLVN0YW1wIFBDQSAyMDEwMB4XDTI0 # MDcyNTE4MzEwOFoXDTI1MTAyMjE4MzEwOFowgdMxCzAJBgNVBAYTAlVTMRMwEQYD # VQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNy # b3NvZnQgQ29ycG9yYXRpb24xLTArBgNVBAsTJE1pY3Jvc29mdCBJcmVsYW5kIE9w # ZXJhdGlvbnMgTGltaXRlZDEnMCUGA1UECxMeblNoaWVsZCBUU1MgRVNOOjMyMUEt # MDVFMC1EOTQ3MSUwIwYDVQQDExxNaWNyb3NvZnQgVGltZS1TdGFtcCBTZXJ2aWNl # MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAxR23pXYnD2BuODdeXs2C # u/T5kKI+bAw8cbtN50Cm/FArjXyL4RTqMe6laQ/CqeMTxgckvZr1JrW0Mi4F15rx # /VveGhKBmob45DmOcV5xyx7h9Tk59NAl5PNMAWKAIWf270SWAAWxQbpVIhhPWCnV # V3otVvahEad8pMmoSXrT5Z7Nk1RnB70A2bq9Hk8wIeC3vBuxEX2E8X50IgAHsyaR # 9roFq3ErzUEHlS8YnSq33ui5uBcrFOcFOCZILuVFVTgEqSrX4UiX0etqi7jUtKyp # gIflaZcV5cI5XI/eCxY8wDNmBprhYMNlYxdmQ9aLRDcTKWtddWpnJtyl5e3gHuYo # j8xuDQ0XZNy7ESRwJIK03+rTZqfaYyM4XSK1s0aa+mO69vo/NmJ4R/f1+KucBPJ4 # yUdbqJWM3xMvBwLYycvigI/WK4kgPog0UBNczaQwDVXpcU+TMcOvWP8HBWmWJQIm # TZInAFivXqUaBbo3wAfPNbsQpvNNGu/12pg0F8O/CdRfgPHfOhIWQ0D8ALCY+Lsi # wbzcejbrVl4N9fn2wOg2sDa8RfNoD614I0pFjy/lq1NsBo9V4GZBikzX7ZjWCRgd # 1FCBXGpfpDikHjQ05YOkAakdWDT2bGSaUZJGVYtepIpPTAs1gd/vUogcdiL51o7s # huHIlB6QSUiQ24XYhRbbQCECAwEAAaOCAUkwggFFMB0GA1UdDgQWBBS9zsZzz57Q # lT5nrt/oitLv1OQ7tjAfBgNVHSMEGDAWgBSfpxVdAF5iXYP05dJlpxtTNRnpcjBf # BgNVHR8EWDBWMFSgUqBQhk5odHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20vcGtpb3Bz # L2NybC9NaWNyb3NvZnQlMjBUaW1lLVN0YW1wJTIwUENBJTIwMjAxMCgxKS5jcmww # bAYIKwYBBQUHAQEEYDBeMFwGCCsGAQUFBzAChlBodHRwOi8vd3d3Lm1pY3Jvc29m # dC5jb20vcGtpb3BzL2NlcnRzL01pY3Jvc29mdCUyMFRpbWUtU3RhbXAlMjBQQ0El # MjAyMDEwKDEpLmNydDAMBgNVHRMBAf8EAjAAMBYGA1UdJQEB/wQMMAoGCCsGAQUF # BwMIMA4GA1UdDwEB/wQEAwIHgDANBgkqhkiG9w0BAQsFAAOCAgEAYfk8GzzpEVnG # l7y6oXoytCb42Hx6TOA0+dkaBI36ftDE9tLubUa/xMbHB5rcNiRhFHZ93RefdPpc # 4+FF0DAl5lP8xKAO+293RWPKDFOFIxgtZY08t8D9cSQpgGUzyw3lETZebNLEA17A # /CTpA2F9uh8j84KygeEbj+bidWDiEfayoH2A5/5ywJJxIuLzFVHacvWxSCKoF9hl # SrZSG5fXWS3namf4tt690UT6AGyWLFWe895coFPxm/m0UIMjjp9VRFH7nb3Ng2Q4 # gPS9E5ZTMZ6nAlmUicDj0NXAs2wQuQrnYnbRAJ/DQW35qLo7Daw9AsItqjFhbMcG # 68gDc4j74L2KYe/2goBHLwzSn5UDftS1HZI0ZRsqmNHI0TZvvUWX9ajm6SfLBTEt # oTo6gLOX0UD/9rrhGjdkiCw4SwU5osClgqgiNMK5ndk2gxFlDXHCyLp5qB6BoPpc # 82RhO0yCzoP9gv7zv2EocAWEsqE5+0Wmu5uarmfvcziLfU1SY240OZW8ld4sS8fn # ybn/jDMmFAhazV1zH0QERWEsfLSpwkOXaImWNFJ5lmcnf1VTm6cmfasScYtElpjq # Z9GooCmk1XFApORPs/PO43IcFmPRwagt00iQSw+rBeIH00KQq+FJT/62SB70g9g/ # R8TS6k6b/wt2UWhqrW+Q8lw6Xzgex/YwggdxMIIFWaADAgECAhMzAAAAFcXna54C # 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 # TjozMjFBLTA1RTAtRDk0NzElMCMGA1UEAxMcTWljcm9zb2Z0IFRpbWUtU3RhbXAg # U2VydmljZaIjCgEBMAcGBSsOAwIaAxUAtkQt/ebWSQ5DnG+aKRzPELCFE9GggYMw # gYCkfjB8MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UE # BxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSYwJAYD # VQQDEx1NaWNyb3NvZnQgVGltZS1TdGFtcCBQQ0EgMjAxMDANBgkqhkiG9w0BAQsF # AAIFAOrhc6wwIhgPMjAyNDExMTUwNzE1NTZaGA8yMDI0MTExNjA3MTU1NlowdzA9 # BgorBgEEAYRZCgQBMS8wLTAKAgUA6uFzrAIBADAKAgEAAgIHbQIB/zAHAgEAAgIS # MDAKAgUA6uLFLAIBADA2BgorBgEEAYRZCgQCMSgwJjAMBgorBgEEAYRZCgMCoAow # CAIBAAIDB6EgoQowCAIBAAIDAYagMA0GCSqGSIb3DQEBCwUAA4IBAQAf4rryHc1p # OdFV/DCyYq537v1dgQpIdA/4eUsPdJR18EfBDebbGuG9523bhdCgmk7J939f30tM # RJsFh+7+34PFI37n4gfbvH6YN5qdvLKTxceRohyqi8PsyN6a4O357emnsF7roIHM # h1GyiYGIoNS6b66k8OcPumtydqBdIXxKb8UZkmDDaqG7Q+CL2/u87q8U1u2yvuSt # afD0TxlOiNoBvGB89Dds+5Nww6klaS30bJdYXFswvaXfO5Xi3oV7r0Q6keLPKtz6 # R8BT4XxI3dHfAQ7nuHX5D44ms5IcTCMSWTxeIpR7eN0eC9SHyM4KavyGFiNMcGKz # Q6RnkXJ687zwMYIEDTCCBAkCAQEwgZMwfDELMAkGA1UEBhMCVVMxEzARBgNVBAgT # Cldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29m # dCBDb3Jwb3JhdGlvbjEmMCQGA1UEAxMdTWljcm9zb2Z0IFRpbWUtU3RhbXAgUENB # IDIwMTACEzMAAAH4o6EmDAxASP4AAQAAAfgwDQYJYIZIAWUDBAIBBQCgggFKMBoG # CSqGSIb3DQEJAzENBgsqhkiG9w0BCRABBDAvBgkqhkiG9w0BCQQxIgQgjcM7X/MW # teUufkdJgHj/kzYc7DUDkrdUMu2kb4PnyYkwgfoGCyqGSIb3DQEJEAIvMYHqMIHn # MIHkMIG9BCDvzDPyXw1UkAUFYt8bR4UdjM90Qv5xnVaiKD3I0Zz3WjCBmDCBgKR+ # MHwxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdS # ZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xJjAkBgNVBAMT # HU1pY3Jvc29mdCBUaW1lLVN0YW1wIFBDQSAyMDEwAhMzAAAB+KOhJgwMQEj+AAEA # AAH4MCIEIMGTHrtFSZ2QXMpCWpOpI+oRB6INUp8HzdASoDGrB/cWMA0GCSqGSIb3 # DQEBCwUABIICAEXT7+oEYZThVZB+89N1E10OKHjAftMSGRTbTAxdkg2UuQztE3fF # dbUl+6HlP8PDSdzE+Y73KU6mKgxtaqLNMVW6oZirC/zYdM/R4ydOG9WnTFzslHdA # TQiwR10lWzBKQSp3yg2PFU2zFGbGgCybyYC759PKn6YLX93q1HF+pD91pmB7dSHC # lEIC6SK1MfLMEWNGfQYRR0g2cDCUSMnYhOeExbQfY/IijrRfTvz6suQPxJcwZe6/ # 1jM/Kh4avMM7CjXHJpNfMQU0rXQEE70iEdZ68BWoAZpAZqWQgsuH/IVnvviu6g0P # P7QgfILQSKS6AJcqYpjrffDfm2UcRp1ChzUXHVxWwQD81sxq3HqUbbRTTS9ixwpk # TxOXU/HAGcvnPXVyjnayI/LiD+7rGwbnwTKbNpMy1nOB+8ITDNbGFuQpyJ4ocP8w # xVdLSUBfWM6uBPmm6QbdWrFnVX84wwP7FeWTnIyKAGHosrpyLYOhPCwrWXdwUJog # FlgChvOKX5nmB25q6ccY3eMYTR0vhZwnpqswbq/scI+Btf3b+EYbR4MCCfpGih3X # Ciill6QTNix9awR77i5bUVhONKQvQFneXTzJ9EaKgOpHnvvNoAALL6f0N94+CwFW # JjxzrqjYaxFjJAFr2s9qfHU/hAJTrcuOKBwYsZ4r2+XFwZmBzdE7KSI2 # SIG # End signature block |