functions/Save-DbaCommunitySoftware.ps1
function Save-DbaCommunitySoftware { <# .SYNOPSIS Download and extract software from Github to update the local cached version of that software. .DESCRIPTION Download and extract software from Github to update the local cached version of that software. This command is run from inside of Install-Dba* and Update-Dba* commands to update the local cache if needed. .PARAMETER Software Name of the software to download. Options include: * MaintenanceSolution: SQL Server Maintenance Solution created by Ola Hallengren (https://ola.hallengren.com) * FirstResponderKit: First Responder Kit created by Brent Ozar (http://FirstResponderKit.org) * DarlingData: Erik Darling's stored procedures (https://www.erikdarlingdata.com) * SQLWATCH: SQL Server Monitoring Solution created by Marcin Gminski (https://sqlwatch.io/) * WhoIsActive: Adam Machanic's comprehensive activity monitoring stored procedure sp_WhoIsActive (https://github.com/amachanic/sp_whoisactive) .PARAMETER Branch Specifies the branch. Defaults to master or main. Can only be used if Software is used. .PARAMETER LocalFile Specifies the path to a local file to install from instead of downloading from Github. .PARAMETER Url Specifies the URL to download from. Is not needed if Software is used. .PARAMETER LocalDirectory Specifies the local directory to extract the downloaded file to. Is not needed if Software is used. .PARAMETER WhatIf If this switch is enabled, no actions are performed but informational messages will be displayed that explain what would happen if the command were to run. .PARAMETER Confirm If this switch is enabled, you will be prompted for confirmation before executing any operations that change state. .PARAMETER EnableException By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message. This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting. Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch. .NOTES Tags: Community Author: Andreas Jordan, @JordanOrdix Website: https://dbatools.io Copyright: (c) 2021 by dbatools, licensed under MIT License: MIT https://opensource.org/licenses/MIT .LINK https://dbatools.io/Save-DbaCommunitySoftware .EXAMPLE PS C:\> Save-DbaCommunitySoftware -Software MaintenanceSolution Updates the local cache of Ola Hallengren's Solution objects. #> [CmdletBinding(SupportsShouldProcess, ConfirmImpact = "Medium")] param ( [ValidateSet('MaintenanceSolution', 'FirstResponderKit', 'DarlingData', 'SQLWATCH', 'WhoIsActive')] [string]$Software, [string]$Branch, [string]$LocalFile, [string]$Url, [string]$LocalDirectory, [switch]$EnableException ) process { $dbatoolsData = Get-DbatoolsConfigValue -FullName "Path.DbatoolsData" # Set Branch, Url and LocalDirectory for known Software if ($Software -eq 'MaintenanceSolution') { if (-not $Branch) { $Branch = 'master' } if (-not $Url) { $Url = "https://github.com/olahallengren/sql-server-maintenance-solution/archive/$Branch.zip" } if (-not $LocalDirectory) { $LocalDirectory = Join-Path -Path $dbatoolsData -ChildPath "sql-server-maintenance-solution-$Branch" } } elseif ($Software -eq 'FirstResponderKit') { if (-not $Branch) { $Branch = 'main' } if (-not $Url) { $Url = "https://github.com/BrentOzarULTD/SQL-Server-First-Responder-Kit/archive/$Branch.zip" } if (-not $LocalDirectory) { $LocalDirectory = Join-Path -Path $dbatoolsData -ChildPath "SQL-Server-First-Responder-Kit-$Branch" } } elseif ($Software -eq 'DarlingData') { if (-not $Branch) { $Branch = 'master' } if (-not $Url) { $Url = "https://github.com/erikdarlingdata/DarlingData/archive/$Branch.zip" } if (-not $LocalDirectory) { $LocalDirectory = Join-Path -Path $dbatoolsData -ChildPath "DarlingData-$Branch" } } elseif ($Software -eq 'SQLWATCH') { if ($Branch -in 'prerelease', 'pre-release') { $preRelease = $true } else { $preRelease = $false } if (-not $Url -and -not $LocalFile) { $releasesUrl = "https://api.github.com/repos/marcingminski/sqlwatch/releases" try { try { $releasesJson = Invoke-TlsWebRequest -Uri $releasesUrl -UseBasicParsing -ErrorAction Stop } catch { # Try with default proxy and usersettings (New-Object System.Net.WebClient).Proxy.Credentials = [System.Net.CredentialCache]::DefaultNetworkCredentials $releasesJson = Invoke-TlsWebRequest -Uri $releasesUrl -UseBasicParsing -ErrorAction Stop } } catch { Stop-Function -Message "Unable to get release information from $releasesUrl." -ErrorRecord $_ return } $latestRelease = ($releasesJson | ConvertFrom-Json) | Where-Object prerelease -eq $preRelease | Select-Object -First 1 if ($null -eq $latestRelease) { Stop-Function -Message "No release found." -ErrorRecord $_ return } $Url = $latestRelease.assets[0].browser_download_url } if (-not $LocalDirectory) { if ($preRelease) { $LocalDirectory = Join-Path -Path $dbatoolsData -ChildPath "SQLWATCH-prerelease" } else { $LocalDirectory = Join-Path -Path $dbatoolsData -ChildPath "SQLWATCH" } } } elseif ($Software -eq 'WhoIsActive') { # We currently ignore -Branch as there is only one branch and there are no pre-releases. if (-not $Url -and -not $LocalFile) { $releasesUrl = "https://api.github.com/repos/amachanic/sp_whoisactive/releases" try { try { $releasesJson = Invoke-TlsWebRequest -Uri $releasesUrl -UseBasicParsing -ErrorAction Stop } catch { # Try with default proxy and usersettings (New-Object System.Net.WebClient).Proxy.Credentials = [System.Net.CredentialCache]::DefaultNetworkCredentials $releasesJson = Invoke-TlsWebRequest -Uri $releasesUrl -UseBasicParsing -ErrorAction Stop } } catch { Stop-Function -Message "Unable to get release information from $releasesUrl." -ErrorRecord $_ return } $latestRelease = ($releasesJson | ConvertFrom-Json) | Select-Object -First 1 if ($null -eq $latestRelease) { Stop-Function -Message "No release found." -ErrorRecord $_ return } $Url = $latestRelease.zipball_url } if (-not $LocalDirectory) { $LocalDirectory = Join-Path -Path $dbatoolsData -ChildPath "WhoIsActive" } } # Test if we now have source and destination for the following actions. if (-not ($Url -or $LocalFile)) { Stop-Function -Message 'Url or LocalFile is missing.' return } if (-not $LocalDirectory) { Stop-Function -Message 'LocalDirectory is missing.' return } # First part is download and extract and we use the temp directory for that and clean up afterwards. # So we use a file and a folder with a random name to reduce potential conflicts, # but name them with dbatools to be able to recognize them. $temp = [System.IO.Path]::GetTempPath() $random = Get-Random $zipFile = Join-DbaPath -Path $temp -Child "dbatools_software_download_$random.zip" $zipFolder = Join-DbaPath -Path $temp -Child "dbatools_software_download_$random" if ($Software -eq 'WhoIsActive' -and $LocalFile.EndsWith('.sql')) { # For WhoIsActive, we allow to pass in the who_is_active.sql file or any other sql file with the source code. # We create the zip folder with a subfolder named WhoIsActive and copy the LocalFile there as who_is_active.sql. $appFolder = Join-DbaPath -Path $zipFolder -Child 'WhoIsActive' $appFile = Join-DbaPath -Path $appFolder -Child 'who_is_active.sql' $null = New-Item -Path $zipFolder -ItemType Directory $null = New-Item -Path $appFolder -ItemType Directory Copy-Item -Path $LocalFile -Destination $appFile } elseif ($LocalFile) { # No download, so we just extract the given file if it exists and is a zip file. if (-not (Test-Path $LocalFile)) { Stop-Function -Message "$LocalFile doesn't exist" return } if (-not ($LocalFile.EndsWith('.zip'))) { Stop-Function -Message "$LocalFile has to be a zip file" return } if ($PSCmdlet.ShouldProcess($LocalFile, "Extracting archive to $zipFolder path")) { try { Unblock-File $LocalFile -ErrorAction SilentlyContinue Expand-Archive -LiteralPath $LocalFile -DestinationPath $zipFolder -Force -ErrorAction Stop } catch { Stop-Function -Message "Unable to extract $LocalFile to $zipFolder." -ErrorRecord $_ return } } } else { # Download and extract. if ($PSCmdlet.ShouldProcess($Url, "Downloading to $zipFile")) { try { try { Invoke-TlsWebRequest -Uri $Url -OutFile $zipFile -UseBasicParsing -ErrorAction Stop } catch { # Try with default proxy and usersettings (New-Object System.Net.WebClient).Proxy.Credentials = [System.Net.CredentialCache]::DefaultNetworkCredentials Invoke-TlsWebRequest -Uri $Url -OutFile $zipFile -UseBasicParsing -ErrorAction Stop } } catch { Stop-Function -Message "Unable to download $Url to $zipFile." -ErrorRecord $_ return } } if ($PSCmdlet.ShouldProcess($zipFile, "Extracting archive to $zipFolder path")) { try { Unblock-File $zipFile -ErrorAction SilentlyContinue Expand-Archive -Path $zipFile -DestinationPath $zipFolder -Force -ErrorAction Stop } catch { Stop-Function -Message "Unable to extract $zipFile to $zipFolder." -ErrorRecord $_ Remove-Item -Path $zipFile -ErrorAction SilentlyContinue return } } } # As a safety net, we test whether the archive contained exactly the desired destination directory. if ($PSCmdlet.ShouldProcess($zipFolder, "Testing for correct content")) { $localDirectoryBase = Split-Path -Path $LocalDirectory $localDirectoryName = Split-Path -Path $LocalDirectory -Leaf $sourceDirectory = Get-ChildItem -Path $zipFolder -Directory $sourceDirectoryName = $sourceDirectory.Name if ($Software -eq 'SQLWATCH') { # As this software is downloaded as a release, the directory has a different name. # Rename the directory from like 'SQLWATCH 4.3.0.23725 20210721131116' to 'SQLWATCH' to be able to handle this like the other software. if ($sourceDirectoryName -like 'SQLWATCH*') { Rename-Item -Path $sourceDirectory.FullName -NewName 'SQLWATCH' $sourceDirectory = Get-ChildItem -Path $zipFolder -Directory $sourceDirectoryName = $sourceDirectory.Name } } elseif ($Software -eq 'WhoIsActive') { # As this software is downloaded as a release, the directory has a different name. # Rename the directory from like 'amachanic-sp_whoisactive-459d2bc' to 'WhoIsActive' to be able to handle this like the other software. if ($sourceDirectoryName -like 'amachanic-sp_whoisactive-*') { Rename-Item -Path $sourceDirectory.FullName -NewName 'WhoIsActive' $sourceDirectory = Get-ChildItem -Path $zipFolder -Directory $sourceDirectoryName = $sourceDirectory.Name } } if ((Get-ChildItem -Path $zipFolder).Count -gt 1 -or $sourceDirectoryName -ne $localDirectoryName) { Stop-Function -Message "The archive does not contain the desired directory $localDirectoryName but $sourceDirectoryName." Remove-Item -Path $zipFile -ErrorAction SilentlyContinue Remove-Item -Path $zipFolder -Recurse -ErrorAction SilentlyContinue return } } # Replace the target directory by the extracted directory. if ($PSCmdlet.ShouldProcess($zipFolder, "Copying content to $LocalDirectory")) { try { if (Test-Path -Path $LocalDirectory) { Remove-Item -Path $LocalDirectory -Recurse -ErrorAction Stop } } catch { Stop-Function -Message "Unable to remove the old target directory $LocalDirectory." -ErrorRecord $_ Remove-Item -Path $zipFile -ErrorAction SilentlyContinue Remove-Item -Path $zipFolder -Recurse -ErrorAction SilentlyContinue return } try { Copy-Item -Path $sourceDirectory.FullName -Destination $localDirectoryBase -Recurse -ErrorAction Stop } catch { Stop-Function -Message "Unable to copy the directory $sourceDirectory to the target directory $localDirectoryBase." -ErrorRecord $_ Remove-Item -Path $zipFile -ErrorAction SilentlyContinue Remove-Item -Path $zipFolder -Recurse -ErrorAction SilentlyContinue return } } if ($PSCmdlet.ShouldProcess($zipFile, "Removing temporary file")) { Remove-Item -Path $zipFile -ErrorAction SilentlyContinue } if ($PSCmdlet.ShouldProcess($zipFolder, "Removing temporary folder")) { Remove-Item -Path $zipFolder -Recurse -ErrorAction SilentlyContinue } } } # SIG # Begin signature block # MIIZewYJKoZIhvcNAQcCoIIZbDCCGWgCAQExCzAJBgUrDgMCGgUAMGkGCisGAQQB # gjcCAQSgWzBZMDQGCisGAQQBgjcCAR4wJgIDAQAABBAfzDtgWUsITrck0sYpfvNR # AgEAAgEAAgEAAgEAAgEAMCEwCQYFKw4DAhoFAAQUjqSkwLs67vjD0pwNStxSPetD # dv2gghSJMIIE/jCCA+agAwIBAgIQDUJK4L46iP9gQCHOFADw3TANBgkqhkiG9w0B # AQsFADByMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYD # VQQLExB3d3cuZGlnaWNlcnQuY29tMTEwLwYDVQQDEyhEaWdpQ2VydCBTSEEyIEFz # c3VyZWQgSUQgVGltZXN0YW1waW5nIENBMB4XDTIxMDEwMTAwMDAwMFoXDTMxMDEw # NjAwMDAwMFowSDELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDkRpZ2lDZXJ0LCBJbmMu # MSAwHgYDVQQDExdEaWdpQ2VydCBUaW1lc3RhbXAgMjAyMTCCASIwDQYJKoZIhvcN # AQEBBQADggEPADCCAQoCggEBAMLmYYRnxYr1DQikRcpja1HXOhFCvQp1dU2UtAxQ # tSYQ/h3Ib5FrDJbnGlxI70Tlv5thzRWRYlq4/2cLnGP9NmqB+in43Stwhd4CGPN4 # bbx9+cdtCT2+anaH6Yq9+IRdHnbJ5MZ2djpT0dHTWjaPxqPhLxs6t2HWc+xObTOK # fF1FLUuxUOZBOjdWhtyTI433UCXoZObd048vV7WHIOsOjizVI9r0TXhG4wODMSlK # XAwxikqMiMX3MFr5FK8VX2xDSQn9JiNT9o1j6BqrW7EdMMKbaYK02/xWVLwfoYer # vnpbCiAvSwnJlaeNsvrWY4tOpXIc7p96AXP4Gdb+DUmEvQECAwEAAaOCAbgwggG0 # MA4GA1UdDwEB/wQEAwIHgDAMBgNVHRMBAf8EAjAAMBYGA1UdJQEB/wQMMAoGCCsG # AQUFBwMIMEEGA1UdIAQ6MDgwNgYJYIZIAYb9bAcBMCkwJwYIKwYBBQUHAgEWG2h0 # dHA6Ly93d3cuZGlnaWNlcnQuY29tL0NQUzAfBgNVHSMEGDAWgBT0tuEgHf4prtLk # YaWyoiWyyBc1bjAdBgNVHQ4EFgQUNkSGjqS6sGa+vCgtHUQ23eNqerwwcQYDVR0f # BGowaDAyoDCgLoYsaHR0cDovL2NybDMuZGlnaWNlcnQuY29tL3NoYTItYXNzdXJl # ZC10cy5jcmwwMqAwoC6GLGh0dHA6Ly9jcmw0LmRpZ2ljZXJ0LmNvbS9zaGEyLWFz # c3VyZWQtdHMuY3JsMIGFBggrBgEFBQcBAQR5MHcwJAYIKwYBBQUHMAGGGGh0dHA6 # Ly9vY3NwLmRpZ2ljZXJ0LmNvbTBPBggrBgEFBQcwAoZDaHR0cDovL2NhY2VydHMu # ZGlnaWNlcnQuY29tL0RpZ2lDZXJ0U0hBMkFzc3VyZWRJRFRpbWVzdGFtcGluZ0NB # LmNydDANBgkqhkiG9w0BAQsFAAOCAQEASBzctemaI7znGucgDo5nRv1CclF0CiNH # o6uS0iXEcFm+FKDlJ4GlTRQVGQd58NEEw4bZO73+RAJmTe1ppA/2uHDPYuj1UUp4 # eTZ6J7fz51Kfk6ftQ55757TdQSKJ+4eiRgNO/PT+t2R3Y18jUmmDgvoaU+2QzI2h # F3MN9PNlOXBL85zWenvaDLw9MtAby/Vh/HUIAHa8gQ74wOFcz8QRcucbZEnYIpp1 # FUL1LTI4gdr0YKK6tFL7XOBhJCVPst/JKahzQ1HavWPWH1ub9y4bTxMd90oNcX6X # t/Q/hOvB46NJofrOp79Wz7pZdmGJX36ntI5nePk2mOHLKNpbh6aKLzCCBRowggQC # oAMCAQICEAMFu4YhsKFjX7/erhIE520wDQYJKoZIhvcNAQELBQAwcjELMAkGA1UE # BhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZMBcGA1UECxMQd3d3LmRpZ2lj # ZXJ0LmNvbTExMC8GA1UEAxMoRGlnaUNlcnQgU0hBMiBBc3N1cmVkIElEIENvZGUg # U2lnbmluZyBDQTAeFw0yMDA1MTIwMDAwMDBaFw0yMzA2MDgxMjAwMDBaMFcxCzAJ # BgNVBAYTAlVTMREwDwYDVQQIEwhWaXJnaW5pYTEPMA0GA1UEBxMGVmllbm5hMREw # DwYDVQQKEwhkYmF0b29sczERMA8GA1UEAxMIZGJhdG9vbHMwggEiMA0GCSqGSIb3 # DQEBAQUAA4IBDwAwggEKAoIBAQC8v2N7q+O/vggBtpjmteofFo140k73JXQ5sOD6 # QLzjgija+scoYPxTmFSImnqtjfZFWmucAWsDiMVVro/6yGjsXmJJUA7oD5BlMdAK # fuiq4558YBOjjc0Bp3NbY5ZGujdCmsw9lqHRAVil6P1ZpAv3D/TyVVq6AjDsJY+x # rRL9iMc8YpD5tiAj+SsRSuT5qwPuW83ByRHqkaJ5YDJ/R82ZKh69AFNXoJ3xCJR+ # P7+pa8tbdSgRf25w4ZfYPy9InEvsnIRVZMeDjjuGvqr0/Mar73UI79z0NYW80yN/ # 7VzlrvV8RnniHWY2ib9ehZligp5aEqdV2/XFVPV4SKaJs8R9AgMBAAGjggHFMIIB # wTAfBgNVHSMEGDAWgBRaxLl7KgqjpepxA8Bg+S32ZXUOWDAdBgNVHQ4EFgQU8MCg # +7YDgENO+wnX3d96scvjniIwDgYDVR0PAQH/BAQDAgeAMBMGA1UdJQQMMAoGCCsG # AQUFBwMDMHcGA1UdHwRwMG4wNaAzoDGGL2h0dHA6Ly9jcmwzLmRpZ2ljZXJ0LmNv # bS9zaGEyLWFzc3VyZWQtY3MtZzEuY3JsMDWgM6Axhi9odHRwOi8vY3JsNC5kaWdp # Y2VydC5jb20vc2hhMi1hc3N1cmVkLWNzLWcxLmNybDBMBgNVHSAERTBDMDcGCWCG # SAGG/WwDATAqMCgGCCsGAQUFBwIBFhxodHRwczovL3d3dy5kaWdpY2VydC5jb20v # Q1BTMAgGBmeBDAEEATCBhAYIKwYBBQUHAQEEeDB2MCQGCCsGAQUFBzABhhhodHRw # Oi8vb2NzcC5kaWdpY2VydC5jb20wTgYIKwYBBQUHMAKGQmh0dHA6Ly9jYWNlcnRz # LmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydFNIQTJBc3N1cmVkSURDb2RlU2lnbmluZ0NB # LmNydDAMBgNVHRMBAf8EAjAAMA0GCSqGSIb3DQEBCwUAA4IBAQCPzflwlQwf1jak # EqymPOc0nBxiY7F4FwcmL7IrTLhub6Pjg4ZYfiC79Akz5aNlqO+TJ0kqglkfnOsc # jfKQzzDwcZthLVZl83igzCLnWMo8Zk/D2d4ZLY9esFwqPNvuuVDrHvgh7H6DJ/zP # Vm5EOK0sljT0UQ6HQEwtouH5S8nrqCGZ8jKM/+DeJlm+rCAGGf7TV85uqsAn5JqD # En/bXE1AlyG1Q5YiXFGS5Sf0qS4Nisw7vRrZ6Qc4NwBty4cAYjzDPDixorWI8+FV # OUWKMdL7tV8i393/XykwsccCstBCp7VnSZN+4vgzjEJQql5uQfysjcW9rrb/qixp # csPTKYRHMIIFMDCCBBigAwIBAgIQBAkYG1/Vu2Z1U0O1b5VQCDANBgkqhkiG9w0B # AQsFADBlMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYD # VQQLExB3d3cuZGlnaWNlcnQuY29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVk # IElEIFJvb3QgQ0EwHhcNMTMxMDIyMTIwMDAwWhcNMjgxMDIyMTIwMDAwWjByMQsw # CQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cu # ZGlnaWNlcnQuY29tMTEwLwYDVQQDEyhEaWdpQ2VydCBTSEEyIEFzc3VyZWQgSUQg # Q29kZSBTaWduaW5nIENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA # +NOzHH8OEa9ndwfTCzFJGc/Q+0WZsTrbRPV/5aid2zLXcep2nQUut4/6kkPApfmJ # 1DcZ17aq8JyGpdglrA55KDp+6dFn08b7KSfH03sjlOSRI5aQd4L5oYQjZhJUM1B0 # sSgmuyRpwsJS8hRniolF1C2ho+mILCCVrhxKhwjfDPXiTWAYvqrEsq5wMWYzcT6s # cKKrzn/pfMuSoeU7MRzP6vIK5Fe7SrXpdOYr/mzLfnQ5Ng2Q7+S1TqSp6moKq4Tz # rGdOtcT3jNEgJSPrCGQ+UpbB8g8S9MWOD8Gi6CxR93O8vYWxYoNzQYIH5DiLanMg # 0A9kczyen6Yzqf0Z3yWT0QIDAQABo4IBzTCCAckwEgYDVR0TAQH/BAgwBgEB/wIB # ADAOBgNVHQ8BAf8EBAMCAYYwEwYDVR0lBAwwCgYIKwYBBQUHAwMweQYIKwYBBQUH # AQEEbTBrMCQGCCsGAQUFBzABhhhodHRwOi8vb2NzcC5kaWdpY2VydC5jb20wQwYI # KwYBBQUHMAKGN2h0dHA6Ly9jYWNlcnRzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydEFz # c3VyZWRJRFJvb3RDQS5jcnQwgYEGA1UdHwR6MHgwOqA4oDaGNGh0dHA6Ly9jcmw0 # LmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydEFzc3VyZWRJRFJvb3RDQS5jcmwwOqA4oDaG # NGh0dHA6Ly9jcmwzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydEFzc3VyZWRJRFJvb3RD # QS5jcmwwTwYDVR0gBEgwRjA4BgpghkgBhv1sAAIEMCowKAYIKwYBBQUHAgEWHGh0 # dHBzOi8vd3d3LmRpZ2ljZXJ0LmNvbS9DUFMwCgYIYIZIAYb9bAMwHQYDVR0OBBYE # FFrEuXsqCqOl6nEDwGD5LfZldQ5YMB8GA1UdIwQYMBaAFEXroq/0ksuCMS1Ri6en # IZ3zbcgPMA0GCSqGSIb3DQEBCwUAA4IBAQA+7A1aJLPzItEVyCx8JSl2qB1dHC06 # GsTvMGHXfgtg/cM9D8Svi/3vKt8gVTew4fbRknUPUbRupY5a4l4kgU4QpO4/cY5j # DhNLrddfRHnzNhQGivecRk5c/5CxGwcOkRX7uq+1UcKNJK4kxscnKqEpKBo6cSgC # PC6Ro8AlEeKcFEehemhor5unXCBc2XGxDI+7qPjFEmifz0DLQESlE/DmZAwlCEIy # sjaKJAL+L3J+HNdJRZboWR3p+nRka7LrZkPas7CM1ekN3fYBIM6ZMWM9CBoYs4Gb # T8aTEAb8B4H6i9r5gkn3Ym6hU/oSlBiFLpKR6mhsRDKyZqHnGKSaZFHvMIIFMTCC # BBmgAwIBAgIQCqEl1tYyG35B5AXaNpfCFTANBgkqhkiG9w0BAQsFADBlMQswCQYD # VQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGln # aWNlcnQuY29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJvb3QgQ0Ew # HhcNMTYwMTA3MTIwMDAwWhcNMzEwMTA3MTIwMDAwWjByMQswCQYDVQQGEwJVUzEV # MBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29t # MTEwLwYDVQQDEyhEaWdpQ2VydCBTSEEyIEFzc3VyZWQgSUQgVGltZXN0YW1waW5n # IENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAvdAy7kvNj3/dqbqC # mcU5VChXtiNKxA4HRTNREH3Q+X1NaH7ntqD0jbOI5Je/YyGQmL8TvFfTw+F+CNZq # FAA49y4eO+7MpvYyWf5fZT/gm+vjRkcGGlV+Cyd+wKL1oODeIj8O/36V+/OjuiI+ # GKwR5PCZA207hXwJ0+5dyJoLVOOoCXFr4M8iEA91z3FyTgqt30A6XLdR4aF5FMZN # JCMwXbzsPGBqrC8HzP3w6kfZiFBe/WZuVmEnKYmEUeaC50ZQ/ZQqLKfkdT66mA+E # f58xFNat1fJky3seBdCEGXIX8RcG7z3N1k3vBkL9olMqT4UdxB08r8/arBD13ays # 6Vb/kwIDAQABo4IBzjCCAcowHQYDVR0OBBYEFPS24SAd/imu0uRhpbKiJbLIFzVu # MB8GA1UdIwQYMBaAFEXroq/0ksuCMS1Ri6enIZ3zbcgPMBIGA1UdEwEB/wQIMAYB # Af8CAQAwDgYDVR0PAQH/BAQDAgGGMBMGA1UdJQQMMAoGCCsGAQUFBwMIMHkGCCsG # AQUFBwEBBG0wazAkBggrBgEFBQcwAYYYaHR0cDovL29jc3AuZGlnaWNlcnQuY29t # MEMGCCsGAQUFBzAChjdodHRwOi8vY2FjZXJ0cy5kaWdpY2VydC5jb20vRGlnaUNl # cnRBc3N1cmVkSURSb290Q0EuY3J0MIGBBgNVHR8EejB4MDqgOKA2hjRodHRwOi8v # Y3JsNC5kaWdpY2VydC5jb20vRGlnaUNlcnRBc3N1cmVkSURSb290Q0EuY3JsMDqg # OKA2hjRodHRwOi8vY3JsMy5kaWdpY2VydC5jb20vRGlnaUNlcnRBc3N1cmVkSURS # b290Q0EuY3JsMFAGA1UdIARJMEcwOAYKYIZIAYb9bAACBDAqMCgGCCsGAQUFBwIB # FhxodHRwczovL3d3dy5kaWdpY2VydC5jb20vQ1BTMAsGCWCGSAGG/WwHATANBgkq # hkiG9w0BAQsFAAOCAQEAcZUS6VGHVmnN793afKpjerN4zwY3QITvS4S/ys8DAv3F # p8MOIEIsr3fzKx8MIVoqtwU0HWqumfgnoma/Capg33akOpMP+LLR2HwZYuhegiUe # xLoceywh4tZbLBQ1QwRostt1AuByx5jWPGTlH0gQGF+JOGFNYkYkh2OMkVIsrymJ # 5Xgf1gsUpYDXEkdws3XVk4WTfraSZ/tTYYmo9WuWwPRYaQ18yAGxuSh1t5ljhSKM # Ycp5lH5Z/IwP42+1ASa2bKXuh1Eh5Fhgm7oMLSttosR+u8QlK0cCCHxJrhO24XxC # QijGGFbPQTS2Zl22dHv1VjMiLyI2skuiSpXY9aaOUjGCBFwwggRYAgEBMIGGMHIx # CzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3 # dy5kaWdpY2VydC5jb20xMTAvBgNVBAMTKERpZ2lDZXJ0IFNIQTIgQXNzdXJlZCBJ # RCBDb2RlIFNpZ25pbmcgQ0ECEAMFu4YhsKFjX7/erhIE520wCQYFKw4DAhoFAKB4 # MBgGCisGAQQBgjcCAQwxCjAIoAKAAKECgAAwGQYJKoZIhvcNAQkDMQwGCisGAQQB # gjcCAQQwHAYKKwYBBAGCNwIBCzEOMAwGCisGAQQBgjcCARUwIwYJKoZIhvcNAQkE # MRYEFEbjXujSRgz4LHbnzUPxLDjlx3gAMA0GCSqGSIb3DQEBAQUABIIBAImwvlbS # V7pmEWHJmCD2Z8uD8eMRS+2W2X5sCB6KU6zJKOdYFfkyV6q/2pQcVKY1Wf1kKZmd # L7OtqTY2c+MYXxOJ0sB/AOOmon6C1gVlTHoAN/hstlbkhOB27SA1cUXfa8GBd8/A # OUzY/2OsfhMV7W3VhEqRyYUp/JV5ChzsFKh3Whkdp9LaF0aFUrMG8Ya0sb2fHRYv # 01vR/a+Q9a5gIxcWe6c8V5tiWTA12+Vs5qs9YsdmBYX4TI6LpwBOZbQ6w3yLU005 # cHIP0d2nBireDo7BVzPEjc4hNjU4BSzLND+vT9zDkyy19mVDP1z+WFSZyPq2/3Yx # ho9x+wYO4nNGjyehggIwMIICLAYJKoZIhvcNAQkGMYICHTCCAhkCAQEwgYYwcjEL # MAkGA1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZMBcGA1UECxMQd3d3 # LmRpZ2ljZXJ0LmNvbTExMC8GA1UEAxMoRGlnaUNlcnQgU0hBMiBBc3N1cmVkIElE # IFRpbWVzdGFtcGluZyBDQQIQDUJK4L46iP9gQCHOFADw3TANBglghkgBZQMEAgEF # AKBpMBgGCSqGSIb3DQEJAzELBgkqhkiG9w0BBwEwHAYJKoZIhvcNAQkFMQ8XDTIx # MTEwNjExMjAwMVowLwYJKoZIhvcNAQkEMSIEIALfml9uuvWbkxRc0S7PemHAt0Ot # OjD2iUOF/jrbYZVXMA0GCSqGSIb3DQEBAQUABIIBAJaI/8TxnaFpAjGWYTTbE6PQ # R3CEMI+eA76UQKP9n+Q+fxsrgKzM5A0HBAUDTSEJk1Z2VxROe06fH/Cmrt3zsh4C # SvYKoI0VrD5tqVEq58dEUv6oF1FHEXesMcjEJMmgaQsaIOLij4EGA6eKe0Wf6Y0F # O489Hy7x3MumJE1UN6U3ZB3QD+JxSiCkXUI5Qw/XfEMrdP6IXl5OqvMI8swM6wYt # Ejw4DvgaSJJUaFkX9o3tYPwGjY4AzVhe4Xf6nViFDSmf23LHtg8kywomAQCcYwI8 # 1UycupdLOX3nCFfwEpafzOXoeNVNpB9F4xqkygSB+1+cxnPYiUd5H3CWgsGhRBM= # SIG # End signature block |