StoreBroker/NugetTools.ps1
# Copyright (C) Microsoft Corporation. All rights reserved. function Get-NugetExe { <# .SYNOPSIS Downloads nuget.exe from http://nuget.org to a new local temporary directory and returns the path to the local copy. .DESCRIPTION Downloads nuget.exe from http://nuget.org to a new local temporary directory and returns the path to the local copy. The Git repo for this module can be found here: http://aka.ms/StoreBroker .EXAMPLE Get-NugetExe Creates a new directory with a GUID under $env:TEMP and then downloads http://nuget.org/nuget.exe to that location. .OUTPUTS System.String - The path to the newly downloaded nuget.exe #> [CmdletBinding(SupportsShouldProcess)] [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSShouldProcess", "", Justification="Methods called within here make use of PSShouldProcess, and the switch is passed on to them inherently.")] param() if ($null -eq $script:nugetExePath) { $sourceNugetExe = "https://dist.nuget.org/win-x86-commandline/latest/nuget.exe" $script:nugetExePath = Join-Path $(New-TemporaryDirectory) "nuget.exe" Write-Log "Downloading $sourceNugetExe to $script:nugetExePath" -Level Verbose Invoke-WebRequest $sourceNugetExe -OutFile $script:nugetExePath } return $script:nugetExePath } function Get-NugetPackage { <# .SYNOPSIS Downloads a nuget package to the specified directory. .DESCRIPTION Downloads a nuget package to the specified directory (or the current directory if no TargetPath was specified). The Git repo for this module can be found here: http://aka.ms/StoreBroker .PARAMETER PackageName The name of the nuget package to download .PARAMETER TargetPath The nuget package will be downloaded to this location. .PARAMETER Version If provided, this indicates the version of the package to download. If not specified, downloads the latest version. .PARAMETER NoStatus If this switch is specified, long-running commands will run on the main thread with no commandline status update. When not specified, those commands run in the background, enabling the command prompt to provide status information. .EXAMPLE Get-NugetPackage "Microsoft.AzureStorage" -Version "6.0.0.0" -TargetPath "c:\foo" Downloads v6.0.0.0 of the Microsoft.AzureStorage nuget package to the c:\foo directory. .EXAMPLE Get-NugetPackage "Microsoft.AzureStorage" "c:\foo" Downloads the most recent version of the Microsoft.AzureStorage nuget package to the c:\foo directory. #> [CmdletBinding(SupportsShouldProcess)] param( [Parameter( Mandatory, ValueFromPipeline)] [string] $PackageName, [Parameter(Mandatory)] [ValidateScript({if (Test-Path -Path $_ -PathType Container) { $true } else { throw "$_ does not exist." }})] [string] $TargetPath, [string] $Version = "", [switch] $NoStatus ) Write-Log "Downloading nuget package [$PackageName] to [$TargetPath]" -Level Verbose $nugetPath = Get-NugetExe if ($NoStatus) { if ($PSCmdlet.ShouldProcess($PackageName, $nugetPath)) { if (-not [System.String]::IsNullOrEmpty($Version)) { & $nugetPath install $PackageName -o $TargetPath -version $Version -source nuget.org -NonInteractive | Out-Null } else { & $nugetPath install $PackageName -o $TargetPath -source nuget.org -NonInteractive | Out-Null } } } else { $jobName = "Get-NugetPackage-" + (Get-Date).ToFileTime().ToString() if ($PSCmdlet.ShouldProcess($jobName, "Start-Job")) { [scriptblock]$scriptBlock = { param($NugetPath, $PackageName, $TargetPath, $Version) if (-not [System.String]::IsNullOrEmpty($Version)) { & $NugetPath install $PackageName -o $TargetPath -version $Version -source nuget.org } else { & $NugetPath install $PackageName -o $TargetPath -source nuget.org } } Start-Job -Name $jobName -ScriptBlock $scriptBlock -Arg @($nugetPath, $PackageName, $TargetPath, $Version) | Out-Null if ($PSCmdlet.ShouldProcess($jobName, "Wait-JobWithAnimation")) { Wait-JobWithAnimation -JobName $jobName -Description "Retrieving nuget package: $PackageName" } if ($PSCmdlet.ShouldProcess($jobName, "Receive-Job")) { Receive-Job $jobName -AutoRemoveJob -Wait -ErrorAction SilentlyContinue -ErrorVariable remoteErrors | Out-Null } } if ($remoteErrors.Count -gt 0) { throw $remoteErrors[0].Exception } } } function Test-AssemblyIsDesiredVersion { <# .SYNOPSIS Checks if the specified file is the expected version. .DESCRIPTION Checks if the specified file is the expected version. Does a best effort match. If you only specify a desired version of "6", any version of the file that has a "major" version of 6 will be considered a match, where we use the terminology of a version being: Major.Minor.Build.PrivateInfo. The Git repo for this module can be found here: http://aka.ms/StoreBroker .PARAMETER AssemblyPath The full path to the assembly file being tested. .PARAMETER DesiredVersion The desired version of the assembly. Specify the version as specifically as necessary. .EXAMPLE Test-AssemblyIsDesiredVersion "c:\Microsoft.WindowsAzure.Storage.dll" "6" Returns back $true if "c:\Microsoft.WindowsAzure.Storage.dll" has a major version of 6, regardless of its Minor, Build or PrivateInfo numbers. .OUTPUTS Boolean - $true if the assembly at the specified path exists and meets the specified version criteria, $false otherwise. #> param( [Parameter(Mandatory)] [ValidateScript( { if (Test-Path -PathType Leaf -Path $_) { $true } else { throw "'$_' cannot be found." } })] [string] $AssemblyPath, [Parameter(Mandatory)] [ValidateScript( { if ($_ -match '^\d+(\.\d+){0,3}$') { $true } else { throw "'$_' not a valid version format." } })] [string] $DesiredVersion ) $splitTargetVer = $DesiredVersion.Split('.') $versionInfo = (Get-Item -Path $AssemblyPath).VersionInfo $splitSourceVer = @( $versionInfo.ProductMajorPart, $versionInfo.ProductMinorPart, $versionInfo.ProductBuildPart, $versionInfo.ProductPrivatePart ) # The cmdlet contract states that we only care about matching # as much of the version number as the user has supplied. for ($i = 0; $i -lt $splitTargetVer.Count; $i++) { if ($splitSourceVer[$i] -ne $splitTargetVer[$i]) { return $false } } return $true } function Get-NugetPackageDllPath { <# .SYNOPSIS Makes sure that the specified assembly from a nuget package is available on the machine, and returns the path to it. .DESCRIPTION Makes sure that the specified assembly from a nuget package is available on the machine, and returns the path to it. This will first look for the assembly in the module's script directory. Next it will look for the assembly in the location defined by $SBAlternateAssemblyDir. This value would have to be defined by the user prior to execution of this cmdlet. If not found there, it will look in a temp folder established during this PowerShell session. If still not found, it will download the nuget package for it to a temp folder accessible during this PowerShell session. The Git repo for this module can be found here: http://aka.ms/StoreBroker .PARAMETER NugetPackageName The name of the nuget package to download .PARAMETER NugetPackageVersion Indicates the version of the package to download. .PARAMETER AssemblyPackageTailDirectory The sub-path within the nuget package download location where the assembly should be found. .PARAMETER AssemblyName The name of the actual assembly that the user is looking for. .PARAMETER NoStatus If this switch is specified, long-running commands will run on the main thread with no commandline status update. When not specified, those commands run in the background, enabling the command prompt to provide status information. .EXAMPLE Get-NugetPackageDllPath "WindowsAzure.Storage" "6.0.0" "WindowsAzure.Storage.6.0.0\lib\net40\" "Microsoft.WindowsAzure.Storage.dll" Returns back the path to "Microsoft.WindowsAzure.Storage.dll", which is part of the "WindowsAzure.Storage" nuget package. If the package has to be downloaded via nuget, the command prompt will show a time duration status counter while the package is being downloaded. .EXAMPLE Get-NugetPackageDllPath "WindowsAzure.Storage" "6.0.0" "WindowsAzure.Storage.6.0.0\lib\net40\" "Microsoft.WindowsAzure.Storage.dll" -NoStatus Returns back the path to "Microsoft.WindowsAzure.Storage.dll", which is part of the "WindowsAzure.Storage" nuget package. If the package has to be downloaded via nuget, the command prompt will appear to hang during this time. .OUTPUTS System.String - The full path to $AssemblyName. #> [CmdletBinding(SupportsShouldProcess)] [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSShouldProcess", "", Justification="Methods called within here make use of PSShouldProcess, and the switch is passed on to them inherently.")] param( [Parameter(Mandatory)] [string] $NugetPackageName, [Parameter(Mandatory)] [string] $NugetPackageVersion, [Parameter(Mandatory)] [string] $AssemblyPackageTailDirectory, [Parameter(Mandatory)] [string] $AssemblyName, [switch] $NoStatus ) Write-Log "Looking for $AssemblyName" -Level Verbose # First we'll check to see if the user has cached the assembly into the module's script directory $moduleAssembly = Join-Path $PSScriptRoot $AssemblyName if (Test-Path -Path $moduleAssembly -PathType Leaf) { if (Test-AssemblyIsDesiredVersion -AssemblyPath $moduleAssembly -DesiredVersion $NugetPackageVersion) { Write-Log "Found $AssemblyName in module directory ($PSScriptRoot)." -Level Verbose return $moduleAssembly } else { Write-Log "Found $AssemblyName in module directory ($PSScriptRoot), but its version number didn't match required [$NugetPackageVersion]." -Level Verbose } } # Next, we'll check to see if the user has defined an alternate path to get the assembly from if (-not [System.String]::IsNullOrEmpty($SBAlternateAssemblyDir)) { $alternateAssemblyPath = Join-Path $SBAlternateAssemblyDir $AssemblyName if (Test-Path -Path $alternateAssemblyPath -PathType Leaf) { if (Test-AssemblyIsDesiredVersion -AssemblyPath $alternateAssemblyPath -DesiredVersion $NugetPackageVersion) { Write-Log "Found $AssemblyName in alternate directory ($SBAlternateAssemblyDir)." -Level Verbose return $alternateAssemblyPath } else { Write-Log "Found $AssemblyName in alternate directory ($SBAlternateAssemblyDir), but its version number didn't match required [$NugetPackageVersion]." -Level Verbose } } } # Then we'll check to see if we've previously cached the assembly in a temp folder during this PowerShell session if ([System.String]::IsNullOrEmpty($script:tempAssemblyCacheDir)) { $script:tempAssemblyCacheDir = New-TemporaryDirectory } else { $cachedAssemblyPath = Join-Path $(Join-Path $script:tempAssemblyCacheDir $AssemblyPackageTailDirectory) $AssemblyName if (Test-Path -Path $cachedAssemblyPath -PathType Leaf) { if (Test-AssemblyIsDesiredVersion -AssemblyPath $cachedAssemblyPath -DesiredVersion $NugetPackageVersion) { Write-Log "Found $AssemblyName in temp directory ($script:tempAssemblyCacheDir)." -Level Verbose return $cachedAssemblyPath } else { Write-Log "Found $AssemblyName in temp directory ($script:tempAssemblyCacheDir), but its version number didn't match required [$NugetPackageVersion]." -Level Verbose } } } # Still not found, so we'll go ahead and download the package via nuget. Write-Log "$AssemblyName is needed and wasn't found. Acquiring it via nuget..." -Level Verbose Get-NugetPackage -PackageName $NugetPackageName -Version $NugetPackageVersion -TargetPath $script:tempAssemblyCacheDir -NoStatus:$NoStatus $cachedAssemblyPath = Join-Path $(Join-Path $script:tempAssemblyCacheDir $AssemblyPackageTailDirectory) $AssemblyName if (Test-Path -Path $cachedAssemblyPath -PathType Leaf) { $output = @() $output += "To avoid this download delay in the future, copy the following file:" $output += " [$cachedAssemblyPath]" $output += "either to:" $output += " [$PSScriptRoot]" $output += "or to:" $output += " a directory of your choosing, and save that directory path to `$SBAlternateAssemblyDir" Write-Log $($output -join [Environment]::NewLine) return $cachedAssemblyPath } $output = "Unable to acquire a reference to $AssemblyName." Write-Log $output -Level Error throw $output } # SIG # Begin signature block # MIIdrgYJKoZIhvcNAQcCoIIdnzCCHZsCAQExCzAJBgUrDgMCGgUAMGkGCisGAQQB # gjcCAQSgWzBZMDQGCisGAQQBgjcCAR4wJgIDAQAABBAfzDtgWUsITrck0sYpfvNR # AgEAAgEAAgEAAgEAAgEAMCEwCQYFKw4DAhoFAAQUo5nl2MGQD38MYiRimCngvfZ6 # 6P6gghhSMIIEwTCCA6mgAwIBAgITMwAAANd4Xn6sPypBiwAAAAAA1zANBgkqhkiG # 9w0BAQUFADB3MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4G # A1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSEw # HwYDVQQDExhNaWNyb3NvZnQgVGltZS1TdGFtcCBQQ0EwHhcNMTcxMDAyMjI1NzU3 # WhcNMTkwMTAyMjI1NzU3WjCBsTELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hp # bmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jw # b3JhdGlvbjEMMAoGA1UECxMDQU9DMSYwJAYDVQQLEx1UaGFsZXMgVFNTIEVTTjo5 # NkZGLTRCQzUtQTdEQzElMCMGA1UEAxMcTWljcm9zb2Z0IFRpbWUtU3RhbXAgU2Vy # dmljZTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAK0R8WghBzWrkgfD # oLwDByma12IHhlSPBbAGiWXRc2ixEiXWFkoH5IDW4fNnINAgbfCWThv3zAknQDa3 # H9IkZcvHSKEPgt7/MpC2LzuYiBGS7osE1YFJru5o3eQ15jRt+//Sk8j4fwis41Aj # CNiePkK8wCHusRFyEOABoMC2KjUwrAEQbsMCCcm9AYq3QXc7tvvDncJfnmSfK8KY # 1isAuPJcfIOsh7ugzUoklOUbkByfrwc51oWxyRhZTMGyJcvskauQzpqw8QIPJi4U # pv/cW8ylaXvDD5rd+J7hJzkWpl/eg21LssBR2TdIVfJs48u99rvgf+ka05hE2lSL # nnd67RUCAwEAAaOCAQkwggEFMB0GA1UdDgQWBBTYj8Ia8/dzgo7zIAVoJi/V/PwV # PTAfBgNVHSMEGDAWgBQjNPjZUkZwCu1A+3b7syuwwzWzDzBUBgNVHR8ETTBLMEmg # R6BFhkNodHRwOi8vY3JsLm1pY3Jvc29mdC5jb20vcGtpL2NybC9wcm9kdWN0cy9N # aWNyb3NvZnRUaW1lU3RhbXBQQ0EuY3JsMFgGCCsGAQUFBwEBBEwwSjBIBggrBgEF # BQcwAoY8aHR0cDovL3d3dy5taWNyb3NvZnQuY29tL3BraS9jZXJ0cy9NaWNyb3Nv # ZnRUaW1lU3RhbXBQQ0EuY3J0MBMGA1UdJQQMMAoGCCsGAQUFBwMIMA0GCSqGSIb3 # DQEBBQUAA4IBAQBtoYxTxcEg/Q/A+oGoitT3aME8OF7a1OAQqSPnV3OLGFLv3uPY # X8nvdOTnhbKV6BIsW/DGukZflJjCo9I5D9+wz0s9hICPFEqvfpqZumy2T94K7veD # 21BOZ59xfVauLrbWtBpISdd2kmGsaYacwd/Bf7ih4gmRKWdpGeLcYvN9d8fb68bt # qwJLKb0B161HcM0SYJ9VxYkvDVqc8YtcH5CszKWLnR2lzBBXR8447n3RY/2ulRFW # FD82SsbqpWVUo7JnVaphz9qR5Jn9iarO/SNmtmobYwDPwVpmq4ef2w6iypR3Nrn/ # PaDv6e7qm3mYnkYtM13zQXbBBQ6DgWferczAMIIGADCCA+igAwIBAgITMwAAAMMO # m6fYstz3LAAAAAAAwzANBgkqhkiG9w0BAQsFADB+MQswCQYDVQQGEwJVUzETMBEG # A1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWlj # cm9zb2Z0IENvcnBvcmF0aW9uMSgwJgYDVQQDEx9NaWNyb3NvZnQgQ29kZSBTaWdu # aW5nIFBDQSAyMDExMB4XDTE3MDgxMTIwMjAyNFoXDTE4MDgxMTIwMjAyNFowdDEL # MAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1v # bmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEeMBwGA1UEAxMVTWlj # cm9zb2Z0IENvcnBvcmF0aW9uMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC # AQEAu1fXONGxBn9JLalts2Oferq2OiFbtJiujdSkgaDFdcUs74JAKreBU3fzYwEK # vM43hANAQ1eCS87tH7b9gG3JwpFdBcfcVlkA4QzrV9798biQJ791Svx1snJYtsVI # mzNiBdGVlKW/OSKtjRJNRmLaMhnOqiJcVkixb0XJZ3ZiXTCIoy8oxR9QKtmG2xoR # JYHC9PVnLud5HfXiHHX0TszH/Oe/C4BHKf/PzWmxDAtg62fmhBubTf1tRzrH2cFh # YfKVEqENB65jIdj0mRz/eFWB7qV56CCCXwratVMZVAFXDYeRjcJ88VSGgOFi24Jz # PiZe8EAS0jnVJgMNhYgxXwoLiwIDAQABo4IBfzCCAXswHwYDVR0lBBgwFgYKKwYB # BAGCN0wIAQYIKwYBBQUHAwMwHQYDVR0OBBYEFKcTXR8hiVXoA+6eFzbq8lSINRmv # MFEGA1UdEQRKMEikRjBEMQwwCgYDVQQLEwNBT0MxNDAyBgNVBAUTKzIzMDAxMitj # ODA0YjVlYS00OWI0LTQyMzgtODM2Mi1kODUxZmEyMjU0ZmMwHwYDVR0jBBgwFoAU # SG5k5VAF04KqFzc3IrVtqMp1ApUwVAYDVR0fBE0wSzBJoEegRYZDaHR0cDovL3d3 # dy5taWNyb3NvZnQuY29tL3BraW9wcy9jcmwvTWljQ29kU2lnUENBMjAxMV8yMDEx # LTA3LTA4LmNybDBhBggrBgEFBQcBAQRVMFMwUQYIKwYBBQUHMAKGRWh0dHA6Ly93 # d3cubWljcm9zb2Z0LmNvbS9wa2lvcHMvY2VydHMvTWljQ29kU2lnUENBMjAxMV8y # MDExLTA3LTA4LmNydDAMBgNVHRMBAf8EAjAAMA0GCSqGSIb3DQEBCwUAA4ICAQBN # l080fvFwk5zj1RpLnBF+aybEpST030TUJLqzagiJmZrLMedwm/8UHbAHOX/kMDsT # It4OyJVnu25++HyVpJCCN5Omg9NJAsGsrVnvkbenZgAOokwl1NznXQcCyig0ZTs5 # g62VKo7KoOgIOhz+PntASZRNjlQlCuWxxwrucTfGm1429adCRPu8h7ANwDXZJodf # /2fvKHT3ijAEEYpnzEs1YGoh58ONB4Nem6udcR8pJgkR1PWC09I2Bymu6JJtkH8A # yahb7tAEZfuhDldTzPKYifOfFZPIBsRjUmECT1dIHPX7dRLKtfn0wmlfu6GdDWmD # J+uDPh1rMcPuDvHEhEOH7jGcBgAyfLcgirkII+pWsBjUsr0V7DftZNNrFQIjxooz # hzrRm7bAllksoAFThAFf8nvBerDs1NhS9l91gURZFjgnU7tQ815x3/fXUdwx1Rpj # NSqXfp9mN1/PVTPvssq8LCOqRB7u+2dItOhCww+KUViiRgJhJloZv1yU6ahAcOdb # MEx8gNRQZ6Kl7g7rPbXx5Xke4fVYGW+7iW144iBYJf/kSLPmr/GyQAQXRlDUDGyR # FH3uyuL2Jt4bOwRnUS4PpBf3Qv8/kYkx+Ke8s+U6UtwqM39KZJFl2GURtttqt7Rs # Uvy/i3EWxCzOc5qg6V0IwUVFpSmG7AExbV50xlYxCzCCBgcwggPvoAMCAQICCmEW # aDQAAAAAABwwDQYJKoZIhvcNAQEFBQAwXzETMBEGCgmSJomT8ixkARkWA2NvbTEZ # MBcGCgmSJomT8ixkARkWCW1pY3Jvc29mdDEtMCsGA1UEAxMkTWljcm9zb2Z0IFJv # b3QgQ2VydGlmaWNhdGUgQXV0aG9yaXR5MB4XDTA3MDQwMzEyNTMwOVoXDTIxMDQw # MzEzMDMwOVowdzELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAO # BgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEh # MB8GA1UEAxMYTWljcm9zb2Z0IFRpbWUtU3RhbXAgUENBMIIBIjANBgkqhkiG9w0B # AQEFAAOCAQ8AMIIBCgKCAQEAn6Fssd/bSJIqfGsuGeG94uPFmVEjUK3O3RhOJA/u # 0afRTK10MCAR6wfVVJUVSZQbQpKumFwwJtoAa+h7veyJBw/3DgSY8InMH8szJIed # 8vRnHCz8e+eIHernTqOhwSNTyo36Rc8J0F6v0LBCBKL5pmyTZ9co3EZTsIbQ5ShG # Lieshk9VUgzkAyz7apCQMG6H81kwnfp+1pez6CGXfvjSE/MIt1NtUrRFkJ9IAEpH # ZhEnKWaol+TTBoFKovmEpxFHFAmCn4TtVXj+AZodUAiFABAwRu233iNGu8QtVJ+v # HnhBMXfMm987g5OhYQK1HQ2x/PebsgHOIktU//kFw8IgCwIDAQABo4IBqzCCAacw # DwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUIzT42VJGcArtQPt2+7MrsMM1sw8w # CwYDVR0PBAQDAgGGMBAGCSsGAQQBgjcVAQQDAgEAMIGYBgNVHSMEgZAwgY2AFA6s # gmBAVieX5SUT/CrhClOVWeSkoWOkYTBfMRMwEQYKCZImiZPyLGQBGRYDY29tMRkw # FwYKCZImiZPyLGQBGRYJbWljcm9zb2Z0MS0wKwYDVQQDEyRNaWNyb3NvZnQgUm9v # dCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHmCEHmtFqFKoKWtTHNY9AcTLmUwUAYDVR0f # BEkwRzBFoEOgQYY/aHR0cDovL2NybC5taWNyb3NvZnQuY29tL3BraS9jcmwvcHJv # ZHVjdHMvbWljcm9zb2Z0cm9vdGNlcnQuY3JsMFQGCCsGAQUFBwEBBEgwRjBEBggr # BgEFBQcwAoY4aHR0cDovL3d3dy5taWNyb3NvZnQuY29tL3BraS9jZXJ0cy9NaWNy # b3NvZnRSb290Q2VydC5jcnQwEwYDVR0lBAwwCgYIKwYBBQUHAwgwDQYJKoZIhvcN # AQEFBQADggIBABCXisNcA0Q23em0rXfbznlRTQGxLnRxW20ME6vOvnuPuC7UEqKM # bWK4VwLLTiATUJndekDiV7uvWJoc4R0Bhqy7ePKL0Ow7Ae7ivo8KBciNSOLwUxXd # T6uS5OeNatWAweaU8gYvhQPpkSokInD79vzkeJkuDfcH4nC8GE6djmsKcpW4oTmc # Zy3FUQ7qYlw/FpiLID/iBxoy+cwxSnYxPStyC8jqcD3/hQoT38IKYY7w17gX606L # f8U1K16jv+u8fQtCe9RTciHuMMq7eGVcWwEXChQO0toUmPU8uWZYsy0v5/mFhsxR # VuidcJRsrDlM1PZ5v6oYemIp76KbKTQGdxpiyT0ebR+C8AvHLLvPQ7Pl+ex9teOk # qHQ1uE7FcSMSJnYLPFKMcVpGQxS8s7OwTWfIn0L/gHkhgJ4VMGboQhJeGsieIiHQ # Q+kr6bv0SMws1NgygEwmKkgkX1rqVu+m3pmdyjpvvYEndAYR7nYhv5uCwSdUtrFq # PYmhdmG0bqETpr+qR/ASb/2KMmyy/t9RyIwjyWa9nR2HEmQCPS2vWY+45CHltbDK # Y7R4VAXUQS5QrJSwpXirs6CWdRrZkocTdSIvMqgIbqBbjCW/oO+EyiHW6x5PyZru # SeD3AWVviQt9yGnI5m7qp5fOMSn/DsVbXNhNG6HY+i+ePy5VFmvJE6P9MIIHejCC # BWKgAwIBAgIKYQ6Q0gAAAAAAAzANBgkqhkiG9w0BAQsFADCBiDELMAkGA1UEBhMC # VVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNV # BAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEyMDAGA1UEAxMpTWljcm9zb2Z0IFJv # b3QgQ2VydGlmaWNhdGUgQXV0aG9yaXR5IDIwMTEwHhcNMTEwNzA4MjA1OTA5WhcN # MjYwNzA4MjEwOTA5WjB+MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3Rv # bjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0 # aW9uMSgwJgYDVQQDEx9NaWNyb3NvZnQgQ29kZSBTaWduaW5nIFBDQSAyMDExMIIC # IjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAq/D6chAcLq3YbqqCEE00uvK2 # WCGfQhsqa+laUKq4BjgaBEm6f8MMHt03a8YS2AvwOMKZBrDIOdUBFDFC04kNeWSH # fpRgJGyvnkmc6Whe0t+bU7IKLMOv2akrrnoJr9eWWcpgGgXpZnboMlImEi/nqwhQ # z7NEt13YxC4Ddato88tt8zpcoRb0RrrgOGSsbmQ1eKagYw8t00CT+OPeBw3VXHml # SSnnDb6gE3e+lD3v++MrWhAfTVYoonpy4BI6t0le2O3tQ5GD2Xuye4Yb2T6xjF3o # iU+EGvKhL1nkkDstrjNYxbc+/jLTswM9sbKvkjh+0p2ALPVOVpEhNSXDOW5kf1O6 # nA+tGSOEy/S6A4aN91/w0FK/jJSHvMAhdCVfGCi2zCcoOCWYOUo2z3yxkq4cI6ep # ZuxhH2rhKEmdX4jiJV3TIUs+UsS1Vz8kA/DRelsv1SPjcF0PUUZ3s/gA4bysAoJf # 28AVs70b1FVL5zmhD+kjSbwYuER8ReTBw3J64HLnJN+/RpnF78IcV9uDjexNSTCn # q47f7Fufr/zdsGbiwZeBe+3W7UvnSSmnEyimp31ngOaKYnhfsi+E11ecXL93KCjx # 7W3DKI8sj0A3T8HhhUSJxAlMxdSlQy90lfdu+HggWCwTXWCVmj5PM4TasIgX3p5O # 9JawvEagbJjS4NaIjAsCAwEAAaOCAe0wggHpMBAGCSsGAQQBgjcVAQQDAgEAMB0G # A1UdDgQWBBRIbmTlUAXTgqoXNzcitW2oynUClTAZBgkrBgEEAYI3FAIEDB4KAFMA # dQBiAEMAQTALBgNVHQ8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAfBgNVHSMEGDAW # gBRyLToCMZBDuRQFTuHqp8cx0SOJNDBaBgNVHR8EUzBRME+gTaBLhklodHRwOi8v # Y3JsLm1pY3Jvc29mdC5jb20vcGtpL2NybC9wcm9kdWN0cy9NaWNSb29DZXJBdXQy # MDExXzIwMTFfMDNfMjIuY3JsMF4GCCsGAQUFBwEBBFIwUDBOBggrBgEFBQcwAoZC # aHR0cDovL3d3dy5taWNyb3NvZnQuY29tL3BraS9jZXJ0cy9NaWNSb29DZXJBdXQy # MDExXzIwMTFfMDNfMjIuY3J0MIGfBgNVHSAEgZcwgZQwgZEGCSsGAQQBgjcuAzCB # gzA/BggrBgEFBQcCARYzaHR0cDovL3d3dy5taWNyb3NvZnQuY29tL3BraW9wcy9k # b2NzL3ByaW1hcnljcHMuaHRtMEAGCCsGAQUFBwICMDQeMiAdAEwAZQBnAGEAbABf # AHAAbwBsAGkAYwB5AF8AcwB0AGEAdABlAG0AZQBuAHQALiAdMA0GCSqGSIb3DQEB # CwUAA4ICAQBn8oalmOBUeRou09h0ZyKbC5YR4WOSmUKWfdJ5DJDBZV8uLD74w3LR # bYP+vj/oCso7v0epo/Np22O/IjWll11lhJB9i0ZQVdgMknzSGksc8zxCi1LQsP1r # 4z4HLimb5j0bpdS1HXeUOeLpZMlEPXh6I/MTfaaQdION9MsmAkYqwooQu6SpBQyb # 7Wj6aC6VoCo/KmtYSWMfCWluWpiW5IP0wI/zRive/DvQvTXvbiWu5a8n7dDd8w6v # mSiXmE0OPQvyCInWH8MyGOLwxS3OW560STkKxgrCxq2u5bLZ2xWIUUVYODJxJxp/ # sfQn+N4sOiBpmLJZiWhub6e3dMNABQamASooPoI/E01mC8CzTfXhj38cbxV9Rad2 # 5UAqZaPDXVJihsMdYzaXht/a8/jyFqGaJ+HNpZfQ7l1jQeNbB5yHPgZ3BtEGsXUf # FL5hYbXw3MYbBL7fQccOKO7eZS/sl/ahXJbYANahRr1Z85elCUtIEJmAH9AAKcWx # m6U/RXceNcbSoqKfenoi+kiVH6v7RyOA9Z74v2u3S5fi63V4GuzqN5l5GEv/1rMj # aHXmr/r8i+sLgOppO6/8MO0ETI7f33VtY5E90Z1WTk+/gFcioXgRMiF670EKsT/7 # qMykXcGhiJtXcVZOSEXAQsmbdlsKgEhr/Xmfwb1tbWrJUnMTDXpQzTGCBMYwggTC # AgEBMIGVMH4xCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYD # VQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xKDAm # BgNVBAMTH01pY3Jvc29mdCBDb2RlIFNpZ25pbmcgUENBIDIwMTECEzMAAADDDpun # 2LLc9ywAAAAAAMMwCQYFKw4DAhoFAKCB2jAZBgkqhkiG9w0BCQMxDAYKKwYBBAGC # NwIBBDAcBgorBgEEAYI3AgELMQ4wDAYKKwYBBAGCNwIBFTAjBgkqhkiG9w0BCQQx # FgQUPw4yP63+SFHxex63WDIVmrACCIQwegYKKwYBBAGCNwIBDDFsMGqgKIAmAFMA # dABvAHIAZQBCAHIAbwBrAGUAcgAgAFMAaQBnAG4AaQBuAGehPoA8aHR0cDovL2Vk # d2ViL3NpdGVzL0lTU0VuZ2luZWVyaW5nL0VuZ0Z1bi9TaXRlUGFnZXMvSG9tZS5h # c3B4MA0GCSqGSIb3DQEBAQUABIIBAEzFJTUiTHI4Bra23FJWKi29ZKNNJ7sJLC63 # nZ4FcsBveeb0aV8nicyhxGTGyRvnGF6z80qANspOagII7s/vZlPdW7c/4hw84qSq # cYwPh8VOcZ+LD2042fcQv78xSVh7K5pdNxwbPdYwtjxtuBSWhmApraZQ+CpghhHw # M/HJP8ZB2e7TAa7wAScYsKho6XsT2VhVM97T9q9Dx5DY5wlVo0pORxOnhQr5svJ6 # C5ZnzuJ+QyAKgBGZyBNyclequlEinkxHgWtTLA/YfvCsb9sdsNmhStyylaP+odvX # UW/unaUwZISk9Dox9HFJnqHH3pY6x4gh7w9L69A+QPAhndAr2z+hggIoMIICJAYJ # KoZIhvcNAQkGMYICFTCCAhECAQEwgY4wdzELMAkGA1UEBhMCVVMxEzARBgNVBAgT # Cldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29m # dCBDb3Jwb3JhdGlvbjEhMB8GA1UEAxMYTWljcm9zb2Z0IFRpbWUtU3RhbXAgUENB # AhMzAAAA13hefqw/KkGLAAAAAADXMAkGBSsOAwIaBQCgXTAYBgkqhkiG9w0BCQMx # CwYJKoZIhvcNAQcBMBwGCSqGSIb3DQEJBTEPFw0xNzEyMTQyMzEzMDRaMCMGCSqG # SIb3DQEJBDEWBBRwGXgcSp84czZHF3F14uxCoxh/QzANBgkqhkiG9w0BAQUFAASC # AQAVjwQa8hh1B6DSDyJJb9YkPbzKpTzzlwrYDiwrOK4/lidHWT3seiAERMdBkBp1 # 0nDPWfZgZ1EykbLYs2/PTVi663Uu7jMnVv0Xt6sNo8PK8+Q6WtlYxelXxJDnafNn # ZIvNFfC+mCAfo68ca+ffMluF8NtisjNjYPU6jPiSmdEPB0BGI8+/5xrOLj8oQO9q # kTdtT5TqpZdEOlBuxnPztQo0ehy38RomVDizAwn+VCuZgGmNbei+tGt1dEG+gUP5 # ypwP9PJdeoracMPb8Zm2zCCfD5NRo5R9YK4gcTH2/wPYrQF1i4wL6HDL3SRLdAiK # wpUybdF7nzYKmRPcCaXouc0+ # SIG # End signature block |