InfrastructureBackupValidator/IBCAdapterClientPkg/InternalBackupUtils.psm1 (871 lines of code) (raw):

## ## <copyright file="InternalBackupUtils.psm1" company="Microsoft"> ## Copyright (C) Microsoft. All rights reserved. ## </copyright> ## Import-Module "$PSScriptRoot\Out-ErrorMessage.psm1" $backupInfoFileName = "InternalBackupInfo.xml" $RetentionLockExpiration = [TimeSpan]::FromMinutes(30) $PickupLockExpiration = [TimeSpan]::FromMinutes(30) <# .SYNOPSIS Exclusively open lock file $Path\$Name. .PARAMETER Path Parent directory of lockfile. .PARAMETER Name Lock file name. .PARAMETER Timeout Timeout of opening operation. .OUTPUTS FileStream of lock file. Return $null if times out. #> function Open-LockFile { param( [parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [string] $Path, [parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [string] $Name, [parameter(Mandatory = $false)] [timespan] $Timeout = [timespan]::FromSeconds(120) ) $null = New-Item $Path -ItemType Directory -Force -ErrorAction SilentlyContinue $timeoutDate = $(Get-Date).Add($Timeout) While ( $(Get-Date) -le $timeoutDate) { try { $res = [System.IO.File]::Open( $(Join-Path $(Convert-Path $Path) "$Name.lock"), [System.IO.FileMode]::OpenOrCreate, [System.IO.FileAccess]::ReadWrite, [System.IO.FileShare]::None) return $res } catch { Write-Warning "Open lock file failed: `n$(Out-ErrorMessage $_)" } # Typical lock holding time is 20 ms wait 100 ms for it to finish. $null = Start-Sleep -Milliseconds 100 } # lock timeout return $null } <# .SYNOPSIS Close lock file $Path\$Name. .INPUTS Lock file FileStream. #> function Close-LockFile { param( [parameter( Mandatory = $true, ValueFromPipeline = $true)] [ValidateNotNull()] [System.IO.FileStream] $pipelineInput ) $pipelineInput.Close() } <# .SYNOPSIS Try to acquire lock. .PARAMETER Path Parent directory of lockfile. .PARAMETER Name Lock file name. .PARAMETER Id Id of owner. .PARAMETER Timeout Lock is automaticly released after this timeout. .OUTPUTS $true for successful acquisition. Return $false if lock is acquired by other process. #> function TryLock-BcdrMutex { param( [parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [string] $Path, [parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [ValidatePattern('^[a-zA-Z0-9]+$')] [string] $Name, [string] $Id = $(New-Guid).ToString(), [timespan] $Expiration = [TimeSpan]::FromMinutes(1) ) $ErrorActionPreference = "Stop" $lock = Open-LockFile -Path $Path -Name $Name if ($null -eq $lock) { return $false } try { if ( -not $(CheckLockValid -Lock $lock)) { $t = $(Get-Date).ToUniversalTime() $lockInfo = @{ MachineName = $env:ComputerName Pid = $PID Id = $Id LockTime = $t ExpiredTime = $t + $Expiration } | ConvertTo-Json -Compress $lock.Position = 0 $writer = New-Object -TypeName System.IO.StreamWriter -ArgumentList @($lock, [System.Text.Encoding]::UTF8, 4096, $true) try { $writer.Write($lockInfo) $writer.Flush() } finally { $writer.Dispose() } return $true } else { return $false } } catch { Write-Warning "TryLock-BcdrMutex failed: `n$(Out-ErrorMessage $_)" return $false } finally { $null = $lock | Close-LockFile } } <# .SYNOPSIS Release lock. .PARAMETER Path Parent directory of lockfile. .PARAMETER Name Lock file name. .PARAMETER Id Id of owner. #> function Unlock-BcdrMutex { param( [parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [string] $Path, [parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [ValidatePattern('^[a-zA-Z0-9]+$')] [string] $Name, [parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [string] $Id ) $lock = Open-LockFile -Path $Path -Name $Name if ($null -eq $lock) { throw "Open-LockFile failed, can not unlock bcdr mutex." } try { $lock.SetLength(0) } finally { $null = $lock | Close-LockFile } } <# .SYNOPSIS Validate the lock file. .PARAMETER Lock FileStream of lock file. .OUTPUTS $false if lock file is empty, corrupt, expired or owner process is already died. Otherwise, $true. #> function CheckLockValid { param( [parameter(Mandatory = $true)] [ValidateNotNull()] [System.IO.FileStream] $Lock ) $reader = New-Object -TypeName System.IO.StreamReader -ArgumentList @($Lock, [System.Text.Encoding]::UTF8, $true, 4096, $true) try { $lockstr = $reader.ReadToEnd() $lockinfo = $lockstr | ConvertFrom-Json if ($null -eq $lockinfo -or $null -eq $lockinfo.ExpiredTime -or $null -eq $lockinfo.MachineName) { Write-Verbose "[CheckLockValid] Lock file empty: $lockstr" return $false } elseif ($($lockinfo.ExpiredTime.ToUniversalTime()) -lt $($(Get-Date).ToUniversalTime())) { Write-Warning "[CheckLockValid] Lock expired" return $false } elseif ($env:ComputerName -eq $lockinfo.MachineName -and $($null -eq $lockinfo.Pid -or $null -eq $(Get-Process -Id $lockinfo.Pid))) { Write-Warning "[CheckLockValid] Process $($lockinfo.Pid) exited unexpectedly" return $false } else { Write-Verbose "[CheckLockValid] Process $($lockinfo.Pid) on $($lockinfo.MachineName) is still running" return $true } } catch { Write-Warning "[CheckLockValid] Lock invalid: `n$(Out-ErrorMessage $_)" return $false } finally { $reader.Dispose() } } <# .SYNOPSIS Read internal backup information. .PARAMETER Path Path of InternalBackupInfo.xml file. .OUTPUTS Internal backup info. Return $null for failed backup. #> function Read-SucceededInternalBackupInfo { param ( [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [string] $Path ) $backupInfo = Join-Path $Path $backupInfoFileName if (-not $(Test-Path $backupInfo)) { return $null } $info = [xml]$(Get-Content $backupInfo) if ("Succeeded" -ne $info.InternalBackupInfo.Status) { return $null } else { return New-Object -TypeName PSObject -Property @{ BackupDataVersion = $info.InternalBackupInfo.BackupDataVersion BackupId = $info.InternalBackupInfo.BackupId Status = $info.InternalBackupInfo.Status CreatedDateTime = $(Get-Date $info.InternalBackupInfo.CreatedDateTime) TimeTakenToCreate = $info.InternalBackupInfo.TimeTakenToCreate } } } <# .SYNOPSIS Remove backup folder .PARAMETER Path Backup folder to be removed. #> function Remove-BackupFolder { param( [parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] $Path ) $lockParam = @{ Path = $Path Name = "read" Id = "Remove_" + $env:ComputerName } Write-Verbose "Removing $Path" if (-not $(TryLock-BcdrMutex @lockParam -Expiration $RetentionLockExpiration)) { Write-Warning "Skip removing of $Path" return } try { if (Test-Path -PathType Leaf "$Path\$backupInfoFileName") { Remove-Item -Force -Confirm:$false "$Path\$backupInfoFileName" -ErrorAction Stop } else { Write-Warning "Backup info not exists. Remove backup file." } } catch { Write-Warning "Remove backup $Path failed: `n$(Out-ErrorMessage $_)" } finally { Unlock-BcdrMutex @lockParam } try { Write-Verbose "Removing backup $Path" Remove-Item $Path -Force -Recurse -Confirm:$false -ErrorAction Stop } catch { Write-Warning "Remove backup failed, try removing with renaming: `n$(Out-ErrorMessage $_)" try { $parentPath = Split-Path $Path -Parent $tempPath = Join-Path $parentPath $($(New-Guid).ToString()) Write-Verbose "Moving $Path to $tempPath" Move-Item $Path $tempPath -Force Write-Verbose "Removing $tempPath" Remove-Item $tempPath -Force -Recurse -Confirm:$false -ErrorAction Stop } catch { Write-Warning "Removing with renaming failed too: `n$(Out-ErrorMessage $_)" } } } <# .SYNOPSIS Get infomation of last succeeded internal backup. .PARAMETER InternalBackupRootPath Backup store path. #> function Get-LastSucceededInternalBackupInfo { param ( [parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [string] $InternalBackupRootPath ) $dirs = Get-ChildItem -Directory $InternalBackupRootPath $lastSuccessBackup = $dirs | Where-Object { $null -ne $(Read-SucceededInternalBackupInfo $_.FullName) } | Sort-Object CreationTime | Select-Object -Last 1 return $lastSuccessBackup } <# .SYNOPSIS Remove old backups and always keep at least one success backup. .PARAMETER InternalBackupRootPath Backup store path. .PARAMETER RetentionPeriod Remove backup taken before this limit. .PARAMETER SizeThreshold Remove old backup to keep total size of backup under this limit. #> function Remove-RetiredBackup { param( [parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [string] $InternalBackupRootPath, [TimeSpan] $RetentionPeriod = [TimeSpan]::MaxValue, [long] $SizeThreshold = [long]::MaxValue ) $lastSuccessBackup = Get-LastSucceededInternalBackupInfo -InternalBackupRootPath $InternalBackupRootPath $dirs = Get-ChildItem -Directory $InternalBackupRootPath if ($RetentionPeriod -ne [timespan]::MaxValue) { $dirs ` | Where-Object CreationTimeUtc -lt $(Get-Date).ToUniversalTime().Add(-$RetentionPeriod) ` | Where-Object Name -ne $lastSuccessBackup.Name ` | ForEach-Object { Remove-BackupFolder $($_.FullName) } } $totalSize = $(Get-ChildItem $InternalBackupRootPath -Recurse | Measure-Object -Property Length -Sum).Sum $dirs = Get-ChildItem -Directory $InternalBackupRootPath ` | Where-Object Name -ne $lastSuccessBackup.Name ` | Sort-Object CreationTimeUtc foreach ($dir in $dirs) { if ($totalSize -gt $SizeThreshold) { $removedSize = $(Get-ChildItem $dir.FullName -Recurse | Measure-Object -Property Length -Sum).Sum Remove-BackupFolder $($dir.FullName) $totalSize = $totalSize - $removedSize } else { break } } } <# .SYNOPSIS Wait powershell background job to finish and kill it if times out. .INPUTS Job to wait. .PARAMETER TimeoutInSec Kill job after this timeout. #> function Wait-JobWithTimeout { param( [System.Management.Automation.Job] [parameter( Mandatory = $true, ValueFromPipeline = $true)] [ValidateNotNull()] $Job, [parameter(Mandatory = $true)] [ValidateNotNull()] [int] $TimeoutInSec ) Wait-Job -Job $Job -Timeout $TimeoutInSec if ($(Get-Job -Id $Job.Id).State -eq "Running") { Write-Warning "Job time out." } $Job | Stop-Job $Job | Receive-Job $Job | Remove-Job } <# .SYNOPSIS This is a helper function for internal backup runner. It makes sure only one backup at a time and handles backup retention for backup runner. .PARAMETER ScriptBlock Script block to trigger backup. .PARAMETER Config Backup runner configuration. #> function Invoke-RunnerBackupScriptBlock { param( [parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [scriptblock] $ScriptBlock, [parameter(Mandatory = $true)] [ValidateNotNull()] [PSObject] $Config, [switch] $NoTranscript ) $ErrorActionPreference = "Stop" $WarningPreference = "Continue" $VerbosePreference = "Continue" $SessionName = $Config.BackupName if (-not $NoTranscript.IsPresent) { $null = Start-Transcript "$env:SystemDrive\MASLogs\Runner_$($SessionName)_$([DateTime]::UtcNow.ToString('yyyyMMdd-HHmmss')).log" -Append } try { $Path = [regex]::Replace($Config.BackupLocation, "{([^}]*)}", "\\$($Config.FileServerName)\$($Config.StorageServerPrefix)_`$1") + "\Temp\$SessionName" $RetentionPeriodInMinutes = $Config.RetentionPeriodInMinutes Write-Verbose "Backup path: $Path" $backupId = New-Guid $null = New-Item $(Join-Path $Path "Backup") -ItemType Directory -Force -ErrorAction SilentlyContinue $lockParam = @{ Path = $Path Name = $SessionName Id = "Backup" + $env:ComputerName } # acquire lock Write-Verbose "Try acquiring lock." if (-not $(TryLock-BcdrMutex @lockParam -Expiration $([TimeSpan]::FromSeconds($Config.TimeoutInSec)))) { Write-Verbose "Backup running skip." return } Write-Verbose "Lock acquired." try { $InternalBackupRootPath = Join-Path $Path "Backup" $lastSuccessBackup = Get-LastSucceededInternalBackupInfo -InternalBackupRootPath $InternalBackupRootPath # Skip backup if a successful backup exists within interval. # Invoke-RunnerBackupScriptBlock can be call with a smaller interval than BackupInteval. if ($null -ne $lastSuccessBackup.CreationTimeUtc -and $null -ne $Config.BackupInterval) { $gap = $(Get-Date).ToUniversalTime() - $lastSuccessBackup.CreationTimeUtc $backupInterval = [timespan] $Config.BackupInterval Write-Verbose "Last success backup: $($lastSuccessBackup.Name), gap: $gap" if ($gap -lt $backupInterval) # $gap -lt $null -> $false { Write-Verbose "Skip backup because last backup is new enough" return } } # Retention Remove-RetiredBackup -InternalBackupRootPath $InternalBackupRootPath -RetentionPeriod $([TimeSpan]::FromMinutes($RetentionPeriodInMinutes)) # trigger backup $startTime = $(Get-Date).ToUniversalTime() $backupResult = "Failed" try { Invoke-Command -ScriptBlock $ScriptBlock -ErrorAction Stop -WarningAction Continue -Verbose -ArgumentList @($Config, $InternalBackupRootPath, $backupId) $backupResult = "Succeeded" } catch { Write-Warning "Invoke backup runner failed: `n$(Out-ErrorMessage $_)" Remove-Item -Recurse -Force -Confirm:$false -ErrorAction SilentlyContinue "$InternalBackupRootPath\$backupId\*" } $endTime = $(Get-Date).ToUniversalTime() $TimeTakenToCreate = $endTime - $startTime Write-Verbose "Write backup info" "<?xml version=""1.0""?> <InternalBackupInfo xmlns:xsd=""http://www.w3.org/2001/XMLSchema"" xmlns:xsi=""http://www.w3.org/2001/XMLSchema-instance""> <BackupDataVersion>1.0.0</BackupDataVersion> <BackupId>$backupId</BackupId> <Status>$backupResult</Status> <CreatedDateTime>$($endTime.ToString("o"))</CreatedDateTime> <TimeTakenToCreate>$([System.Xml.XmlConvert]::ToString($TimeTakenToCreate))</TimeTakenToCreate> </InternalBackupInfo>" | Out-File $InternalBackupRootPath\$backupId\$backupInfoFileName -ErrorAction SilentlyContinue # Retention Remove-RetiredBackup -InternalBackupRootPath $InternalBackupRootPath -SizeThreshold $([long]$Config.SizeThresholdInMB * 1024 * 1024) } finally { Unlock-BcdrMutex @lockParam } } catch { Write-Warning "Invoke-RunnerBackupScriptBlock failed with exception: `n$(Out-ErrorMessage $_)" throw } finally { if (-not $NoTranscript.IsPresent) { $null = Stop-Transcript } } } <# .SYNOPSIS Copy last success backup to infrastucture backup staging area. .PARAMETER InternalBackupRoot Root folder of internal backup. .PARAMETER StagingAreaPath Staging area of infrastructure backup. .PARAMETER BackupId Infrastructure backup ID. #> function Copy-RunnerBackupToStagingArea { [CmdletBinding()] param ( [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [string] $InternalBackupRoot, [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [string] $StagingAreaPath, [Parameter(Mandatory = $true)] [ValidateNotNull()] [Guid] $BackupId ) $ErrorActionPreference = 'Stop' $backupIdStr = $BackupId.ToString() $destPath = "$StagingAreaPath\Backup\$backupIdStr" New-Item $destPath -ItemType Directory -Force $lastSuccessBackup = Get-ChildItem -Directory $InternalBackupRoot ` | Foreach-Object { Read-SucceededInternalBackupInfo $_.FullName } ` | Where-Object { $null -ne $_ } ` | Sort-Object { Get-Date $_.CreatedDateTime } ` | Select-Object -Last 1 if ($null -eq $lastSuccessBackup) { throw "Can not find any success backup." } $lastSuccessBackupPath = $(Join-Path $InternalBackupRoot $lastSuccessBackup.BackupId) Write-Verbose "using backup $lastSuccessBackupPath" $lockParam = @{ Path = $lastSuccessBackupPath Name = "read" Id = "Pickup_" + $env:ComputerName } $lockFileName = "$($lockParam.Name).lock" $failedFiles = @() try { # lock backup to avoid it being deleted by retention if (-not $(TryLock-BcdrMutex @lockParam -Expiration $PickupLockExpiration)) { throw "Last success backup is being deleted." } $null = Get-ChildItem $lastSuccessBackupPath -Exclude @($backupInfoFileName, $lockFileName) ` | Foreach-Object { $sourceName = $_.FullName $destName = Join-Path $(Convert-Path $destPath) $_.Name try { Copy-Item $sourceName $destName -Recurse -ErrorAction Stop } catch { Write-Warning "Copy file $sourceName failed: $_" $failedFiles += $sourceName } } if ($failedFiles.Count -ne 0) { throw "Copying of following files failed: $failedFiles" } } finally { $null = Unlock-BcdrMutex @lockParam } return Read-SucceededInternalBackupInfo -Path $lastSuccessBackupPath } Export-ModuleMember -Function Wait-JobWithTimeout Export-ModuleMember -Function TryLock-BcdrMutex Export-ModuleMember -Function Unlock-BcdrMutex Export-ModuleMember -Function Remove-RetiredBackup Export-ModuleMember -Function Copy-RunnerBackupToStagingArea Export-ModuleMember -Function Invoke-RunnerBackupScriptBlock # SIG # Begin signature block # MIInvwYJKoZIhvcNAQcCoIInsDCCJ6wCAQExDzANBglghkgBZQMEAgEFADB5Bgor # BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG # KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCA8aoEUsVRaGaSz # AlLYSgboZtLf/o/AjTKqqjeXf6CJ4aCCDXYwggX0MIID3KADAgECAhMzAAADTrU8 # esGEb+srAAAAAANOMA0GCSqGSIb3DQEBCwUAMH4xCzAJBgNVBAYTAlVTMRMwEQYD # VQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNy # b3NvZnQgQ29ycG9yYXRpb24xKDAmBgNVBAMTH01pY3Jvc29mdCBDb2RlIFNpZ25p # bmcgUENBIDIwMTEwHhcNMjMwMzE2MTg0MzI5WhcNMjQwMzE0MTg0MzI5WjB0MQsw # CQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9u # ZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMR4wHAYDVQQDExVNaWNy # b3NvZnQgQ29ycG9yYXRpb24wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB # AQDdCKiNI6IBFWuvJUmf6WdOJqZmIwYs5G7AJD5UbcL6tsC+EBPDbr36pFGo1bsU # p53nRyFYnncoMg8FK0d8jLlw0lgexDDr7gicf2zOBFWqfv/nSLwzJFNP5W03DF/1 # 1oZ12rSFqGlm+O46cRjTDFBpMRCZZGddZlRBjivby0eI1VgTD1TvAdfBYQe82fhm # WQkYR/lWmAK+vW/1+bO7jHaxXTNCxLIBW07F8PBjUcwFxxyfbe2mHB4h1L4U0Ofa # +HX/aREQ7SqYZz59sXM2ySOfvYyIjnqSO80NGBaz5DvzIG88J0+BNhOu2jl6Dfcq # jYQs1H/PMSQIK6E7lXDXSpXzAgMBAAGjggFzMIIBbzAfBgNVHSUEGDAWBgorBgEE # AYI3TAgBBggrBgEFBQcDAzAdBgNVHQ4EFgQUnMc7Zn/ukKBsBiWkwdNfsN5pdwAw # RQYDVR0RBD4wPKQ6MDgxHjAcBgNVBAsTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEW # MBQGA1UEBRMNMjMwMDEyKzUwMDUxNjAfBgNVHSMEGDAWgBRIbmTlUAXTgqoXNzci # tW2oynUClTBUBgNVHR8ETTBLMEmgR6BFhkNodHRwOi8vd3d3Lm1pY3Jvc29mdC5j # b20vcGtpb3BzL2NybC9NaWNDb2RTaWdQQ0EyMDExXzIwMTEtMDctMDguY3JsMGEG # CCsGAQUFBwEBBFUwUzBRBggrBgEFBQcwAoZFaHR0cDovL3d3dy5taWNyb3NvZnQu # Y29tL3BraW9wcy9jZXJ0cy9NaWNDb2RTaWdQQ0EyMDExXzIwMTEtMDctMDguY3J0 # MAwGA1UdEwEB/wQCMAAwDQYJKoZIhvcNAQELBQADggIBAD21v9pHoLdBSNlFAjmk # mx4XxOZAPsVxxXbDyQv1+kGDe9XpgBnT1lXnx7JDpFMKBwAyIwdInmvhK9pGBa31 # TyeL3p7R2s0L8SABPPRJHAEk4NHpBXxHjm4TKjezAbSqqbgsy10Y7KApy+9UrKa2 # kGmsuASsk95PVm5vem7OmTs42vm0BJUU+JPQLg8Y/sdj3TtSfLYYZAaJwTAIgi7d # hzn5hatLo7Dhz+4T+MrFd+6LUa2U3zr97QwzDthx+RP9/RZnur4inzSQsG5DCVIM # pA1l2NWEA3KAca0tI2l6hQNYsaKL1kefdfHCrPxEry8onJjyGGv9YKoLv6AOO7Oh # JEmbQlz/xksYG2N/JSOJ+QqYpGTEuYFYVWain7He6jgb41JbpOGKDdE/b+V2q/gX # UgFe2gdwTpCDsvh8SMRoq1/BNXcr7iTAU38Vgr83iVtPYmFhZOVM0ULp/kKTVoir # IpP2KCxT4OekOctt8grYnhJ16QMjmMv5o53hjNFXOxigkQWYzUO+6w50g0FAeFa8 # 5ugCCB6lXEk21FFB1FdIHpjSQf+LP/W2OV/HfhC3uTPgKbRtXo83TZYEudooyZ/A # Vu08sibZ3MkGOJORLERNwKm2G7oqdOv4Qj8Z0JrGgMzj46NFKAxkLSpE5oHQYP1H # tPx1lPfD7iNSbJsP6LiUHXH1MIIHejCCBWKgAwIBAgIKYQ6Q0gAAAAAAAzANBgkq # 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 # /Xmfwb1tbWrJUnMTDXpQzTGCGZ8wghmbAgEBMIGVMH4xCzAJBgNVBAYTAlVTMRMw # EQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVN # aWNyb3NvZnQgQ29ycG9yYXRpb24xKDAmBgNVBAMTH01pY3Jvc29mdCBDb2RlIFNp # Z25pbmcgUENBIDIwMTECEzMAAANOtTx6wYRv6ysAAAAAA04wDQYJYIZIAWUDBAIB # BQCgga4wGQYJKoZIhvcNAQkDMQwGCisGAQQBgjcCAQQwHAYKKwYBBAGCNwIBCzEO # MAwGCisGAQQBgjcCARUwLwYJKoZIhvcNAQkEMSIEIE1SDabzOUVIGrN20fMvh2m5 # D44gvXx6yaAWsh4wjvyuMEIGCisGAQQBgjcCAQwxNDAyoBSAEgBNAGkAYwByAG8A # cwBvAGYAdKEagBhodHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20wDQYJKoZIhvcNAQEB # BQAEggEA2St0tdGGnh9Zli60q3IS5Wy2hi0CI7tHIqb6XNPO/26EFgce/2RxepHg # NYUxRniL6UIruxNZtEqdR0ABQEZtLXpIdYnq6P0rGiaAPHKZ3szQToXqmAo+z2XZ # 6yZ9RRulSFObLKIaUwKFTGOLmCHKjGxC8FawAooxGM5dH0OjHQ4yL6Rzfqqpk5zh # ItCnqZ62yBkFnTNKi7Sy9Oun5GRvEqh4UmpK0PrulsegRC+X1t4XIucqS1ts0CQR # aRtX43FyjIJPPJZpOUfOMvu9EHb0dVShH4XhwfF/G6uex1RsrC8uQ+cUUAfjEF1E # A5e7Nm764KEkQ8B3J2iGJRv4VpT2VqGCFykwghclBgorBgEEAYI3AwMBMYIXFTCC # FxEGCSqGSIb3DQEHAqCCFwIwghb+AgEDMQ8wDQYJYIZIAWUDBAIBBQAwggFZBgsq # hkiG9w0BCRABBKCCAUgEggFEMIIBQAIBAQYKKwYBBAGEWQoDATAxMA0GCWCGSAFl # AwQCAQUABCBp1tYtfkXpQ56wCZ4jU1yNdvhQ4S+w4yySD87JlzJaZQIGZUK6Y0KN # GBMyMDIzMTExMzA3MTM0NS4wNDZaMASAAgH0oIHYpIHVMIHSMQswCQYDVQQGEwJV # UzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UE # ChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMS0wKwYDVQQLEyRNaWNyb3NvZnQgSXJl # bGFuZCBPcGVyYXRpb25zIExpbWl0ZWQxJjAkBgNVBAsTHVRoYWxlcyBUU1MgRVNO # OjA4NDItNEJFNi1DMjlBMSUwIwYDVQQDExxNaWNyb3NvZnQgVGltZS1TdGFtcCBT # ZXJ2aWNloIIReDCCBycwggUPoAMCAQICEzMAAAHajtXJWgDREbEAAQAAAdowDQYJ # KoZIhvcNAQELBQAwfDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24x # EDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlv # bjEmMCQGA1UEAxMdTWljcm9zb2Z0IFRpbWUtU3RhbXAgUENBIDIwMTAwHhcNMjMx # MDEyMTkwNjU5WhcNMjUwMTEwMTkwNjU5WjCB0jELMAkGA1UEBhMCVVMxEzARBgNV # BAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jv # c29mdCBDb3Jwb3JhdGlvbjEtMCsGA1UECxMkTWljcm9zb2Z0IElyZWxhbmQgT3Bl # cmF0aW9ucyBMaW1pdGVkMSYwJAYDVQQLEx1UaGFsZXMgVFNTIEVTTjowODQyLTRC # RTYtQzI5QTElMCMGA1UEAxMcTWljcm9zb2Z0IFRpbWUtU3RhbXAgU2VydmljZTCC # AiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAJOQBgh2tVFR1j8jQA4NDf8b # cVrXSN080CNKPSQo7S57sCnPU0FKF47w2L6qHtwm4EnClF2cruXFp/l7PpMQg25E # 7X8xDmvxr8BBE6iASAPCfrTebuvAsZWcJYhy7prgCuBf7OidXpgsW1y8p6Vs7sD2 # aup/0uveYxeXlKtsPjMCplHkk0ba+HgLho0J68Kdji3DM2K59wHy9xrtsYK+X9er # bDGZ2mmX3765aS5Q7/ugDxMVgzyj80yJn6ULnknD9i4kUQxVhqV1dc/DF6UBeuzf # ukkMed7trzUEZMRyla7qhvwUeQlgzCQhpZjz+zsQgpXlPczvGd0iqr7lACwfVGog # 5plIzdExvt1TA8Jmef819aTKwH1IVEIwYLA6uvS8kRdA6RxvMcb//ulNjIuGceyy # kMAXEynVrLG9VvK4rfrCsGL3j30Lmidug+owrcCjQagYmrGk1hBykXilo9YB8Qyy # 5Q1KhGuH65V3zFy8a0kwbKBRs8VR4HtoPYw9z1DdcJfZBO2dhzX3yAMipCGm6Smv # mvavRsXhy805jiApDyN+s0/b7os2z8iRWGJk6M9uuT2493gFV/9JLGg5YJJCJXI+ # yxkO/OXnZJsuGt0+zWLdHS4XIXBG17oPu5KsFfRTHREloR2dI6GwaaxIyDySHYOt # vIydla7u4lfnfCjY/qKTAgMBAAGjggFJMIIBRTAdBgNVHQ4EFgQUoXyNyVE9ZhOV # izEUVwhNgL8PX0UwHwYDVR0jBBgwFoAUn6cVXQBeYl2D9OXSZacbUzUZ6XIwXwYD # VR0fBFgwVjBUoFKgUIZOaHR0cDovL3d3dy5taWNyb3NvZnQuY29tL3BraW9wcy9j # cmwvTWljcm9zb2Z0JTIwVGltZS1TdGFtcCUyMFBDQSUyMDIwMTAoMSkuY3JsMGwG # CCsGAQUFBwEBBGAwXjBcBggrBgEFBQcwAoZQaHR0cDovL3d3dy5taWNyb3NvZnQu # Y29tL3BraW9wcy9jZXJ0cy9NaWNyb3NvZnQlMjBUaW1lLVN0YW1wJTIwUENBJTIw # MjAxMCgxKS5jcnQwDAYDVR0TAQH/BAIwADAWBgNVHSUBAf8EDDAKBggrBgEFBQcD # CDAOBgNVHQ8BAf8EBAMCB4AwDQYJKoZIhvcNAQELBQADggIBALmDVdTtuI0jAEt4 # 1O2OM8CU237TGMyhrGr7FzKCEFaXxtoqk/IObQriq1caHVh2vyuQ24nz3TdOBv7r # cs/qnPjOxnXFLyZPeaWLsNuARVmUViyVYXjXYB5DwzaWZgScY8GKL7yGjyWrh78W # JUgh7rE1+5VD5h0/6rs9dBRqAzI9fhZz7spsjt8vnx50WExbBSSH7rfabHendpeq # bTmW/RfcaT+GFIsT+g2ej7wRKIq/QhnsoF8mpFNPHV1q/WK/rF/ChovkhJMDvlqt # ETWi97GolOSKamZC9bYgcPKfz28ed25WJy10VtQ9P5+C/2dOfDaz1RmeOb27Kbeg # ha0SfPcriTfORVvqPDSa3n9N7dhTY7+49I8evoad9hdZ8CfIOPftwt3xTX2RhMZJ # CVoFlabHcvfb84raFM6cz5EYk+x1aVEiXtgK6R0xn1wjMXHf0AWlSjqRkzvSnRKz # FsZwEl74VahlKVhI+Ci9RT9+6Gc0xWzJ7zQIUFE3Jiix5+7KL8ArHfBY9UFLz4sn # boJ7Qip3IADbkU4ZL0iQ8j8Ixra7aSYfToUefmct3dM69ff4Eeh2Kh9NsKiiph58 # 9Ap/xS1jESlrfjL/g/ZboaS5d9a2fA598mubDvLD5x5PP37700vm/Y+PIhmp2fTv # uS2sndeZBmyTqcUNHRNmCk+njV3nMIIHcTCCBVmgAwIBAgITMwAAABXF52ueAptJ # mQAAAAAAFTANBgkqhkiG9w0BAQsFADCBiDELMAkGA1UEBhMCVVMxEzARBgNVBAgT # Cldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29m # dCBDb3Jwb3JhdGlvbjEyMDAGA1UEAxMpTWljcm9zb2Z0IFJvb3QgQ2VydGlmaWNh # dGUgQXV0aG9yaXR5IDIwMTAwHhcNMjEwOTMwMTgyMjI1WhcNMzAwOTMwMTgzMjI1 # WjB8MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMH # UmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSYwJAYDVQQD # Ex1NaWNyb3NvZnQgVGltZS1TdGFtcCBQQ0EgMjAxMDCCAiIwDQYJKoZIhvcNAQEB # BQADggIPADCCAgoCggIBAOThpkzntHIhC3miy9ckeb0O1YLT/e6cBwfSqWxOdcjK # NVf2AX9sSuDivbk+F2Az/1xPx2b3lVNxWuJ+Slr+uDZnhUYjDLWNE893MsAQGOhg # fWpSg0S3po5GawcU88V29YZQ3MFEyHFcUTE3oAo4bo3t1w/YJlN8OWECesSq/XJp # rx2rrPY2vjUmZNqYO7oaezOtgFt+jBAcnVL+tuhiJdxqD89d9P6OU8/W7IVWTe/d # vI2k45GPsjksUZzpcGkNyjYtcI4xyDUoveO0hyTD4MmPfrVUj9z6BVWYbWg7mka9 # 7aSueik3rMvrg0XnRm7KMtXAhjBcTyziYrLNueKNiOSWrAFKu75xqRdbZ2De+JKR # Hh09/SDPc31BmkZ1zcRfNN0Sidb9pSB9fvzZnkXftnIv231fgLrbqn427DZM9itu # qBJR6L8FA6PRc6ZNN3SUHDSCD/AQ8rdHGO2n6Jl8P0zbr17C89XYcz1DTsEzOUyO # ArxCaC4Q6oRRRuLRvWoYWmEBc8pnol7XKHYC4jMYctenIPDC+hIK12NvDMk2ZItb # oKaDIV1fMHSRlJTYuVD5C4lh8zYGNRiER9vcG9H9stQcxWv2XFJRXRLbJbqvUAV6 # bMURHXLvjflSxIUXk8A8FdsaN8cIFRg/eKtFtvUeh17aj54WcmnGrnu3tz5q4i6t # AgMBAAGjggHdMIIB2TASBgkrBgEEAYI3FQEEBQIDAQABMCMGCSsGAQQBgjcVAgQW # BBQqp1L+ZMSavoKRPEY1Kc8Q/y8E7jAdBgNVHQ4EFgQUn6cVXQBeYl2D9OXSZacb # UzUZ6XIwXAYDVR0gBFUwUzBRBgwrBgEEAYI3TIN9AQEwQTA/BggrBgEFBQcCARYz # aHR0cDovL3d3dy5taWNyb3NvZnQuY29tL3BraW9wcy9Eb2NzL1JlcG9zaXRvcnku # 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 # dmljZaIjCgEBMAcGBSsOAwIaAxUAQqIfIYljHUbNoY0/wjhXRn/sSA2ggYMwgYCk # fjB8MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMH # UmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSYwJAYDVQQD # Ex1NaWNyb3NvZnQgVGltZS1TdGFtcCBQQ0EgMjAxMDANBgkqhkiG9w0BAQUFAAIF # AOj7uKQwIhgPMjAyMzExMTMwNDQ4MzZaGA8yMDIzMTExNDA0NDgzNlowdDA6Bgor # BgEEAYRZCgQBMSwwKjAKAgUA6Pu4pAIBADAHAgEAAgIBYjAHAgEAAgISqjAKAgUA # 6P0KJAIBADA2BgorBgEEAYRZCgQCMSgwJjAMBgorBgEEAYRZCgMCoAowCAIBAAID # B6EgoQowCAIBAAIDAYagMA0GCSqGSIb3DQEBBQUAA4GBAC0wIED0ScS8Gb0r3tGo # ElIKFGOlRWfTNU06PtLc5CIDO+l1Um1B1+IeYjdhIlOJnRmTeXmXz4+8/r3VP9XM # iCUqlQWi6S/OvpMDcCzivpIPQdXIo8Sr7Di3lF0sE3P85tTaUvAxVaqvze4q8tHO # spKB5TFoVKpaM682Chd1ihHVMYIEDTCCBAkCAQEwgZMwfDELMAkGA1UEBhMCVVMx # EzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoT # FU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEmMCQGA1UEAxMdTWljcm9zb2Z0IFRpbWUt # U3RhbXAgUENBIDIwMTACEzMAAAHajtXJWgDREbEAAQAAAdowDQYJYIZIAWUDBAIB # BQCgggFKMBoGCSqGSIb3DQEJAzENBgsqhkiG9w0BCRABBDAvBgkqhkiG9w0BCQQx # IgQg5LocpUmON6W0zdA3hv8+e3py1kzMUssZRNk+Kl42wQEwgfoGCyqGSIb3DQEJ # EAIvMYHqMIHnMIHkMIG9BCAipaNpYsDvnqTe95Dj1C09020I5ljibrW/ndICOxg9 # xjCBmDCBgKR+MHwxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAw # DgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24x # JjAkBgNVBAMTHU1pY3Jvc29mdCBUaW1lLVN0YW1wIFBDQSAyMDEwAhMzAAAB2o7V # yVoA0RGxAAEAAAHaMCIEIAPGASDXQaR+/wRJpR5yz6T24kPw999Oq0Vp4fBkxnhO # MA0GCSqGSIb3DQEBCwUABIICAGxlyM9O3whRxhL2OUMSShgvtE7gOmByiyXplmWl # 2zIsvbcgIJddgQQL6vymRLM/tUt/N+6BCMb9HH/yQkUG0Yik2glfBSJQojexWmlq # rBgSNTxg5Uj48bsnJtzwOcQavJoE5T59eaLkxoo2izZKjDgHe7QOKsHLIn5TExXo # lmBemzkK8KK3ZgvG2KM2sk6Fht5TcXgdck0I0aOBUnB17QvJXUAt+2DWV/w5KDH+ # gZjjpjF/+TOd0hSLh21fMS5OglJwjwi8l2lLvDoGvYzOQH4r3rA6i9uM97scpAE6 # ovY8eWLjBnENLfB3kHeZGBaK4V80+h5P4+Msws7kzAPbK9KmszxBQLpqTR1blzp4 # OJQCLGvpgy6BzT8CygkblvydWczCkEkFw34hyA/wklrYIdXT/X16eGwd4/UcDkLy # FJJz9ZdXZjkVDSftTJwBv4859IT9Zyme/5rpyV/Y62rs+qvQ+giL9mjY+nJ4rfUe # PqnGcgBD0WUets866CqF6lJgVbpQJn0MigZK0nM9NUc8BN6fIzKGoHHRp0/9H5ov # 6JgZB0Z0J6481OnIPlyoQ8hjlsvysIQ+CyG5NHgYNY3DPyMj8MFx8f9k8DeCMbFP # mkVoDgQ+EfXqRxo/dubGDcmfgLdLELPPiq7PzzKUeOs58VWTBKfV4BQBs9Bsbr4A # b5p1 # SIG # End signature block