AL-Go/New-ALGoRepo.ps1
function New-ALGoRepo { Param( [Parameter(Mandatory=$true)] $org, [Parameter(Mandatory=$true)] $repo, $branch = "main", [Parameter(Mandatory=$true)] [ValidateSet('PTE','AppSource')] $appType, [Parameter(Mandatory=$true)] [ValidateSet('public','private')] $accessControl, $algoBranch = 'main', $description = $repo, $apps = @(), [HashTable] $addRepoSettings = @{}, [HashTable] $addProjectSettings = @{}, $readme = "# $repo", [string] $keyVaultName, [switch] $useOrgSecrets, [HashTable] $secrets = @{}, [switch] $additionalCountriesAlways, [switch] $openFolder, [switch] $openVSCode, [switch] $openBrowser, $tmpFolder = (Join-Path ([System.IO.Path]::GetTempPath()) ([Guid]::NewGuid().ToString())) ) # Well known AppIds $systemAppId = "63ca2fa4-4f03-4f2b-a480-172fef340d3f" $baseAppId = "437dbf0e-84ff-417a-965d-ed2bb9650972" $applicationAppId = "c1335042-3002-4257-bf8a-75c898ccb1b8" $permissionsMockAppId = "40860557-a18d-42ad-aecb-22b7dd80dc80" $testRunnerAppId = "23de40a6-dfe8-4f80-80db-d70f83ce8caf" $anyAppId = "e7320ebb-08b3-4406-b1ec-b4927d3e280b" $libraryAssertAppId = "dd0be2ea-f733-4d65-bb34-a28f4624fb14" $libraryVariableStorageAppId = "5095f467-0a01-4b99-99d1-9ff1237d286f" $systemApplicationTestLibraryAppId = "9856ae4f-d1a7-46ef-89bb-6ef056398228" $TestsTestLibrariesAppId = "5d86850b-0d76-4eca-bd7b-951ad998e997" $performanceToolkitAppId = "75f1590f-55c5-4501-ae63-bada5534e852" $performanceToolkitApps = @($performanceToolkitAppId) $testLibrariesApps = @($systemApplicationTestLibraryAppId, $TestsTestLibrariesAppId) $testFrameworkApps = @($anyAppId, $libraryAssertAppId, $libraryVariableStorageAppId) + $testLibrariesApps $testRunnerApps = @($permissionsMockAppId, $testRunnerAppId) + $performanceToolkitApps + $testLibrariesApps + $testFrameworkApps function SetSetting { Param( [PSCustomObject] $settings, [string] $name, $value ) Write-Host "Set $name=$value" if ($settings.PSObject.Properties.Name -eq $name) { $settings."$name" = $value } else { $settings | Add-Member -MemberType NoteProperty -Name $name -Value $value } } function GetUniqueFolderName { Param( [string] $baseFolder, [string] $folderName ) $i = 2 $name = $folderName while (Test-Path (Join-Path $baseFolder $name)) { $name = "$folderName($i)" $i++ } $name } function getfiles { Param( [string] $path ) $tempdir = $false if ($path -like "https://*" -or $path -like "http://*") { $url = $path $path = Join-Path ([System.IO.Path]::GetTempPath()) "$([Guid]::NewGuid().ToString()).app" $tempdir = $true Download-File -sourceUrl $url -destinationFile $path if (!(Test-Path -Path $path)) { throw "could not download the file." } } expandfile -path $path if ($tempdir) { Remove-Item $path -Force -ErrorAction SilentlyContinue } } function expandfile { Param( [string] $path ) if (Test-Path -Path $path -PathType Container) { $appFolders = @() if (Test-Path (Join-Path $path 'app.json')) { $appFolders += @($path) } Get-ChildItem $path -Recurse | Where-Object { $_.PSIsContainer -and (Test-Path -Path (Join-Path $_.FullName 'app.json')) } | ForEach-Object { if (!($appFolders -contains $_.Parent.FullName)) { $appFolders += @($_.FullName) } } $appFolders | ForEach-Object { $newFolder = Join-Path ([System.IO.Path]::GetTempPath()) "$([Guid]::NewGuid().ToString())" write-Host "$_ -> $newFolder" Copy-Item -Path $_ -Destination $newFolder -Force -Recurse Write-Host "done" $newFolder } Get-ChildItem $path -include @("*.zip", "*.app") -Recurse | ForEach-Object { expandfile $_.FullName } } elseif (-not (Test-Path -Path $path -PathType Leaf)) { throw "Path $path does not exist" } elseif ([string]::new([char[]](Get-Content $path @byteEncodingParam -TotalCount 2)) -eq "PK") { # .zip file $destinationPath = Join-Path ([System.IO.Path]::GetTempPath()) "$([Guid]::NewGuid().ToString())" Expand-7zipArchive -path $path -destinationPath $destinationPath $directoryInfo = Get-ChildItem $destinationPath | Measure-Object if ($directoryInfo.count -eq 0) { throw "The file is empty or malformed." } expandfile -path $destinationPath Remove-Item -Path $destinationPath -Force -Recurse -ErrorAction SilentlyContinue } elseif ([string]::new([char[]](Get-Content $path @byteEncodingParam -TotalCount 4)) -eq "NAVX") { $destinationPath = Join-Path ([System.IO.Path]::GetTempPath()) "$([Guid]::NewGuid().ToString())" Extract-AppFileToFolder -appFilename $path -appFolder $destinationPath -generateAppJson $destinationPath } else { throw "The provided file cannot be extracted. The url might be wrong or the file is malformed." } } try { invoke-git --version } catch { throw "You need to install Git (https://git-scm.com/) in order to use the AL-Go for GitHub setup function." } try { invoke-gh --version | Where-Object { $_ -like 'gh*' } invoke-gh auth status } catch { throw "You need to install GitHub CLI (https://cli.github.com/) in order to use the AL-Go for GitHub setup function." } try { $azModule = get-installedmodule -name az Write-Host "Az PS Module Version $($azModule.Version)" $context = Get-AzContext if (-not ($context)) { throw "You must run Login-AzAccount and Set-AzContext to select account and subscription" } Write-Host $context.Name } catch { throw "You need to install the Az PowerShell module Azure CLI (https://www.powershellgallery.com/packages/Az) in order to use the AL-Go for GitHub setup function." } if (Test-Path $tmpFolder) { throw "Specified folder already exists" } if ($addRepoSettings.ContainsKey('repoVersion')) { try { $version = [Version]"$($addRepoSettings.repoVersion).0.0" } catch { throw "addRepoSettings.repoVersion is not correctly formatted, needs to be major.minor" } } else { $version = [Version]"0.0.0.0" } if ($useOrgSecrets) { Write-Host -ForegroundColor Yellow "NOTE: You need to make sure your organization secrets are accessible from the repo here: https://github.com/organizations/$org/settings/secrets/actions" } elseif ($keyVaultName) { $keyvault = Get-AzKeyVault -name $keyVaultName -WarningAction SilentlyContinue $context = Get-AzContext if (-not ($keyVault)) { throw "KeyVault doesn't exist" } } New-Item -Path $tmpFolder -ItemType Directory | Out-Null Set-Location $tmpFolder $repository = "$org/$repo" invoke-gh repo create $repository --$accessControl --clone --description $description $folder = Join-Path $tmpFolder $repo Set-Location $folder Write-Host "Downloading and applying AL-Go-$AppType template" $templateUrl = "https://github.com/microsoft/AL-Go-$AppType/archive/refs/heads/$($algoBranch).zip" $tempZip = Join-Path ([System.IO.Path]::GetTempPath()) "$([Guid]::NewGuid().ToString()).zip" Download-File -sourceUrl $templateUrl -destinationFile $tempZip Expand-7zipArchive -Path $tempZip -DestinationPath $folder Remove-Item -Path $tempZip -Force Copy-Item -Path "AL-Go-$appType-$algoBranch\*" -Recurse -Destination . -Force Remove-Item -Path "AL-Go-$appType-$algoBranch" -Recurse -Force Write-Host "Committing and pushing template" invoke-git -silent add * invoke-git -silent commit --allow-empty -m 'template' invoke-git -silent branch -M $branch invoke-git -silent remote set-url origin "https://github.com/$repository.git" invoke-git -silent push --set-upstream origin $branch Write-Host "Reading Settings" $repoSettingsFile = Join-Path $folder ".github\AL-Go-Settings.json" $repoSettings = Get-Content $repoSettingsFile -Encoding UTF8 | ConvertFrom-Json $projectSettingsFile = Join-Path $folder ".AL-Go\Settings.json" $projectSettings = Get-Content $projectSettingsFile -Encoding UTF8 | ConvertFrom-Json Rename-Item -Path "al.code-workspace" -NewName "$repo.code-workspace" $workspaceFile = Join-Path $folder "$repo.code-workspace" $workspace = Get-Content $workspaceFile -Encoding UTF8 | ConvertFrom-Json if (-not $addProjectSettings.ContainsKey('VersioningStrategy')) { $addProjectSettings.VersioningStrategy = 16 } [string[]] $additionalCountries = @() if ($addProjectSettings.ContainsKey('AdditionalCountries')) { $additionalCountries = [string[]] $addProjectSettings.AdditionalCountries if (!$additionalCountriesAlways) { $addProjectSettings.AdditionalCountries = @() } } if ($apps) { $apps | ForEach-Object { getfiles -path $_ | ForEach-Object { $appFolder = $_ "?Content_Types?.xml", "MediaIdListing.xml", "navigation.xml", "NavxManifest.xml", "DocComments.xml", "SymbolReference.json" | ForEach-Object { Remove-Item (Join-Path $appFolder $_) -Force -ErrorAction SilentlyContinue } $appJson = Get-Content (Join-Path $appFolder "app.json") -Encoding UTF8 | ConvertFrom-Json $ranges = @() if ($appJson.PSObject.Properties.Name -eq "idRanges") { $ranges += $appJson.idRanges } if ($appJson.PSObject.Properties.Name -eq "idRange") { $ranges += @($appJson.idRange) } $ttype = "" $ranges | Select-Object -First 1 | ForEach-Object { if ($_.from -lt 100000 -and $_.to -lt 100000) { $ttype = "PTE" } else { $ttype = "AppSource App" } } if ($appJson.PSObject.Properties.Name -eq "dependencies") { $appJson.dependencies | ForEach-Object { if ($_.PSObject.Properties.Name -eq "AppId") { $id = $_.AppId } else { $id = $_.Id } if ($testRunnerApps.Contains($id)) { $ttype = "Test App" } } } if ($ttype -ne "Test App") { Get-ChildItem -Path $appFolder -Filter "*.al" -Recurse | ForEach-Object { $alContent = (Get-Content -Path $_.FullName -Encoding UTF8) -join "`n" if ($alContent -like "*codeunit*subtype*=*test*[test]*") { $ttype = "Test App" } } } if ($ttype -ne "Test App" -and $ttype -ne $AppType) { Write-Host -ForegroundColor Yellow "You are adding a $ttype app into a $appType repository" } $orgfolderName = $appJson.name.Split([System.IO.Path]::getInvalidFileNameChars()) -join "" $folderName = GetUniqueFolderName -baseFolder $folder -folderName $orgfolderName if ($folderName -ne $orgfolderName) { Write-Host -ForegroundColor Yellow "$orgFolderName already exists as a folder in the repo, using $folderName instead" } Move-Item -Path $appFolder -Destination $folder -Force Rename-Item -Path ([System.IO.Path]::GetFileName($appFolder)) -NewName $folderName $appFolder = Join-Path $folder $folderName Get-ChildItem $appFolder -Filter '*.*' -Recurse | ForEach-Object { if ($_.Name.Contains('%20')) { Rename-Item -Path $_.FullName -NewName $_.Name.Replace('%20', ' ') } } if ($ttype -eq "Test App") { $projectSettings.TestFolders += @($folderName) } else { $projectSettings.AppFolders += @($folderName) } if (-not ($workspace.folders | Where-Object { $_.Path -eq $foldername })) { $workspace.folders += @(@{ "path" = $foldername }) } } } } Write-Host "Analyzing app version numbers" $maxVersionNumber = [Version]"0.0.0.0" $maxBuildNo = 0 $projectSettings.AppFolders+$projectSettings.TestFolders | ForEach-Object { $appJsonFile = Join-Path $folder "$_\app.json" $appJson = Get-Content $appJsonFile -Encoding UTF8 | ConvertFrom-Json $appVersion = [Version]$appJson.Version if ($appVersion -gt $maxVersionNumber) { $maxVersionNumber = $appVersion } if ($appVersion.Build -ge $maxBuildNo) { $maxBuildNo = $appVersion.Build+1 } } if (($addProjectSettings.VersioningStrategy -band 16) -eq 16) { if (-not $addRepoSettings.ContainsKey('repoVersion')) { $addRepoSettings.repoVersion = "$($maxVersionNumber.Major).$($maxVersionNumber.Minor+1)" $version = [Version]"$($addRepoSettings.repoVersion).0.0" } $projectSettings.AppFolders+$projectSettings.TestFolders | ForEach-Object { $appJsonFile = Join-Path $folder "$_\app.json" $appJson = Get-Content $appJsonFile -Encoding UTF8 | ConvertFrom-Json $appVersion = [Version]$appJson.Version if ([Version]$appJson.Version -gt $version) { Write-Host -ForegroundColor Yellow "WARNING: Version number of app in $_ was $($appJson.Version), will be set to $version, meaning that you will not be able to upgrade existing installations to this new version" } $appJson.Version = "$version" $appJson | ConvertTo-Json -Depth 99 | Set-Content $appJsonFile -Encoding UTF8 } } else { $addRepoSettings.repoVersion = "1.0" if (($addProjectSettings.VersioningStrategy -band 15) -eq 0) { SetSetting -settings $repoSettings -name "RunNumberOffset" -value $maxBuildNo } } Write-Host "Updating Repo Settings" $addRepoSettings.Keys | ForEach-Object { Write-Host "- $_ = $($addRepoSettings."$_")" SetSetting -settings $repoSettings -name $_ -value $addRepoSettings."$_" } Write-Host "Updating Project Settings" $addProjectSettings.Keys | ForEach-Object { Write-Host "- $_ = $($addProjectSettings."$_")" SetSetting -settings $projectSettings -name $_ -value $addProjectSettings."$_" } $orgSecrets = @(invoke-gh -returnValue secret list --org $Org -ErrorAction SilentlyContinue) if ($keyVaultName) { if ($useOrgSecrets -and ($orgSecrets | Where-Object { $_ -like "AZURE_CREDENTIALS`t*" })) { SetSetting -settings $repoSettings -name "KeyVaultName" -value $keyvault.VaultName } else { if (!$secrets.Contains('AZURE_CREDENTIALS')) { $secrets.AZURE_CREDENTIALS = "$org/$repo" } if (!$secrets.AZURE_CREDENTIALS.StartsWith('{')) { Write-Host "Creating Service Principal for $($secrets.AZURE_CREDENTIALS) to access KeyVault $keyVaultName using get, list" $adsp = New-AzADServicePrincipal -DisplayName $secrets.AZURE_CREDENTIALS -Role reader -Scope "/subscriptions/$($context.Subscription.Id)/resourceGroups/$($keyvault.ResourceGroupName)/providers/Microsoft.KeyVault/vaults/$($keyvault.VaultName)" Set-AzKeyVaultAccessPolicy -VaultName $keyvault.VaultName -PermissionsToSecrets get,list -ObjectId $adsp.Id $authContext = @{ "clientId" = $adsp.AppId "clientSecret" = $adsp.PasswordCredentials.secrettext "subscriptionId" = $context.Subscription.Id "tenantId" = $context.Tenant.Id "KeyVaultName" = $keyvault.VaultName } $secrets.AZURE_CREDENTIALS = "$($authContext | ConvertTo-Json -Compress)" } if ($useOrgSecrets) { Write-Host "Creating organizational secret AZURE_CREDENTIALS with access to KeyVault" invoke-gh -silent secret set AZURE_CREDENTIALS --org $Org --body $secrets.AZURE_CREDENTIALS --visibility selected --repos $repo } else { Write-Host "Creating repository secret AZURE_CREDENTIALS with access to KeyVault" invoke-gh -silent secret set AZURE_CREDENTIALS --body $secrets.AZURE_CREDENTIALS } } } $secrets.Keys | ForEach-Object { $key = $_ $value = $secrets."$key" if ($key -ne "AZURE_CREDENTIALS" -and ($value)) { if ($useOrgSecrets) { Write-Host "Creating organizational secret $key with value $value in $Org" invoke-gh -silent secret set $key --org $Org --body $value --visibility selected --repos $repo } else { Write-Host "Creating repository secret $key" invoke-gh -silent secret set $key --body $value } } } 'NextMajor','NextMinor','Current' | ForEach-Object { $name = "$($_)Schedule" if ($addRepoSettings.ContainsKey($name)) { $value = $repoSettings."$name" $workflowFile = ".github\workflows\$_.yaml" $srcContent = (Get-Content -Path $workflowFile -Encoding UTF8 -Raw).Replace("`r", "").TrimEnd("`n").Replace("`n", "`r`n") if ($value) { SetSetting -settings $repoSettings -name $name -value $value $srcPattern = "on:`r`n workflow_dispatch:`r`n" $replacePattern = "on:`r`n schedule:`r`n - cron: '$($value)'`r`n workflow_dispatch:`r`n" $srcContent = $srcContent.Replace($srcPattern, $replacePattern) Set-Content -Path $workflowFile -Encoding UTF8 -Value $srcContent } if (!$additionalCountriesAlways -and $additionalCountries) { $workflowSettingsFile = Join-Path $folder ".github\$($srcContent.Split("`r")[0].Substring(6).Trim("'").Trim(' ')).settings.json" $workflowSettings = Get-Content $workflowSettingsFile -Encoding UTF8 | ConvertFrom-Json SetSetting -settings $workflowSettings -name "AdditionalCountries" -value $additionalCountries $workflowSettings | ConvertTo-Json -Depth 99 | Set-Content -Path $workflowSettingsFile -Encoding UTF8 } } } Write-Host "Writing Settings" $repoSettings | ConvertTo-Json -Depth 99 | Set-Content -Path $repoSettingsFile -Encoding UTF8 $projectSettings | ConvertTo-Json -Depth 99 | Set-Content -Path $projectSettingsFile -Encoding UTF8 $workspace | ConvertTo-Json -Depth 99 | Set-Content -Path $workspaceFile -Encoding UTF8 Write-Host "Setting README.md content" Set-Content -Path (Join-Path $folder "README.md") -Value $readme Write-Host "Pushing Changes" invoke-git -silent add * invoke-git -silent commit --allow-empty -m "initial commit" invoke-git -silent push Write-Host "https://github.com/$repository" if ($openBrowser) { Start-Process "https://github.com/$repository" } if ($openVSCode) { code "$tmpFolder\$repo\$repo.code-workspace" } elseif ($openFolder) { Start-Process "$tmpFolder\$repo" } else { $tmpFolder } } Export-ModuleMember -Function New-ALGoRepo # SIG # Begin signature block # MIImbAYJKoZIhvcNAQcCoIImXTCCJlkCAQExDzANBglghkgBZQMEAgEFADB5Bgor # BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG # KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCDaKRvizXP9CUfC # LeWynbEpMq1Md5EFDTn8TVZgQAg3baCCH4QwggWNMIIEdaADAgECAhAOmxiO+dAt # 5+/bUOIIQBhaMA0GCSqGSIb3DQEBDAUAMGUxCzAJBgNVBAYTAlVTMRUwEwYDVQQK # EwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xJDAiBgNV # BAMTG0RpZ2lDZXJ0IEFzc3VyZWQgSUQgUm9vdCBDQTAeFw0yMjA4MDEwMDAwMDBa # Fw0zMTExMDkyMzU5NTlaMGIxCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2Vy # dCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xITAfBgNVBAMTGERpZ2lD # ZXJ0IFRydXN0ZWQgUm9vdCBHNDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoC # ggIBAL/mkHNo3rvkXUo8MCIwaTPswqclLskhPfKK2FnC4SmnPVirdprNrnsbhA3E # MB/zG6Q4FutWxpdtHauyefLKEdLkX9YFPFIPUh/GnhWlfr6fqVcWWVVyr2iTcMKy # unWZanMylNEQRBAu34LzB4TmdDttceItDBvuINXJIB1jKS3O7F5OyJP4IWGbNOsF # xl7sWxq868nPzaw0QF+xembud8hIqGZXV59UWI4MK7dPpzDZVu7Ke13jrclPXuU1 # 5zHL2pNe3I6PgNq2kZhAkHnDeMe2scS1ahg4AxCN2NQ3pC4FfYj1gj4QkXCrVYJB # MtfbBHMqbpEBfCFM1LyuGwN1XXhm2ToxRJozQL8I11pJpMLmqaBn3aQnvKFPObUR # WBf3JFxGj2T3wWmIdph2PVldQnaHiZdpekjw4KISG2aadMreSx7nDmOu5tTvkpI6 # nj3cAORFJYm2mkQZK37AlLTSYW3rM9nF30sEAMx9HJXDj/chsrIRt7t/8tWMcCxB # YKqxYxhElRp2Yn72gLD76GSmM9GJB+G9t+ZDpBi4pncB4Q+UDCEdslQpJYls5Q5S # UUd0viastkF13nqsX40/ybzTQRESW+UQUOsxxcpyFiIJ33xMdT9j7CFfxCBRa2+x # q4aLT8LWRV+dIPyhHsXAj6KxfgommfXkaS+YHS312amyHeUbAgMBAAGjggE6MIIB # NjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBTs1+OC0nFdZEzfLmc/57qYrhwP # TzAfBgNVHSMEGDAWgBRF66Kv9JLLgjEtUYunpyGd823IDzAOBgNVHQ8BAf8EBAMC # AYYweQYIKwYBBQUHAQEEbTBrMCQGCCsGAQUFBzABhhhodHRwOi8vb2NzcC5kaWdp # Y2VydC5jb20wQwYIKwYBBQUHMAKGN2h0dHA6Ly9jYWNlcnRzLmRpZ2ljZXJ0LmNv # bS9EaWdpQ2VydEFzc3VyZWRJRFJvb3RDQS5jcnQwRQYDVR0fBD4wPDA6oDigNoY0 # aHR0cDovL2NybDMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0QXNzdXJlZElEUm9vdENB # LmNybDARBgNVHSAECjAIMAYGBFUdIAAwDQYJKoZIhvcNAQEMBQADggEBAHCgv0Nc # Vec4X6CjdBs9thbX979XB72arKGHLOyFXqkauyL4hxppVCLtpIh3bb0aFPQTSnov # Lbc47/T/gLn4offyct4kvFIDyE7QKt76LVbP+fT3rDB6mouyXtTP0UNEm0Mh65Zy # oUi0mcudT6cGAxN3J0TU53/oWajwvy8LpunyNDzs9wPHh6jSTEAZNUZqaVSwuKFW # juyk1T3osdz9HNj0d1pcVIxv76FQPfx2CWiEn2/K2yCNNWAcAgPLILCsWKAOQGPF # mCLBsln1VWvPJ6tsds5vIy30fnFqI2si/xK4VC0nftg62fC2h5b9W9FcrBjDTZ9z # twGpn1eqXijiuZQwggYaMIIEAqADAgECAhBiHW0MUgGeO5B5FSCJIRwKMA0GCSqG # SIb3DQEBDAUAMFYxCzAJBgNVBAYTAkdCMRgwFgYDVQQKEw9TZWN0aWdvIExpbWl0 # ZWQxLTArBgNVBAMTJFNlY3RpZ28gUHVibGljIENvZGUgU2lnbmluZyBSb290IFI0 # NjAeFw0yMTAzMjIwMDAwMDBaFw0zNjAzMjEyMzU5NTlaMFQxCzAJBgNVBAYTAkdC # MRgwFgYDVQQKEw9TZWN0aWdvIExpbWl0ZWQxKzApBgNVBAMTIlNlY3RpZ28gUHVi # bGljIENvZGUgU2lnbmluZyBDQSBSMzYwggGiMA0GCSqGSIb3DQEBAQUAA4IBjwAw # ggGKAoIBgQCbK51T+jU/jmAGQ2rAz/V/9shTUxjIztNsfvxYB5UXeWUzCxEeAEZG # bEN4QMgCsJLZUKhWThj/yPqy0iSZhXkZ6Pg2A2NVDgFigOMYzB2OKhdqfWGVoYW3 # haT29PSTahYkwmMv0b/83nbeECbiMXhSOtbam+/36F09fy1tsB8je/RV0mIk8XL/ # tfCK6cPuYHE215wzrK0h1SWHTxPbPuYkRdkP05ZwmRmTnAO5/arnY83jeNzhP06S # hdnRqtZlV59+8yv+KIhE5ILMqgOZYAENHNX9SJDm+qxp4VqpB3MV/h53yl41aHU5 # pledi9lCBbH9JeIkNFICiVHNkRmq4TpxtwfvjsUedyz8rNyfQJy/aOs5b4s+ac7I # H60B+Ja7TVM+EKv1WuTGwcLmoU3FpOFMbmPj8pz44MPZ1f9+YEQIQty/NQd/2yGg # W+ufflcZ/ZE9o1M7a5Jnqf2i2/uMSWymR8r2oQBMdlyh2n5HirY4jKnFH/9gRvd+ # QOfdRrJZb1sCAwEAAaOCAWQwggFgMB8GA1UdIwQYMBaAFDLrkpr/NZZILyhAQnAg # NpFcF4XmMB0GA1UdDgQWBBQPKssghyi47G9IritUpimqF6TNDDAOBgNVHQ8BAf8E # BAMCAYYwEgYDVR0TAQH/BAgwBgEB/wIBADATBgNVHSUEDDAKBggrBgEFBQcDAzAb # BgNVHSAEFDASMAYGBFUdIAAwCAYGZ4EMAQQBMEsGA1UdHwREMEIwQKA+oDyGOmh0 # dHA6Ly9jcmwuc2VjdGlnby5jb20vU2VjdGlnb1B1YmxpY0NvZGVTaWduaW5nUm9v # dFI0Ni5jcmwwewYIKwYBBQUHAQEEbzBtMEYGCCsGAQUFBzAChjpodHRwOi8vY3J0 # LnNlY3RpZ28uY29tL1NlY3RpZ29QdWJsaWNDb2RlU2lnbmluZ1Jvb3RSNDYucDdj # MCMGCCsGAQUFBzABhhdodHRwOi8vb2NzcC5zZWN0aWdvLmNvbTANBgkqhkiG9w0B # AQwFAAOCAgEABv+C4XdjNm57oRUgmxP/BP6YdURhw1aVcdGRP4Wh60BAscjW4HL9 # hcpkOTz5jUug2oeunbYAowbFC2AKK+cMcXIBD0ZdOaWTsyNyBBsMLHqafvIhrCym # laS98+QpoBCyKppP0OcxYEdU0hpsaqBBIZOtBajjcw5+w/KeFvPYfLF/ldYpmlG+ # vd0xqlqd099iChnyIMvY5HexjO2AmtsbpVn0OhNcWbWDRF/3sBp6fWXhz7DcML4i # TAWS+MVXeNLj1lJziVKEoroGs9Mlizg0bUMbOalOhOfCipnx8CaLZeVme5yELg09 # Jlo8BMe80jO37PU8ejfkP9/uPak7VLwELKxAMcJszkyeiaerlphwoKx1uHRzNyE6 # bxuSKcutisqmKL5OTunAvtONEoteSiabkPVSZ2z76mKnzAfZxCl/3dq3dUNw4rg3 # sTCggkHSRqTqlLMS7gjrhTqBmzu1L90Y1KWN/Y5JKdGvspbOrTfOXyXvmPL6E52z # 1NZJ6ctuMFBQZH3pwWvqURR8AgQdULUvrxjUYbHHj95Ejza63zdrEcxWLDX6xWls # /GDnVNueKjWUH3fTv1Y8Wdho698YADR7TNx8X8z2Bev6SivBBOHY+uqiirZtg0y9 # ShQoPzmCcn63Syatatvx157YK9hlcPmVoa1oDE5/L9Uo2bC5a4CH2RwwggZZMIIE # waADAgECAhANIM3qwHRbWKHw+Zq6JhzlMA0GCSqGSIb3DQEBDAUAMFQxCzAJBgNV # BAYTAkdCMRgwFgYDVQQKEw9TZWN0aWdvIExpbWl0ZWQxKzApBgNVBAMTIlNlY3Rp # Z28gUHVibGljIENvZGUgU2lnbmluZyBDQSBSMzYwHhcNMjExMDIyMDAwMDAwWhcN # MjQxMDIxMjM1OTU5WjBdMQswCQYDVQQGEwJESzEUMBIGA1UECAwLSG92ZWRzdGFk # ZW4xGzAZBgNVBAoMEkZyZWRkeSBLcmlzdGlhbnNlbjEbMBkGA1UEAwwSRnJlZGR5 # IEtyaXN0aWFuc2VuMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAgYC5 # tlg+VRktRRkahxxaV8+DAd6vHoDpcO6w7yT24lnSoMuA6nR7kgy90Y/sHIwKE9Ww # t/px/GAY8eBePWjJrFpG8fBtJbXadRTVd/470Hs/q9t+kh6A/0ELj7wYsKSNOyuF # Poy4rtClOv9ZmrRpoDVnh8Epwg2DpklX2BNzykzBQxIbkpp+xVo2mhPNWDIesntc # 4/BnSebLGw1Vkxmu2acKkIjYrne/7lsuyL9ue0vk8TGk9JBPNPbGKJvHu9szP9oG # oH36fU1sEZ+AacXrp+onsyPf/hkkpAMHAhzQHl+5Ikvcus/cDm06twm7VywmZcas # 2rFAV5MyE6WMEaYAolwAHiPz9WAs2GDhFtZZg1tzbRjJIIgPpR+doTIcpcDBcHnN # dSdgWKrTkr2f339oT5bnJfo7oVzc/2HGWvb8Fom6LQAqSC11vWmznHYsCm72g+fo # TKqW8lLDfLF0+aFvToLosrtW9l6Z+l+RQ8MtJ9EHOm2Ny8cFLzZCDZYw32BydwcL # V5rKdy4Ica9on5xZvyMOLiFwuL4v2V4pjEgKJaGSS/IVSMEGjrM9DHT6YS4/oq9q # 20rQUmMZZQmGmEyyKQ8t11si8VHtScN5m0Li8peoWfCU9mRFxSESwTWow8d462+o # 9/SzmDxCACdFwzvfKx4JqDMm55cL+beunIvc0NsCAwEAAaOCAZwwggGYMB8GA1Ud # IwQYMBaAFA8qyyCHKLjsb0iuK1SmKaoXpM0MMB0GA1UdDgQWBBTZD6uy9ZWIIqQh # 3srYu1FlUhdM0TAOBgNVHQ8BAf8EBAMCB4AwDAYDVR0TAQH/BAIwADATBgNVHSUE # DDAKBggrBgEFBQcDAzARBglghkgBhvhCAQEEBAMCBBAwSgYDVR0gBEMwQTA1Bgwr # BgEEAbIxAQIBAwIwJTAjBggrBgEFBQcCARYXaHR0cHM6Ly9zZWN0aWdvLmNvbS9D # UFMwCAYGZ4EMAQQBMEkGA1UdHwRCMEAwPqA8oDqGOGh0dHA6Ly9jcmwuc2VjdGln # by5jb20vU2VjdGlnb1B1YmxpY0NvZGVTaWduaW5nQ0FSMzYuY3JsMHkGCCsGAQUF # BwEBBG0wazBEBggrBgEFBQcwAoY4aHR0cDovL2NydC5zZWN0aWdvLmNvbS9TZWN0 # aWdvUHVibGljQ29kZVNpZ25pbmdDQVIzNi5jcnQwIwYIKwYBBQUHMAGGF2h0dHA6 # Ly9vY3NwLnNlY3RpZ28uY29tMA0GCSqGSIb3DQEBDAUAA4IBgQASEbZACurQeQN8 # WDTR+YyNpoQ29YAbbdBRhhzHkT/1ao7LE0QIOgGR4GwKRzufCAwu8pCBiMOUTDHT # ezkh0rQrG6khxBX2nSTBL5i4LwKMR08HgZBsbECciABy15yexYWoB/D0H8WuGe63 # PhGWueR4IFPbIz+jEVxfW0Nyyr7bXTecpKd1iprm+TOmzc2E6ab95dkcXdJVx6Zy # s++QrrOfQ+a57qEXkS/wnjjbN9hukL0zg+g8L4DHLKTodzfiQOampvV8QzbnB7Y8 # YjNcxR9s/nptnlQH3jorNFhktiBXvD62jc8pAIg6wyH6NxSMjtTsn7QhkIp2kusw # IQwD8hN/fZ/m6gkXZhRJWFr2WRZOz+edZ62Jf25C/NYWscwfBwn2hzRZf1HgyxkX # Al88dvvUA3kw1T6uo8aAB9IcL6Owiy7q4T+RLRF7oqx0vcw0193Yhq/gPOaUFlqz # ExP6TQ5TR9XWVPQk+a1B1ATKMLi1JShO6KWTmNkFkgkgpkW69BEwggauMIIElqAD # AgECAhAHNje3JFR82Ees/ShmKl5bMA0GCSqGSIb3DQEBCwUAMGIxCzAJBgNVBAYT # AlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2Vy # dC5jb20xITAfBgNVBAMTGERpZ2lDZXJ0IFRydXN0ZWQgUm9vdCBHNDAeFw0yMjAz # MjMwMDAwMDBaFw0zNzAzMjIyMzU5NTlaMGMxCzAJBgNVBAYTAlVTMRcwFQYDVQQK # Ew5EaWdpQ2VydCwgSW5jLjE7MDkGA1UEAxMyRGlnaUNlcnQgVHJ1c3RlZCBHNCBS # U0E0MDk2IFNIQTI1NiBUaW1lU3RhbXBpbmcgQ0EwggIiMA0GCSqGSIb3DQEBAQUA # A4ICDwAwggIKAoICAQDGhjUGSbPBPXJJUVXHJQPE8pE3qZdRodbSg9GeTKJtoLDM # g/la9hGhRBVCX6SI82j6ffOciQt/nR+eDzMfUBMLJnOWbfhXqAJ9/UO0hNoR8XOx # s+4rgISKIhjf69o9xBd/qxkrPkLcZ47qUT3w1lbU5ygt69OxtXXnHwZljZQp09ns # ad/ZkIdGAHvbREGJ3HxqV3rwN3mfXazL6IRktFLydkf3YYMZ3V+0VAshaG43IbtA # rF+y3kp9zvU5EmfvDqVjbOSmxR3NNg1c1eYbqMFkdECnwHLFuk4fsbVYTXn+149z # k6wsOeKlSNbwsDETqVcplicu9Yemj052FVUmcJgmf6AaRyBD40NjgHt1biclkJg6 # OBGz9vae5jtb7IHeIhTZgirHkr+g3uM+onP65x9abJTyUpURK1h0QCirc0PO30qh # HGs4xSnzyqqWc0Jon7ZGs506o9UD4L/wojzKQtwYSH8UNM/STKvvmz3+DrhkKvp1 # KCRB7UK/BZxmSVJQ9FHzNklNiyDSLFc1eSuo80VgvCONWPfcYd6T/jnA+bIwpUzX # 6ZhKWD7TA4j+s4/TXkt2ElGTyYwMO1uKIqjBJgj5FBASA31fI7tk42PgpuE+9sJ0 # sj8eCXbsq11GdeJgo1gJASgADoRU7s7pXcheMBK9Rp6103a50g5rmQzSM7TNsQID # AQABo4IBXTCCAVkwEgYDVR0TAQH/BAgwBgEB/wIBADAdBgNVHQ4EFgQUuhbZbU2F # L3MpdpovdYxqII+eyG8wHwYDVR0jBBgwFoAU7NfjgtJxXWRM3y5nP+e6mK4cD08w # DgYDVR0PAQH/BAQDAgGGMBMGA1UdJQQMMAoGCCsGAQUFBwMIMHcGCCsGAQUFBwEB # BGswaTAkBggrBgEFBQcwAYYYaHR0cDovL29jc3AuZGlnaWNlcnQuY29tMEEGCCsG # AQUFBzAChjVodHRwOi8vY2FjZXJ0cy5kaWdpY2VydC5jb20vRGlnaUNlcnRUcnVz # dGVkUm9vdEc0LmNydDBDBgNVHR8EPDA6MDigNqA0hjJodHRwOi8vY3JsMy5kaWdp # Y2VydC5jb20vRGlnaUNlcnRUcnVzdGVkUm9vdEc0LmNybDAgBgNVHSAEGTAXMAgG # BmeBDAEEAjALBglghkgBhv1sBwEwDQYJKoZIhvcNAQELBQADggIBAH1ZjsCTtm+Y # qUQiAX5m1tghQuGwGC4QTRPPMFPOvxj7x1Bd4ksp+3CKDaopafxpwc8dB+k+YMjY # C+VcW9dth/qEICU0MWfNthKWb8RQTGIdDAiCqBa9qVbPFXONASIlzpVpP0d3+3J0 # FNf/q0+KLHqrhc1DX+1gtqpPkWaeLJ7giqzl/Yy8ZCaHbJK9nXzQcAp876i8dU+6 # WvepELJd6f8oVInw1YpxdmXazPByoyP6wCeCRK6ZJxurJB4mwbfeKuv2nrF5mYGj # VoarCkXJ38SNoOeY+/umnXKvxMfBwWpx2cYTgAnEtp/Nh4cku0+jSbl3ZpHxcpzp # SwJSpzd+k1OsOx0ISQ+UzTl63f8lY5knLD0/a6fxZsNBzU+2QJshIUDQtxMkzdwd # eDrknq3lNHGS1yZr5Dhzq6YBT70/O3itTK37xJV77QpfMzmHQXh6OOmc4d0j/R0o # 08f56PGYX/sr2H7yRp11LB4nLCbbbxV7HhmLNriT1ObyF5lZynDwN7+YAN8gFk8n # +2BnFqFmut1VwDophrCYoCvtlUG3OtUVmDG0YgkPCr2B2RP+v6TR81fZvAT6gt4y # 3wSJ8ADNXcL50CN/AAvkdgIm2fBldkKmKYcJRyvmfxqkhQ/8mJb2VVQrH4D6wPIO # K+XW+6kvRBVK5xMOHds3OBqhK/bt1nz8MIIGwjCCBKqgAwIBAgIQBUSv85SdCDmm # v9s/X+VhFjANBgkqhkiG9w0BAQsFADBjMQswCQYDVQQGEwJVUzEXMBUGA1UEChMO # RGlnaUNlcnQsIEluYy4xOzA5BgNVBAMTMkRpZ2lDZXJ0IFRydXN0ZWQgRzQgUlNB # NDA5NiBTSEEyNTYgVGltZVN0YW1waW5nIENBMB4XDTIzMDcxNDAwMDAwMFoXDTM0 # MTAxMzIzNTk1OVowSDELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDkRpZ2lDZXJ0LCBJ # bmMuMSAwHgYDVQQDExdEaWdpQ2VydCBUaW1lc3RhbXAgMjAyMzCCAiIwDQYJKoZI # hvcNAQEBBQADggIPADCCAgoCggIBAKNTRYcdg45brD5UsyPgz5/X5dLnXaEOCdwv # SKOXejsqnGfcYhVYwamTEafNqrJq3RApih5iY2nTWJw1cb86l+uUUI8cIOrHmjsv # lmbjaedp/lvD1isgHMGXlLSlUIHyz8sHpjBoyoNC2vx/CSSUpIIa2mq62DvKXd4Z # GIX7ReoNYWyd/nFexAaaPPDFLnkPG2ZS48jWPl/aQ9OE9dDH9kgtXkV1lnX+3RCh # G4PBuOZSlbVH13gpOWvgeFmX40QrStWVzu8IF+qCZE3/I+PKhu60pCFkcOvV5aDa # Y7Mu6QXuqvYk9R28mxyyt1/f8O52fTGZZUdVnUokL6wrl76f5P17cz4y7lI0+9S7 # 69SgLDSb495uZBkHNwGRDxy1Uc2qTGaDiGhiu7xBG3gZbeTZD+BYQfvYsSzhUa+0 # rRUGFOpiCBPTaR58ZE2dD9/O0V6MqqtQFcmzyrzXxDtoRKOlO0L9c33u3Qr/eTQQ # fqZcClhMAD6FaXXHg2TWdc2PEnZWpST618RrIbroHzSYLzrqawGw9/sqhux7Ujip # mAmhcbJsca8+uG+W1eEQE/5hRwqM/vC2x9XH3mwk8L9CgsqgcT2ckpMEtGlwJw1P # t7U20clfCKRwo+wK8REuZODLIivK8SgTIUlRfgZm0zu++uuRONhRB8qUt+JQofM6 # 04qDy0B7AgMBAAGjggGLMIIBhzAOBgNVHQ8BAf8EBAMCB4AwDAYDVR0TAQH/BAIw # ADAWBgNVHSUBAf8EDDAKBggrBgEFBQcDCDAgBgNVHSAEGTAXMAgGBmeBDAEEAjAL # BglghkgBhv1sBwEwHwYDVR0jBBgwFoAUuhbZbU2FL3MpdpovdYxqII+eyG8wHQYD # VR0OBBYEFKW27xPn783QZKHVVqllMaPe1eNJMFoGA1UdHwRTMFEwT6BNoEuGSWh0 # dHA6Ly9jcmwzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydFRydXN0ZWRHNFJTQTQwOTZT # SEEyNTZUaW1lU3RhbXBpbmdDQS5jcmwwgZAGCCsGAQUFBwEBBIGDMIGAMCQGCCsG # AQUFBzABhhhodHRwOi8vb2NzcC5kaWdpY2VydC5jb20wWAYIKwYBBQUHMAKGTGh0 # dHA6Ly9jYWNlcnRzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydFRydXN0ZWRHNFJTQTQw # OTZTSEEyNTZUaW1lU3RhbXBpbmdDQS5jcnQwDQYJKoZIhvcNAQELBQADggIBAIEa # 1t6gqbWYF7xwjU+KPGic2CX/yyzkzepdIpLsjCICqbjPgKjZ5+PF7SaCinEvGN1O # tt5s1+FgnCvt7T1IjrhrunxdvcJhN2hJd6PrkKoS1yeF844ektrCQDifXcigLiV4 # JZ0qBXqEKZi2V3mP2yZWK7Dzp703DNiYdk9WuVLCtp04qYHnbUFcjGnRuSvExnvP # nPp44pMadqJpddNQ5EQSviANnqlE0PjlSXcIWiHFtM+YlRpUurm8wWkZus8W8oM3 # NG6wQSbd3lqXTzON1I13fXVFoaVYJmoDRd7ZULVQjK9WvUzF4UbFKNOt50MAcN7M # mJ4ZiQPq1JE3701S88lgIcRWR+3aEUuMMsOI5ljitts++V+wQtaP4xeR0arAVeOG # v6wnLEHQmjNKqDbUuXKWfpd5OEhfysLcPTLfddY2Z1qJ+Panx+VPNTwAvb6cKmx5 # AdzaROY63jg7B145WPR8czFVoIARyxQMfq68/qTreWWqaNYiyjvrmoI1VygWy2ny # Mpqy0tg6uLFGhmu6F/3Ed2wVbK6rr3M66ElGt9V/zLY4wNjsHPW2obhDLN9OTH0e # aHDAdwrUAuBcYLso/zjlUlrWrBciI0707NMX+1Br/wd3H3GXREHJuEbTbDJ8WC9n # R2XlG3O2mflrLAZG70Ee8PBf4NvZrZCARK+AEEGKMYIGPjCCBjoCAQEwaDBUMQsw # CQYDVQQGEwJHQjEYMBYGA1UEChMPU2VjdGlnbyBMaW1pdGVkMSswKQYDVQQDEyJT # ZWN0aWdvIFB1YmxpYyBDb2RlIFNpZ25pbmcgQ0EgUjM2AhANIM3qwHRbWKHw+Zq6 # JhzlMA0GCWCGSAFlAwQCAQUAoIGEMBgGCisGAQQBgjcCAQwxCjAIoAKAAKECgAAw # GQYJKoZIhvcNAQkDMQwGCisGAQQBgjcCAQQwHAYKKwYBBAGCNwIBCzEOMAwGCisG # AQQBgjcCARUwLwYJKoZIhvcNAQkEMSIEIDC0h3woDitR/QFHAeGd01VXz3i4vNxR # UuLF6Vutp+3xMA0GCSqGSIb3DQEBAQUABIICACTTJd8C/CqdF7Fn6ba2ndaqm8Rz # hwjl0w3rbCsFjQoAVVDu9uDvXGU7G+6qnQVr5+OI61xGYqPYMQJwLIAEUqnpkewF # GMcZTk0U2g47vvl+U4Q97CG0v1eRR6kaHTWWs5bsKu33ChqfY+MvOrYN9n26ONV9 # 31bl4s30aRk8ZUb7P2pEgpXkU+UpmoyZ6pCwZwiEO7qY16rBFMsft4RK99AMp5jN # VppjjegcXLb1oMpppHHwI0QvQ0I2bmIS1X4WyGopUGD8sVo4qCAMqUpA29ozBnI/ # ynE+OMQ1mVwpFlGMbRz8mJmpWZi/kYQtn1skb6CA+CCut2VxZCu9KfILrp1Ftf0Q # ZMcDPq2JADrm32pwm8CVY0WC0tCLd9inDuh0pWlCHjStnyvgQtQEw0OUf+17Sm0+ # KuaVmQlV2vPOlynbMgpOr64q4rt2VDR7oIWpwz/v/BaR8cbxGzWMI19+N6FvAAq1 # A7yZtPF+NNNXHLLmjDlQgzLw9o+3wjEkvVUTc5LpvhgsCw55pkBsDIcKikiIW4Bt # IJ3p1TwOuLZKHypniNu7SlNUr+Ax8FWJsnUgqbFHCps8sNUfBP3ZUMnmF66F9sFn # u+ibQr9+G/RXsgcy96d7PgloRmhxULgg+Jo9Y3kUjqKyrQgsxdFOv56VD8rvX1k6 # BYdI7CUntD0GN9W8oYIDIDCCAxwGCSqGSIb3DQEJBjGCAw0wggMJAgEBMHcwYzEL # MAkGA1UEBhMCVVMxFzAVBgNVBAoTDkRpZ2lDZXJ0LCBJbmMuMTswOQYDVQQDEzJE # aWdpQ2VydCBUcnVzdGVkIEc0IFJTQTQwOTYgU0hBMjU2IFRpbWVTdGFtcGluZyBD # QQIQBUSv85SdCDmmv9s/X+VhFjANBglghkgBZQMEAgEFAKBpMBgGCSqGSIb3DQEJ # AzELBgkqhkiG9w0BBwEwHAYJKoZIhvcNAQkFMQ8XDTI0MDgwOTA3NDkwNVowLwYJ # KoZIhvcNAQkEMSIEIA0JYwq5h8QtWzqMXA0FX+zsU5RiypxzHAJl64UlpUOyMA0G # CSqGSIb3DQEBAQUABIICAAJVVIAxul8FQ7l+TUyDnIiOevuh2eZCvwNhjqsxlROD # iIZn6yAVMGXxS3ug+1NhRkPBKJJUjUvZxaGFFVXDt6WDLFTi7aNajTDh1uOMzGmB # Yepky/+JhdUWDN5AaD1Rc6HdXd7unOlFw6KXm8U08W6AGSMe9ZektQKfhG6BxJnB # a6NjtuAubkd7UIuN7Nw5eh0mSeR4rEngQ12/4CEaTG0CSxdybnKJ9KpnQuNzIAoI # ZOI9b11saa7bEWiogKsomC8xBdfh+DGQNpDvQBHNsef4qG61+BW+zSOCdOkOS+L/ # neZbDgV1DztmcGWXG6fVu1EB+MprEK5+XhUtQtFzXIenwiDS/4FY6UWgp5OmO+cw # D/e/OxD5mn+riEDDTN1nXYBvOWkorNITvDqa9Uxw4i+QiatQ1mnGfK1BqX0Vyszo # 8j2b+o/ubc1K/NBIjnHBMHrCKR1SQX4PeS+3BmVpei5Y3zMgDcFNGkeSX2q2EQ1m # 7/GoT59fqPIJxi5dcjOfFqDwoQnayKY+XMts/nJ/54peomULlJKxN4wwPfGKBavr # TgYWs2+5CDr5wVBp4vku9EB3EkUNxr7CagIorO3jG46NsY8fjPe/85nf8OB9aOpu # Gm5KunOypSVc7DtF/k6HbzJAHY3gN1PbGDwAWV67pFM05kb2A8OKc+ZmWaqgvkhG # SIG # End signature block |