Src/Private/Private.ps1

<#
    .SYNOPSIS
        Ensures a directory path exists, creating it if necessary
#>

function Assert-Directory
{
    [CmdletBinding()]
    param
    (
        [Parameter(Mandatory, ValueFromPipeline, ValueFromPipelineByPropertyName)]
        [System.String] $Path
    )
    process
    {
        if (Test-Path -Path $Path -PathType Leaf)
        {
            throw ($localized.SpecifiedPathIsNotEmptyError -f $Path)
        }
        elseif (-not (Test-Path -Path $Path))
        {
            Write-Verbose -Message ($localized.CreatingPackageDirectory -f $Path)
            $parentPath = Split-Path -Path $Path -Parent
            $childPath = Split-Path -Path $Path -Leaf
            try
            {
                $null = New-Item -Path $parentPath -Name $childPath -ItemType Directory -Force
            }
            catch
            {
                Write-Error -ErrorRecord $_ -ErrorAction Stop
            }
        }
    }
}

<#
    .SYNOPSIS
        Ensures that package metadata file is (or isn't) cached locally.
#>

function Assert-EvergreenPackageCacheMetadata
{
    [CmdletBinding()]
    param
    (
        ## Evergreen package(s) metadata to cache
        [Parameter(Mandatory, ValueFromPipeline, ValueFromPipelineByPropertyName)]
        [System.String] $Name,

        ## Evergreen Api subscription key
        [Parameter(Mandatory, ValueFromPipelineByPropertyName, ParameterSetName = 'Default')]
        [System.String] $SubscriptionKey,

        ## Enumerate customer package repository.
        [Parameter(ValueFromPipelineByPropertyName)]
        [System.String] $CustomerCode,

        ## Web proxy credential
        [Parameter(ValueFromPipelineByPropertyName, ParameterSetName = 'Default')]
        [System.Management.Automation.PSCredential]
        [System.Management.Automation.CredentialAttribute()]
        $Credential,

        ## Force refresh of cached package metadata
        [Parameter(ValueFromPipelineByPropertyName, ParameterSetName = 'Default')]
        [System.Management.Automation.SwitchParameter] $Force,

        ## Include hidden (test and deprecated) packages.
        [Parameter(ValueFromPipelineByPropertyName)]
        [System.Management.Automation.SwitchParameter] $Hidden,

        [Parameter(ValueFromPipelineByPropertyName)]
        [System.Management.Automation.SwitchParameter] $NotPresent
    )
    process
    {
        $metadataFilename = '{0}.psd1' -f $Name
        $destinationPath = Join-Path -Path $cachedMetadataPath -ChildPath $metadataFilename
        $metadataItem = Get-Item -Path $destinationPath -ErrorAction SilentlyContinue

        if ($NotPresent)
        {
            if ($null -ne $metadataItem)
            {
                Write-Verbose -Message ($localized.RemovingCachedPackageMetadata -f $metadataItem.FullName)
                Remove-Item -Path $metadataItem.FullName -Force
            }
        }
        else
        {
            if ($Force -or ($null -eq $metadataItem))
            {
                $invokeEvergreenMetadataDownloadParams = @{
                    Name            = $Name
                    SubscriptionKey = $SubscriptionKey
                    Force           = $Force
                }
                if ($PSBoundParameters.ContainsKey('CustomerCode'))
                {
                    $invokeEvergreenMetadataDownloadParams['CustomerCode'] = $CustomerCode
                }
                if ($PSBoundParameters.ContainsKey('Credential'))
                {
                    $invokeEvergreenMetadataDownloadParams['Credential'] = $Credential
                }
                if ($PSBoundParameters.ContainsKey('Hidden'))
                {
                    $invokeEvergreenMetadataDownloadParams['Hidden'] = $Hidden.ToBool()
                }
                $null = Invoke-EvergreenMetadataDownload @invokeEvergreenMetadataDownloadParams
            }
        }
    }
}

<#
    .SYNOPSIS
        Downloads an Evergreen package (if not already cached) and extracting any Zip packages.
#>

function Assert-EvergreenPackageDownload
{
    [CmdletBinding()]
    [OutputType()]
    param
    (
        [Parameter(Mandatory, ValueFromPipelineByPropertyName)]
        [System.String] $Name,

        [Parameter(Mandatory, ValueFromPipelineByPropertyName)]
        [System.String] $SubscriptionKey,

        ## Enumerate customer package repository.
        [Parameter(ValueFromPipelineByPropertyName)]
        [System.String] $CustomerCode,

        ## Web proxy credential
        [Parameter(ValueFromPipelineByPropertyName)]
        [System.Management.Automation.PSCredential]
        [System.Management.Automation.CredentialAttribute()]
        $Credential,

        ## Refresh cached package metadata
        [Parameter(ValueFromPipelineByPropertyName)]
        [System.Management.Automation.SwitchParameter] $Force,

        ## Download retry attempts
        [Parameter(ValueFromPipelineByPropertyName)]
        [System.Uint32] $Retry = 3,

        ## Include hidden (test and deprecated) packages.
        [Parameter(ValueFromPipelineByPropertyName)]
        [System.Management.Automation.SwitchParameter] $Hidden
    )
    process
    {
        Write-Verbose -Message ($localized.FindingPackage -f $packageName)
        $saveEvergreenPackageParams = @{
            Name            = $Name
            SubscriptionKey = $SubscriptionKey
            Path            = $cachedMetadataPath
            Force           = $Force
            Platform        = 'Exe'
            Retry           = $Retry
        }
        if ($PSBoundParameters.ContainsKey('CustomerCode'))
        {
            $saveEvergreenPackageParams['CustomerCode'] = $CustomerCode
        }
        if ($PSBoundParameters.ContainsKey('Credential'))
        {
            $saveEvergreenPackageParams['Credential'] = $Credential
        }
        if ($PSBoundParameters.ContainsKey('Hidden'))
        {
            $saveEvergreenPackageParams['Hidden'] = $Hidden.ToBool()
        }
        $exePackagePath = Save-EvergreenPackage @saveEvergreenPackageParams -ErrorAction SilentlyContinue

        if ($null -ne $exePackagePath)
        {
            Write-Output -InputObject ([PSCustomObject] @{
                Name     = $Name
                Path     = $exePackagePath
                TempPath = $null
            })
        }
        else
        {
            $saveEvergreenPackageParams['Platform'] = 'Zip'
            Write-Verbose -Message ($localized.TryingZipPackageBackup -f $Name)
            $zipPackagePath = Save-EvergreenPackage @saveEvergreenPackageParams -ErrorAction Stop

            ## Extract Zip to temporary folder
            $tempDirectory = New-TempDirectory
            Write-Verbose -Message ($localized.ExtractingArchive -f $tempDirectory)
            Expand-Archive -Path $zipPackagePath -DestinationPath $tempDirectory
            $exePackagePath = Get-Item -Path (Join-Path $tempDirectory -ChildPath 'Deploy-Application.exe')

            Write-Output -InputObject ([PSCustomObject] @{
                Name     = $Name
                Path     = $exePackagePath
                TempPath = $tempDirectory
            })
        }
    }
}

<#
    .SYNOPSIS
        Returns file version from the specified path.
 
    .NOTES
        Required to test 'Test-PackageDetectionRuleFile'
#>

function Get-FileVersion
{
    [CmdletBinding()]
    [OutputType([System.Version])]
    param
    (
        [Parameter(Mandatory, ValueFromPipeline)]
        [System.String] $Path
    )
    process
    {
        [System.Diagnostics.FileVersionInfo]::GetVersionInfo($path).FileVersionRaw -as [System.Version]
    }
}

<#
    .SYNOPSIS
        Returns unique package identifier name from package metadata, e.g. 'Virtual-Engine-ACE' or
        'Microsoft-Visual-Studio-Code-x64'.
#>

function Get-PackageMetadataPackageIdentifier
{
    [CmdletBinding()]
    param
    (
        [Parameter(Mandatory, ValueFromPipeline)]
        [Microsoft.PowerShell.DesiredStateConfiguration.ArgumentToConfigurationDataTransformationAttribute()]
        [System.Collections.Hashtable] $Metadata
    )
    process
    {
        $properties = $Metadata.Properties
        $packageName = $properties.Name

        if (-not (Test-PackageExcludeArchitecture -Metadata $Metadata) -and $properties.Contains('Architecture'))
        {
            $packageName = '{0} {1}' -f $packageName, $properties.Architecture.ToLower()
        }

        if (-not (Test-PackageExcludePublisher -Metadata $metadata))
        {
            $packageName = '{0} {1}' -f $properties.Publisher, $packageName
        }

        if (-not (Test-PackageExcludeStoreApp -Metadata $metadata))
        {
            $packageName = '{0} UWP' -f $packageName
        }

        return $packageName.Replace(' ', '-')
    }
}

<#
    .SYNOPSIS
        Downloads an Evergreen package's metadata.
#>

function Invoke-EvergreenMetadataDownload
{
    [CmdletBinding()]
    param
    (
        ## Evergreen package(s) metadata to cache
        [Parameter(Mandatory, ValueFromPipelineByPropertyName)]
        [System.String] $Name,

        ## Evergreen Api subscription key
        [Parameter(Mandatory, ValueFromPipelineByPropertyName)]
        [System.String] $SubscriptionKey,

        ## Enumerate customer package repository.
        [Parameter(ValueFromPipelineByPropertyName)]
        [System.String] $CustomerCode,

        ## Web proxy credential
        [Parameter(ValueFromPipelineByPropertyName)]
        [System.Management.Automation.PSCredential]
        [System.Management.Automation.CredentialAttribute()]
        $Credential,

        ## Force refresh of cached package metadata
        [Parameter(ValueFromPipelineByPropertyName)]
        [System.Management.Automation.SwitchParameter] $Force,

        ## Include hidden (test and deprecated) packages.
        [Parameter(ValueFromPipelineByPropertyName)]
        [System.Management.Automation.SwitchParameter] $Hidden
    )
    process
    {
        ## Download metadata file
        Write-Verbose -Message ($localized.DownloadingPackageMetadata -f $Name)
        $invokeEvergreenPackageApiParams = @{
            Name            = $Name
            SubscriptionKey = $SubscriptionKey
            Platform        = 'Exe'
            Force           = $Force
        }
        if ($PSBoundParameters.ContainsKey('CustomerCode'))
        {
            $invokeEvergreenPackageApiParams['CustomerCode'] = $CustomerCode
        }
        if ($PSBoundParameters.ContainsKey('Credential'))
        {
            $invokeEvergreenPackageApiParams['Credential'] = $Credential
        }
        if ($PSBoundParameters.ContainsKey('Hidden'))
        {
            $invokeEvergreenPackageApiParams['Hidden'] = $Hidden.ToBool()
        }
        $package = Invoke-EvergreenPackageApi @invokeEvergreenPackageApiParams

        ## Evergreen API returns an empty string if there are no matching packages
        if ($package -is [System.String])
        {
            ## We have no Exe package so try Zip as backup
            $invokeEvergreenPackageApiParams['Platform'] = 'Zip'
            Write-Verbose -Message ($localized.TryingZipPackageBackup -f $Name)
            $package = Invoke-EvergreenPackageApi @invokeEvergreenPackageApiParams
        }

        if ($package -is [System.String])
        {
            throw ($localized.CannotFindPackageError -f $Name)
        }

        $invokeEvergreenPackageDownloadParams = @{
            Path     = $cachedMetadataPath
            Package  = $package
            Metadata = $true
            Force    = $Force
        }
        Invoke-EvergreenPackageDownload @invokeEvergreenPackageDownloadParams
    }
}

<#
    .SYNOPSIS
        Calls the Virtual Engine Evergreen Api.
#>

function Invoke-EvergreenPackageApi
{
    [CmdletBinding()]
    param
    (
        [Parameter(ValueFromPipelineByPropertyName)]
        [System.String] $Name,

        [Parameter(Mandatory, ValueFromPipelineByPropertyName)]
        [System.String] $SubscriptionKey,

        ## Enumerate customer package repository.
        [Parameter(ValueFromPipelineByPropertyName)]
        [System.String] $CustomerCode,

        [Parameter(ValueFromPipelineByPropertyName)]
        [ValidateSet('Exe', 'Zip', 'Intune')]
        [System.String] $Platform,

        ## Return all available package versions
        [Parameter(ValueFromPipelineByPropertyName)]
        [System.Management.Automation.SwitchParameter] $All,

        ## Include hidden (test and deprecated) packages
        [Parameter(ValueFromPipelineByPropertyName)]
        [System.Management.Automation.SwitchParameter] $Hidden,

        ## Web proxy credential
        [Parameter(ValueFromPipelineByPropertyName)]
        [System.Management.Automation.PSCredential]
        [System.Management.Automation.CredentialAttribute()]
        $Credential,

        [Parameter(ValueFromRemainingArguments)]
        $RemainingArgs
    )
    process
    {
        $uri = 'https://virtualengine.azure-api.net/evergreen/packages'

        $filters = @()
        if ($PSBoundParameters.ContainsKey('Name'))
        {
            $filters += 'name={0}' -f $Name
        }
        if ($PSBoundParameters.ContainsKey('CustomerCode'))
        {
            $filters += 'customercode={0}' -f $CustomerCode
        }
        if ($PSBoundParameters.ContainsKey('Platform'))
        {
            $filters += 'platform={0}' -f $Platform
        }
        if ($All)
        {
            $filters += 'all={0}' -f $All.ToBool().ToString().ToLower()
        }
        if ($Hidden)
        {
            $filters += 'hidden={0}' -f $Hidden.ToBool().ToString().ToLower()
        }
        if ($filters.Count -gt 0)
        {
            $filterString = [System.String]::Join('&', $filters)
            $uri          = '{0}?{1}' -f $uri, $filterString
        }

        $invokeRestMethodParams = @{
            Uri     = $uri
            Headers = @{ 'Ocp-Apim-Subscription-Key' = $SubscriptionKey }
            Verbose = $false
        }
        if ($PSBoundParameters.ContainsKey('Credential'))
        {
            $invokeRestMethodParams['ProxyCredential'] = $Credential
        }
        Write-Verbose ($localized.InvokingEvergreenApi -f $uri)
        Invoke-RestMethod @invokeRestMethodParams
    }
}

<#
    .SYNOPSIS
        Downloads an Evergreen package if not already cached.
#>

function Invoke-EvergreenPackageDownload
{
    [CmdletBinding()]
    param
    (
        ## Evergreen package
        [Parameter(Mandatory, ValueFromPipeline, ValueFromPipelineByPropertyName)]
        [System.Management.Automation.PSObject] $Package,

        ## Destination folder path
        [Parameter(Mandatory, ValueFromPipeline)]
        [System.String] $Path,

        ## Web proxy credential
        [Parameter(ValueFromPipelineByPropertyName)]
        [System.Management.Automation.PSCredential]
        [System.Management.Automation.CredentialAttribute()]
        $Credential,

        ## Download from the MetadataUri property
        [Parameter(ValueFromPipelineByPropertyName)]
        [System.Management.Automation.SwitchParameter] $Metadata,

        ## Refresh cached package metadata
        [Parameter(ValueFromPipelineByPropertyName)]
        [System.Management.Automation.SwitchParameter] $Force,

        ## Download retry attempts
        [Parameter(ValueFromPipelineByPropertyName)]
        [System.Uint32] $Retry = 3,

        [Parameter(ValueFromRemainingArguments)]
        $RemainingArgs
    )
    process
    {
        $destinationFilename = ($Package.Uri -as [System.Uri]).AbsolutePath.Split('/')[-1]
        $uri = $Package.Uri
        if ($Metadata)
        {
            $destinationFilename = '{0}.psd1' -f $Package.Name
            $uri                 = $Package.MetadataUri
        }
        $destinationPath = Join-Path -Path $Path -ChildPath $destinationFilename

        if ($Force -or (-not (Test-Path -Path $destinationPath)))
        {
            $invokeWebClientDownloadParams = @{
                DestinationPath = $destinationPath
                Uri             = $uri
                Retry           = $Retry
            }
            if ($PSBoundParameters.ContainsKey('Credential'))
            {
                $invokeWebClientDownloadParams['Credential'] = $Credential
            }
            Write-Verbose -Message ($localized.StartingDownload -f $destinationPath)
            Invoke-WebClientDownload @invokeWebClientDownloadParams
        }
        else
        {
            Write-Verbose -Message ($localized.ReturningCachedDownload -f $destinationPath)
            Get-Item -Path $destinationPath
        }
    }
}

function Invoke-WebClientDownload
{
<#
    .SYNOPSIS
        Downloads a (web) resource using System.Net.WebClient.
 
    .NOTES
        This solves issues when downloading resources using BITS under alternative credentials.
 
    .LINK
        https://github.com/VirtualEngine/Lability/blob/dev/Src/Private/Invoke-WebClientDownload.ps1
#>

    [CmdletBinding()]
    [OutputType([System.IO.FileInfo])]
    param
    (
        [Parameter(Mandatory, ValueFromPipeline)]
        [System.String] $DestinationPath,

        [Parameter(Mandatory, ValueFromPipelineByPropertyName)]
        [System.String] $Uri,

        [Parameter(ValueFromPipelineByPropertyName)]
        [System.UInt32] $BufferSize = 64KB,

        ## Download retry attempts
        [Parameter(ValueFromPipelineByPropertyName)]
        [System.Uint32] $Retry = 3,

        [Parameter(ValueFromPipelineByPropertyName)] [AllowNull()]
        [System.Management.Automation.PSCredential]
        [System.Management.Automation.CredentialAttribute()]
        $Credential
    )
    process
    {
        for ($attempt = 1; $attempt -le $Retry; $attempt++)
        {
            try
            {
                [System.Net.WebClient] $webClient = New-Object -TypeName 'System.Net.WebClient'
                $webClient.Headers.Add('user-agent', $labDefaults.ModuleName)
                $webClient.Proxy = [System.Net.WebRequest]::GetSystemWebProxy()

                if (-not $webClient.Proxy.IsBypassed($Uri))
                {
                    $proxyInfo = $webClient.Proxy.GetProxy($Uri)
                    if ($null -ne $proxyInfo.AbsoluteUri)
                    {
                        Write-Verbose -Message ($localized.UsingProxyServer -f $proxyInfo.AbsoluteUri)
                    }
                }

                if ($Credential)
                {
                    $webClient.Credentials = $Credential
                    $webClient.Proxy.Credentials = $Credential
                }
                else
                {
                    $webClient.UseDefaultCredentials = $true
                    $webClient.Proxy.Credentials = [System.Net.CredentialCache]::DefaultCredentials
                }

                [System.IO.Stream] $inputStream = $webClient.OpenRead($Uri)
                [System.UInt64] $contentLength = $webClient.ResponseHeaders['Content-Length']
                $path = $ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($DestinationPath)
                [System.IO.Stream] $outputStream = [System.IO.File]::Create($path)
                [System.Byte[]] $buffer = New-Object -TypeName System.Byte[] -ArgumentList $BufferSize
                [System.UInt64] $bytesRead = 0
                [System.UInt64] $totalBytes = 0
                $writeProgessActivity = $localized.DownloadingActivity -f $Uri

                do
                {
                    $iteration ++
                    $bytesRead = $inputStream.Read($buffer, 0, $buffer.Length)
                    $totalBytes += $bytesRead
                    $outputStream.Write($buffer, 0, $bytesRead)
                    ## Avoid divide by zero
                    if ($contentLength -gt 0)
                    {
                        if ($iteration % 30 -eq 0)
                        {
                            [System.Byte] $percentComplete = ($totalBytes / $contentLength) * 100
                            $writeProgressParams = @{
                                Activity = $writeProgessActivity
                                PercentComplete = $percentComplete
                                Status = $localized.DownloadStatus -f $totalBytes, $contentLength, $percentComplete
                            }
                            Write-Progress @writeProgressParams
                        }
                    }
                }
                while ($bytesRead -ne 0)

                $outputStream.Close()
                return (Get-Item -Path $path)
            }
            catch
            {
                $sleepSeconds = [System.Math]::Pow(5, $attempt)
                Write-Warning -Message ($localized.WebResourceDownloadFailedWarning -f $Uri, $sleepSeconds)
                Start-Sleep -Seconds $sleepSeconds
            }
            finally
            {
                if ($null -ne $writeProgressActivity)
                {
                    Write-Progress -Activity $writeProgessActivity -Completed
                }
                if ($null -ne $outputStream)
                {
                    $outputStream.Close()
                }
                if ($null -ne $inputStream)
                {
                    $inputStream.Close()
                }
                if ($null -ne $webClient)
                {
                    $webClient.Dispose()
                }
            }
        }

        ## if we get here we have failed all attempts
        throw ($localized.WebResourceDownloadFailedError -f $Uri)
    }
}

<#
    .SYNOPSIS
        Creates a new temporary directory.
#>

function New-TempDirectory
{
    [Cmdletbinding()]
    [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseShouldProcessForStateChangingFunctions', '')]
    param ( )
    begin
    {
        if ($PSBoundParameters.ContainsKey('Debug')) { $DebugPreference = 'Continue' }
    }
    process
    {
        do
        {
            $path = [System.IO.Path]::Combine([System.IO.Path]::GetTempPath(), [System.IO.Path]::GetRandomFileName())
        }
        while ([System.IO.Directory]::Exists($path))
        Write-Debug ("Generated temp directory path '{0}'" -f $path)
        return [System.IO.Directory]::CreateDirectory($path).FullName
    }
}

<#
    .SYNOPSIS
        Writes byte array ([Byte[]]) to a file.
#>

function Set-FileBytes
{
    [CmdletBinding()]
    [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseSingularNouns','')]
    [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseShouldProcessForStateChangingFunctions', '')]
    [OutputType()]
    param
    (
        [Parameter(Mandatory, ValueFromPipeline)]
        [System.Byte[]] $InputObject,

        [Parameter(Mandatory, ValueFromPipelineByPropertyName)]
        [System.String] $Path
    )
    process
    {
        if ($PSVersionTable['PSEdition'] -eq 'Core')
        {
            Set-Content -Path $Path -Value $InputObject -AsByteStream
        }
        else
        {
            Set-Content -Path $Path -Value $InputObject -Encoding Byte
        }
    }
}

<#
    .SYNOPSIS
        Starts and waits for a process to exit.
#>

function Start-WaitProcess
{
    [CmdletBinding(SupportsShouldProcess)]
    [OutputType([System.Int32])]
    param
    (
        # Path to process to start.
        [Parameter(Mandatory)]
        [ValidateNotNullOrEmpty()]
        [System.String] $FilePath,

        # Arguments (if any) to apply to the process.
        [Parameter()]
        [AllowNull()]
        [System.String[]] $ArgumentList,

        # Credential to start the process as.
        [Parameter()]
        [AllowNull()]
        [System.Management.Automation.PSCredential]
        [System.Management.Automation.CredentialAttribute()]
        $Credential,

        # Working directory
        [Parameter()]
        [ValidateNotNullOrEmpty()]
        [System.String] $WorkingDirectory = (Split-Path -Path $FilePath -Parent)
    )
    process
    {
        $startProcessParams = @{
            FilePath         = $FilePath
            WorkingDirectory = $WorkingDirectory
            NoNewWindow      = $true
            PassThru         = $true
        }
        $displayParams = '<None>'
        if ($ArgumentList)
        {
            $displayParams = [System.String]::Join(' ', $ArgumentList)
            $startProcessParams['ArgumentList'] = $ArgumentList
        }
        Write-Verbose ($localized.StartingProcess -f $FilePath, $displayParams)
        if ($Credential)
        {
            Write-Verbose ($localized.StartingProcessAs -f $Credential.UserName);
            $startProcessParams['Credential'] = $Credential
        }

        $exitCode = 0
        if ($PSCmdlet.ShouldProcess($FilePath, 'Start Process'))
        {
            $process = Start-Process @startProcessParams -ErrorAction Stop
            Write-Verbose ($localized.ProcessLaunched -f $process.Id)
            Wait-Process -InputObject $process
            $exitCode = [System.Convert]::ToInt32($process.ExitCode)
            Write-Verbose ($localized.ProcessExited -f $process.Id, $exitCode)
        }
        return $exitCode
    }
}

<#
    .SYNOPSIS
        Tests whether a package is installed using the package metadata Intune detection rules.
#>

function Test-PackageDetectionRule
{
    [CmdletBinding()]
    param
    (
        [Parameter(Mandatory, ValueFromPipeline)]
        [System.Collections.Hashtable]
        [Microsoft.PowerShell.DesiredStateConfiguration.ArgumentToConfigurationDataTransformationAttribute()]
        $Metadata,

        ## Package name used for message output.
        [Parameter(Mandatory, ValueFromPipelineByPropertyName)]
        [System.String] $PackageName,

        [Parameter(ValueFromPipelineByPropertyName)]
        [System.String] $Path = $PWD,

        ## Does not fail application test if no detection rules are defined.
        [Parameter(ValueFromPipelineByPropertyName)]
        [System.Management.Automation.SwitchParameter] $IgnoreEmptyDetectionRule
    )
    process
    {
        if (($Metadata.Contains('Intune')) -and ($Metadata.Intune.Contains('DetectionRules')))
        {
            foreach ($detectionRule in $Metadata.Intune.DetectionRules)
            {
                switch ($detectionRule.Type)
                {
                    File
                    {
                        if (-not (Test-PackageDetectionRuleFile -DetectionRule $detectionRule))
                        {

                            return $false
                        }
                    }
                    Registry
                    {
                        if (-not (Test-PackageDetectionRuleRegistry -DetectionRule $detectionRule))
                        {
                            return $false
                        }
                    }
                    Msi
                    {
                        if (-not (Test-PackageDetectionRuleMsi -DetectionRule $detectionRule))
                        {
                            return $false
                        }
                    }
                    PowerShell
                    {
                        if (-not (Test-PackageDetectionRuleScript -DetectionRule $detectionRule -Path $Path))
                        {
                            return $false
                        }
                    }
                    Default
                    {
                        Write-Warning -Message ($localized.UnsupportedPackageDectectionRuleWarning -f $detectionRule.Type)
                    }
                }
            }
        }
        else
        {
            if ($IgnoreEmptyDetectionRule)
            {
                ## Unable to determine installation state so return nothing
                Write-Verbose -Message ($localized.NoPackageDectectionRuleDefinedWarning -f $PackageName)
                return $null
            }
            else
            {
                throw ($localized.CannotFindPackageDetectionRuleError -f $PackageName)
            }
        }

        return $true
    }
}

<#
    .SYNOPSIS
        Tests a package metadata Intune file detection rule.
#>

function Test-PackageDetectionRuleFile
{
    [CmdletBinding()]
    param
    (
        [Parameter(Mandatory, ValueFromPipeline)]
        [System.Collections.Hashtable] $DetectionRule
    )
    process
    {
        $path = $DetectionRule.Path
        if (([System.Environment]::Is64BitOperatingSystem) -and
            ($DetectionRule.ContainsKey('Check32BitOn64System')) -and
            ($DetectionRule.Check32BitOn64System -eq $true))
        {
            $path = $path -replace '%ProgramFiles%', '%ProgramFiles(x86)%'
        }
        $path = '{0}\{1}' -f [System.Environment]::ExpandEnvironmentVariables($path).TrimEnd('\'), $DetectionRule.FileOrFolderName

        switch ($DetectionRule.FileDetectionType)
        {
            NotConfigured
            {
                Write-Verbose -Message ($localized.EnumeratedFilePathNotConfigured -f $path)
            }
            Exists
            {
                Write-Verbose -Message ($localized.EnumeratingPathDoesExist -f $path)
                if (Test-Path -Path $path)
                {
                    Write-Verbose -Message ($localized.EnumeratedPathDoesExist -f $path)
                    return $true
                }
                else
                {
                    Write-Verbose -Message ($localized.EnumeratedPathDoesNotExist -f $path)
                    return $false
                }
            }
            DoesNotExist
            {
                Write-Verbose -Message ($localized.EnumeratingPathDoesNotExist -f $path)
                if (-not (Test-Path -Path $path))
                {
                    Write-Verbose -Message ($localized.EnumeratedPathDoesNotExist -f $path)
                    return $true
                }
                else
                {
                    Write-Verbose -Message ($localized.EnumeratedPathDoesExist -f $path)
                    return $false
                }
            }
            Version
            {
                if (-not (Test-Path -Path $path -PathType Leaf))
                {
                    Write-Verbose -Message ($localized.EnumeratingPathDoesExist -f $path)
                    Write-Verbose -Message ($localized.EnumeratedPathDoesNotExist -f $path)
                    return $false
                }
                $version = $DetectionRule.FileDetectionValue -as [System.Version]
                $currentVersion = Get-FileVersion -Path $path
                $detectionOperator = $DetectionRule.FileDetectionOperator
                Write-Verbose -Message ($localized.EnumeratingFileVersion -f $path, $detectionOperator, $version)
                switch ($DetectionRule.FileDetectionOperator)
                {
                    NotConfigurated { }
                    Equal
                    {
                        if ($currentVersion -eq $Version)
                        {
                            Write-Verbose -Message ($localized.EnumeratedFileVersionIs -f $currentVersion, $detectionOperator, $version)
                            return $true
                        }
                        else
                        {
                            Write-Verbose -Message ($localized.EnumeratedFileVersionIsNot -f $currentVersion, $detectionOperator, $version)
                            return $false

                        }
                    }
                    NotEqual
                    {
                        if ($currentVersion -ne $version)
                        {
                            Write-Verbose -Message ($localized.EnumeratedFileVersionIs -f $currentVersion, $detectionOperator, $version)
                            return $true
                        }
                        else
                        {
                            Write-Verbose -Message ($localized.EnumeratedFileVersionIsNot -f $currentVersion, $detectionOperator, $version)
                            return $false
                        }
                    }
                    GreaterThan
                    {
                        if ($currentVersion -gt $version)
                        {
                            Write-Verbose -Message ($localized.EnumeratedFileVersionIs -f $currentVersion, $detectionOperator, $version)
                            return $true
                        }
                        else
                        {
                            Write-Verbose -Message ($localized.EnumeratedFileVersionIsNot -f $currentVersion, $detectionOperator, $version)
                            return $false
                        }
                    }
                    GreaterThanOrEqual
                    {
                        if ($currentVersion -ge $version)
                        {
                            Write-Verbose -Message ($localized.EnumeratedFileVersionIs -f $currentVersion, $detectionOperator, $version)
                            return $true
                        }
                        else
                        {
                            Write-Verbose -Message ($localized.EnumeratedFileVersionIsNot -f $currentVersion, $detectionOperator, $version)
                            return $false
                        }
                    }
                    LessThan
                    {
                        if ($currentVersion -lt $version)
                        {
                            Write-Verbose -Message ($localized.EnumeratedFileVersionIs -f $currentVersion, $detectionOperator, $version)
                            return $true
                        }
                        else
                        {
                            Write-Verbose -Message ($localized.EnumeratedFileVersionIsNot -f $currentVersion, $detectionOperator, $version)
                            return $false
                        }
                    }
                    LessThanOrEqual
                    {
                        if ($currentVersion -le $version)
                        {
                            Write-Verbose -Message ($localized.EnumeratedFileVersionIs -f $currentVersion, $detectionOperator, $version)
                            return $true
                        }
                        else
                        {
                            Write-Verbose -Message ($localized.EnumeratedFileVersionIsNot -f $currentVersion, $detectionOperator, $version)
                            return $false
                        }
                    }
                }
            }
            Default
            {
                Write-Warning -Message ($localized.UnsupportedFileDectectionRuleWarning -f $DetectionRule.FileDetectionType)
            }
        }
        return $true
    }
}

<#
    .SYNOPSIS
        Tests a package metadata Intune MSI detection rule.
#>

function Test-PackageDetectionRuleMsi
{
    [CmdletBinding()]
    param
    (
        [Parameter(Mandatory, ValueFromPipeline)]
        [System.Collections.Hashtable] $DetectionRule
    )
    process
    {
        $paths = 'HKLM:\Software\Microsoft\windows\CurrentVersion\Uninstall\*', 'HKLM:\Software\WOW6432Node\Microsoft\windows\CurrentVersion\Uninstall\*'
        Write-Verbose -Message ($localized.EnumeratingMSIProductCodeDoesExist -f $DetectionRule.MsiProductCode)
        $package =  Get-ItemProperty -Path $paths -ErrorAction SilentlyContinue |
                        Where-Object { $_.PSChildName -eq $DetectionRule.MsiProductCode }
        if ($null -ne $package)
        {
            Write-Verbose -Message ($localized.EnumeratedMSIProductCodeDoesExist -f $DetectionRule.MsiProductCode)
            return $true
        }
        else
        {
            Write-Verbose -Message ($localized.EnumeratedMSIProductCodeDoesNotExist -f $DetectionRule.MsiProductCode)
            return $false
        }
    }
}

<#
    .SYNOPSIS
        Tests a package metadata Intune registry detection rule.
#>

function Test-PackageDetectionRuleRegistry
{
    [CmdletBinding()]
    param
    (
        [Parameter(Mandatory, ValueFromPipeline)]
        [System.Collections.Hashtable] $DetectionRule
    )
    process
    {
        $path = $DetectionRule.RegistryKeyPath -replace 'HKEY_LOCAL_MACHINE', 'HKLM:' -replace 'HKEY_CURRENT_USER', 'HKCU:' -replace 'HKEY_CLASSES_ROOT', 'HKCR:'
        if (([System.Environment]::Is64BitOperatingSystem) -and
            ($DetectionRule.ContainsKey('Check32BitOn64System')) -and
            ($DetectionRule.Check32BitOn64System -eq $true))
        {
            $path = $path -replace '\\SOFTWARE\\', '\\SOFTWARE\\WOW6432Node\\'
        }

        if (($DetectionRule.ContainsKey('RegistryValue')) -and
            (-not [System.String]::IsNullOrEmpty($DetectionRule.RegistryValue)))
        {
            $registryValue = Get-ItemProperty -Path $path -Name $DetectionRule.RegistryValue -ErrorAction SilentlyContinue |
                                Select-Object -ExpandProperty $DetectionRule.RegistryValue -ErrorAction SilentlyContinue
        }

        switch ($DetectionRule.RegistryDetectionType)
        {
            NotConfigured
            {
                Write-Verbose -Message ($localized.EnumeratedRegistryPathNotConfigured -f $path)
            }
            Exists
            {
                if (($DetectionRule.ContainsKey('RegistryValue')) -and
                    (-not [System.String]::IsNullOrEmpty($DetectionRule.RegistryValue)))
                {
                    Write-Verbose -Message ($localized.EnumeratingPathItemDoesExist -f $path, $DetectionRule.RegistryValue)
                    if ($null -ne $registryValue)
                    {
                        Write-Verbose -Message ($localized.EnumeratedPathItemDoesExist -f $path, $DetectionRule.RegistryValue)
                        return $true
                    }
                    else
                    {
                        Write-Verbose -Message ($localized.EnumeratedPathItemDoesNotExist -f $path, $DetectionRule.RegistryValue)
                        return $false
                    }
                }
                else
                {
                    Write-Verbose -Message ($localized.EnumeratingPathExists -f $path)
                    if (Test-Path -Path $path)
                    {
                        Write-Verbose -Message ($localized.EnumeratedPathExists -f $path, $DetectionRule.RegistryValue)
                        return $true
                    }
                    else
                    {
                        Write-Verbose -Message ($localized.EnumeratedPathDoesNotExist -f $path, $DetectionRule.RegistryValue)
                        return $false
                    }
                }
            }
            DoesNotExist
            {
                if (($DetectionRule.ContainsKey('RegistryValue')) -and
                    (-not [System.String]::IsNullOrEmpty($DetectionRule.RegistryValue)))
                {
                    Write-Verbose -Message ($localized.EnumeratingPathItemDoesNotExist -f $path, $DetectionRule.RegistryValue)
                    if ($null -eq $registryValue)
                    {
                        Write-Verbose -Message ($localized.EnumeratedPathItemDoesNotExist -f $path, $DetectionRule.RegistryValue)
                        return $true
                    }
                    else
                    {
                        Write-Verbose -Message ($localized.EnumeratedPathItemDoesExist -f $path, $DetectionRule.RegistryValue)
                        return $false
                    }
                }
                else
                {
                    Write-Verbose -Message ($localized.EnumeratingPathDoesNotExist -f $path)
                    if (-not (Test-Path -Path $path))
                    {
                        Write-Verbose -Message ($localized.EnumeratedPathDoesNotExist -f $path)
                        return $true
                    }
                    else
                    {
                        Write-Verbose -Message ($localized.EnumeratedPathDoesExist -f $path)
                        return $false
                    }
                }
            }
            Default
            {
                if ($null -eq $registryValue)
                {
                    Write-Verbose -Message ($localized.EnumeratedRegistryPathValueDoesNotExist -f $path, $DetectionRule.RegistryValue)
                    return $false
                }

                Write-Verbose -Message ($localized.EnumeratingRegistryPathValue -f $path, $DetectionRule.RegistryValue)

                switch ($DetectionRule.RegistryDetectionType)
                {
                    Version
                    {
                        $value = $DetectionRule.RegistryDetectionValue -as [System.Version]
                        $currentValue = $registryValue -as [System.Version]
                    }
                    Integer
                    {
                        $value = $DetectionRule.RegistryDetectionValue -as [System.Int32]
                        $currentValue = $registryValue -as [System.Int32]
                    }
                    String
                    {
                        $value = $DetectionRule.RegistryDetectionValue -as [System.String]
                        $currentValue = $registryValue -as [System.String]
                    }
                }

                $registryDetectionOperator = $DetectionRule.RegistryDetectionOperator
                Write-Verbose -Message ($localized.EnumeratingRegistryValue -f $currentValue, $registryDetectionOperator, $value)
                switch ($DetectionRule.RegistryDetectionOperator)
                {
                    NotConfigurated
                    {
                        Write-Verbose -Message ($localized.EnumeratedRegistryPathNotConfigured -f $path)
                    }
                    Equal
                    {
                        if ($currentValue -eq $value)
                        {
                            Write-Verbose -Message ($localized.EnumeratedRegistryValueIs -f $currentValue, $registryDetectionOperator, $value)
                            return $true
                        }
                        else
                        {
                            Write-Verbose -Message ($localized.EnumeratedRegistryValueIsNot -f $currentValue, $registryDetectionOperator, $value)
                            return $false
                        }
                    }
                    NotEqual
                    {
                        if ($currentValue -ne $value)
                        {
                            Write-Verbose -Message ($localized.EnumeratedRegistryValueIsNot -f $currentValue, $registryDetectionOperator, $value)
                            return $true
                        }
                        else
                        {
                            Write-Verbose -Message ($localized.EnumeratedRegistryValueIs -f $currentValue, $registryDetectionOperator, $value)
                            return $false
                        }
                    }
                    GreaterThan
                    {
                        if ($currentValue -gt $value)
                        {
                            Write-Verbose -Message ($localized.EnumeratedRegistryValueIs -f $currentValue, $registryDetectionOperator, $value)
                            return $true
                        }
                        else
                        {
                            Write-Verbose -Message ($localized.EnumeratedRegistryValueIsNot -f $currentValue, $registryDetectionOperator, $value)
                            return $false
                        }
                    }
                    GreaterThanOrEqual
                    {
                        if ($currentValue -ge $value)
                        {
                            Write-Verbose -Message ($localized.EnumeratedRegistryValueIs -f $currentValue, $registryDetectionOperator, $value)
                            return $true
                        }
                        else
                        {
                            Write-Verbose -Message ($localized.EnumeratedRegistryValueIsNot -f $currentValue, $registryDetectionOperator, $value)
                            return $false
                        }
                    }
                    LessThan
                    {
                        if ($currentValue -lt $value)
                        {
                            Write-Verbose -Message ($localized.EnumeratedRegistryValueIs -f $currentValue, $registryDetectionOperator, $value)
                            return $true
                        }
                        else
                        {
                            Write-Verbose -Message ($localized.EnumeratedRegistryValueIsNot -f $currentValue, $registryDetectionOperator, $value)
                            return $false
                        }
                    }
                    LessThanOrEqual
                    {
                        if ($currentValue -le $value)
                        {
                            Write-Verbose -Message ($localized.EnumeratedRegistryValueIs -f $currentValue, $registryDetectionOperator, $value)
                            return $true
                        }
                        else
                        {
                            Write-Verbose -Message ($localized.EnumeratedRegistryValueIsNot -f $currentValue, $registryDetectionOperator, $value)
                            return $false
                        }
                    }
                }
            }
        }
        return $true
    }
}

<#
    .SYNOPSIS
        Tests a package metadata Intune detection/requirement script rule.
#>

function Test-PackageDetectionRuleScript
{
    [CmdletBinding()]
    param
    (
        [Parameter(Mandatory, ValueFromPipeline)]
        [System.Collections.Hashtable] $DetectionRule,

        [Parameter(ValueFromPipelineByPropertyName)]
        [System.String] $Path = $PWD
    )
    process
    {
        $isEncodedCommand = $false

        try
        {
            $encodedBytes = [System.Convert]::FromBase64String($DetectionRule.ScriptFile)
            $isEncodedCommand = $true
            Write-Verbose -Message $localized.EncodedPowerShellScriptDetected
            $scriptPath = [System.IO.Path]::GetTempFileName().Replace('.tmp','.ps1')
            Write-Verbose -Message ($localized.CreatingTemporaryFile -f $scriptPath)
            Set-FileBytes -Path $scriptPath -InputObject $encodedBytes
        }
        catch
        {
            $scriptPath = Join-Path -Path $Path -ChildPath $DetectionRule.ScriptFile
            Write-Verbose -Message ($localized.EnumeratingPathDoesExist -f $scriptPath)

        }

        $isCompliant = $true

        if (Test-Path -Path $scriptPath -PathType Leaf)
        {
            Write-Verbose -Message ($localized.EnumeratedPathDoesExist -f $scriptPath)
            $scriptPathItem = Get-Item -Path $scriptPath

            Write-Verbose -Message ($localized.EnumeratingPowerShellScript -f $scriptPathItem.Name)

            $powerShellPath = "$env:WINDIR\System32\WindowsPowerShell\v1.0\powershell.exe"
            if ($DetectionRule.RunAs32Bit -eq $true)
            {
                ## Assumption here is that we are running a x64 PowerShell process to launch a x86 process
                $powerShellPath = "$env:WINDIR\SysWOW64\WindowsPowerShell\v1.0\powershell.exe"

            }
            Write-Verbose -Message ($localized.LaunchingPowerShellProcess -f $powerShellPath)
            $scriptOutput = & $powerShellPath -NoProfile -NonInteractive -NoLogo -File "$scriptPath"

            if ($LASTEXITCODE -eq 0)
            {
                Write-Verbose -Message ($localized.EnumeratedScriptExitCodeIsValid -f $LASTEXITCODE)
            }
            else
            {
                $isCompliant = $false
                Write-Verbose -Message ($localized.EnumeratedScriptExitCodeIsNotValid -f $LASTEXITCODE)

            }

            if ($null -ne $scriptOutput)
            {
                Write-Verbose -Message ($localized.EnumeratedScriptOutputIsValid)
                $scriptOutput -split "(`r)?`n" | ForEach-Object {
                    Write-Verbose -Message " $_"
                }
            }
            else
            {
                $isCompliant = $false
                Write-Verbose -Message ($localized.EnumeratedScriptOutputIsNotValid)
            }

            if ($DetectionRule.EnforceSignatureCheck -eq $true)
            {
                Write-Verbose -Message ($localized.EnumeratingPowerShellScriptSignature -f $scriptPathItem.Name)
                $authenticodeSignature = Get-AuthenticodeSignature -FilePath $scriptPath -ErrorAction SilentlyContinue
                if (($null -eq $authenticodeSignature) -or ($authenticodeSignature.Status -ne 'Signed'))
                {
                    Write-Verbose -Message ($localized.InvalidDetectionScriptSignatureWarning -f $scriptPathItem.Name)
                    $isCompliant = $false
                }
                elseif ($authenticodeSignature.Status -eq 'Signed')
                {
                    Write-Verbose -Message ($localized.DetectionScriptSignatureIsValid -f $scriptPathItem.Name)
                }
            }
        }
        else
        {
            Write-Verbose -Message ($localized.EnumeratedPathDoesNotExist -f $scriptPath)
            $isCompliant = $false
        }

        if($isEncodedCommand)
        {
            Write-Verbose -Message ($localized.RemovingTemporaryFile -f $scriptPath)
            Remove-Item -Path $scriptPath -Force
        }

        return $isCompliant
    }
}

<#
    .SYNOPSIS
        Tests metadata whether architecture should be excluded from package name.
#>

function Test-PackageExcludeArchitecture
{
    [CmdletBinding()]
    param
    (
        [Parameter(Mandatory, ValueFromPipeline)]
        [Microsoft.PowerShell.DesiredStateConfiguration.ArgumentToConfigurationDataTransformationAttribute()]
        [System.Collections.Hashtable] $Metadata
    )
    process
    {
        if ((($Metadata.Properties.ContainsKey('ExcludeArchitecture')) -and ($Metadata.Properties.ExcludeArchitecture -eq $true)))
        {
            return $true
        }
        else
        {
            return $false
        }
    }
}

<#
    .SYNOPSIS
        Tests metadata whether publisher name should be excluded.
#>

function Test-PackageExcludePublisher
{
    [CmdletBinding()]
    param
    (
        [Parameter(Mandatory, ValueFromPipeline)]
        [Microsoft.PowerShell.DesiredStateConfiguration.ArgumentToConfigurationDataTransformationAttribute()]
        [System.Collections.Hashtable] $Metadata
    )
    process
    {
        if ((($Metadata.Properties.ContainsKey('ExcludePublisher')) -and ($Metadata.Properties.ExcludePublisher -eq $true)) -or
            (($Metadata.Properties.ContainsKey('ExcludePublisherName')) -and ($Metadata.Properties.ExcludePublisherName -eq $true)))
        {
            return $true
        }
        else
        {
            return $false
        }
    }
}

<#
    .SYNOPSIS
        Tests metadata whether the UWP moniker should be excluded.
#>

function Test-PackageExcludeStoreApp
{
    [CmdletBinding()]
    param
    (
        [Parameter(Mandatory, ValueFromPipeline)]
        [Microsoft.PowerShell.DesiredStateConfiguration.ArgumentToConfigurationDataTransformationAttribute()]
        [System.Collections.Hashtable] $Metadata
    )
    process
    {
        if ($Metadata.Properties.ContainsKey('IsStoreApp') -and ($Metadata.Properties.IsStoreApp -eq $true))
        {
            if ($Metadata.Properties.ContainsKey('ExcludeStoreApp') -and ($Metadata.Properties.ExcludeStoreApp -eq $true))
            {
                return $true
            }
            else
            {
                return $false
            }
        }
        else
        {
            ## Not a store app so always exclude
            return $true
        }
    }
}


# SIG # Begin signature block
# MIIuwQYJKoZIhvcNAQcCoIIusjCCLq4CAQExDzANBglghkgBZQMEAgEFADB5Bgor
# BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG
# KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCDHGWuJdI5W022E
# 4JnneTeLxjktu4B1nWl8zcwu7rRy+6CCE6QwggWQMIIDeKADAgECAhAFmxtXno4h
# MuI5B72nd3VcMA0GCSqGSIb3DQEBDAUAMGIxCzAJBgNVBAYTAlVTMRUwEwYDVQQK
# EwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xITAfBgNV
# BAMTGERpZ2lDZXJ0IFRydXN0ZWQgUm9vdCBHNDAeFw0xMzA4MDExMjAwMDBaFw0z
# ODAxMTUxMjAwMDBaMGIxCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJ
# bmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xITAfBgNVBAMTGERpZ2lDZXJ0
# IFRydXN0ZWQgUm9vdCBHNDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIB
# AL/mkHNo3rvkXUo8MCIwaTPswqclLskhPfKK2FnC4SmnPVirdprNrnsbhA3EMB/z
# G6Q4FutWxpdtHauyefLKEdLkX9YFPFIPUh/GnhWlfr6fqVcWWVVyr2iTcMKyunWZ
# anMylNEQRBAu34LzB4TmdDttceItDBvuINXJIB1jKS3O7F5OyJP4IWGbNOsFxl7s
# Wxq868nPzaw0QF+xembud8hIqGZXV59UWI4MK7dPpzDZVu7Ke13jrclPXuU15zHL
# 2pNe3I6PgNq2kZhAkHnDeMe2scS1ahg4AxCN2NQ3pC4FfYj1gj4QkXCrVYJBMtfb
# BHMqbpEBfCFM1LyuGwN1XXhm2ToxRJozQL8I11pJpMLmqaBn3aQnvKFPObURWBf3
# JFxGj2T3wWmIdph2PVldQnaHiZdpekjw4KISG2aadMreSx7nDmOu5tTvkpI6nj3c
# AORFJYm2mkQZK37AlLTSYW3rM9nF30sEAMx9HJXDj/chsrIRt7t/8tWMcCxBYKqx
# YxhElRp2Yn72gLD76GSmM9GJB+G9t+ZDpBi4pncB4Q+UDCEdslQpJYls5Q5SUUd0
# viastkF13nqsX40/ybzTQRESW+UQUOsxxcpyFiIJ33xMdT9j7CFfxCBRa2+xq4aL
# T8LWRV+dIPyhHsXAj6KxfgommfXkaS+YHS312amyHeUbAgMBAAGjQjBAMA8GA1Ud
# EwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgGGMB0GA1UdDgQWBBTs1+OC0nFdZEzf
# Lmc/57qYrhwPTzANBgkqhkiG9w0BAQwFAAOCAgEAu2HZfalsvhfEkRvDoaIAjeNk
# aA9Wz3eucPn9mkqZucl4XAwMX+TmFClWCzZJXURj4K2clhhmGyMNPXnpbWvWVPjS
# PMFDQK4dUPVS/JA7u5iZaWvHwaeoaKQn3J35J64whbn2Z006Po9ZOSJTROvIXQPK
# 7VB6fWIhCoDIc2bRoAVgX+iltKevqPdtNZx8WorWojiZ83iL9E3SIAveBO6Mm0eB
# cg3AFDLvMFkuruBx8lbkapdvklBtlo1oepqyNhR6BvIkuQkRUNcIsbiJeoQjYUIp
# 5aPNoiBB19GcZNnqJqGLFNdMGbJQQXE9P01wI4YMStyB0swylIQNCAmXHE/A7msg
# dDDS4Dk0EIUhFQEI6FUy3nFJ2SgXUE3mvk3RdazQyvtBuEOlqtPDBURPLDab4vri
# RbgjU2wGb2dVf0a1TD9uKFp5JtKkqGKX0h7i7UqLvBv9R0oN32dmfrJbQdA75PQ7
# 9ARj6e/CVABRoIoqyc54zNXqhwQYs86vSYiv85KZtrPmYQ/ShQDnUBrkG5WdGaG5
# nLGbsQAe79APT0JsyQq87kP6OnGlyE0mpTX9iV28hWIdMtKgK1TtmlfB2/oQzxm3
# i0objwG2J5VT6LaJbVu8aNQj6ItRolb58KaAoNYes7wPD1N1KarqE3fk3oyBIa0H
# EEcRrYc9B9F1vM/zZn4wggawMIIEmKADAgECAhAIrUCyYNKcTJ9ezam9k67ZMA0G
# CSqGSIb3DQEBDAUAMGIxCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJ
# bmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xITAfBgNVBAMTGERpZ2lDZXJ0
# IFRydXN0ZWQgUm9vdCBHNDAeFw0yMTA0MjkwMDAwMDBaFw0zNjA0MjgyMzU5NTla
# MGkxCzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5EaWdpQ2VydCwgSW5jLjFBMD8GA1UE
# AxM4RGlnaUNlcnQgVHJ1c3RlZCBHNCBDb2RlIFNpZ25pbmcgUlNBNDA5NiBTSEEz
# ODQgMjAyMSBDQTEwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDVtC9C
# 0CiteLdd1TlZG7GIQvUzjOs9gZdwxbvEhSYwn6SOaNhc9es0JAfhS0/TeEP0F9ce
# 2vnS1WcaUk8OoVf8iJnBkcyBAz5NcCRks43iCH00fUyAVxJrQ5qZ8sU7H/Lvy0da
# E6ZMswEgJfMQ04uy+wjwiuCdCcBlp/qYgEk1hz1RGeiQIXhFLqGfLOEYwhrMxe6T
# SXBCMo/7xuoc82VokaJNTIIRSFJo3hC9FFdd6BgTZcV/sk+FLEikVoQ11vkunKoA
# FdE3/hoGlMJ8yOobMubKwvSnowMOdKWvObarYBLj6Na59zHh3K3kGKDYwSNHR7Oh
# D26jq22YBoMbt2pnLdK9RBqSEIGPsDsJ18ebMlrC/2pgVItJwZPt4bRc4G/rJvmM
# 1bL5OBDm6s6R9b7T+2+TYTRcvJNFKIM2KmYoX7BzzosmJQayg9Rc9hUZTO1i4F4z
# 8ujo7AqnsAMrkbI2eb73rQgedaZlzLvjSFDzd5Ea/ttQokbIYViY9XwCFjyDKK05
# huzUtw1T0PhH5nUwjewwk3YUpltLXXRhTT8SkXbev1jLchApQfDVxW0mdmgRQRNY
# mtwmKwH0iU1Z23jPgUo+QEdfyYFQc4UQIyFZYIpkVMHMIRroOBl8ZhzNeDhFMJlP
# /2NPTLuqDQhTQXxYPUez+rbsjDIJAsxsPAxWEQIDAQABo4IBWTCCAVUwEgYDVR0T
# AQH/BAgwBgEB/wIBADAdBgNVHQ4EFgQUaDfg67Y7+F8Rhvv+YXsIiGX0TkIwHwYD
# VR0jBBgwFoAU7NfjgtJxXWRM3y5nP+e6mK4cD08wDgYDVR0PAQH/BAQDAgGGMBMG
# A1UdJQQMMAoGCCsGAQUFBwMDMHcGCCsGAQUFBwEBBGswaTAkBggrBgEFBQcwAYYY
# aHR0cDovL29jc3AuZGlnaWNlcnQuY29tMEEGCCsGAQUFBzAChjVodHRwOi8vY2Fj
# ZXJ0cy5kaWdpY2VydC5jb20vRGlnaUNlcnRUcnVzdGVkUm9vdEc0LmNydDBDBgNV
# HR8EPDA6MDigNqA0hjJodHRwOi8vY3JsMy5kaWdpY2VydC5jb20vRGlnaUNlcnRU
# cnVzdGVkUm9vdEc0LmNybDAcBgNVHSAEFTATMAcGBWeBDAEDMAgGBmeBDAEEATAN
# BgkqhkiG9w0BAQwFAAOCAgEAOiNEPY0Idu6PvDqZ01bgAhql+Eg08yy25nRm95Ry
# sQDKr2wwJxMSnpBEn0v9nqN8JtU3vDpdSG2V1T9J9Ce7FoFFUP2cvbaF4HZ+N3HL
# IvdaqpDP9ZNq4+sg0dVQeYiaiorBtr2hSBh+3NiAGhEZGM1hmYFW9snjdufE5Btf
# Q/g+lP92OT2e1JnPSt0o618moZVYSNUa/tcnP/2Q0XaG3RywYFzzDaju4ImhvTnh
# OE7abrs2nfvlIVNaw8rpavGiPttDuDPITzgUkpn13c5UbdldAhQfQDN8A+KVssIh
# dXNSy0bYxDQcoqVLjc1vdjcshT8azibpGL6QB7BDf5WIIIJw8MzK7/0pNVwfiThV
# 9zeKiwmhywvpMRr/LhlcOXHhvpynCgbWJme3kuZOX956rEnPLqR0kq3bPKSchh/j
# wVYbKyP/j7XqiHtwa+aguv06P0WmxOgWkVKLQcBIhEuWTatEQOON8BUozu3xGFYH
# Ki8QxAwIZDwzj64ojDzLj4gLDb879M4ee47vtevLt/B3E+bnKD+sEq6lLyJsQfmC
# XBVmzGwOysWGw/YmMwwHS6DTBwJqakAwSEs0qFEgu60bhQjiWQ1tygVQK+pKHJ6l
# /aCnHwZ05/LWUpD9r4VIIflXO7ScA+2GRfS0YW6/aOImYIbqyK+p/pQd52MbOoZW
# eE4wggdYMIIFQKADAgECAhAIfHT3o/FeY5ksO94AUhTmMA0GCSqGSIb3DQEBCwUA
# MGkxCzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5EaWdpQ2VydCwgSW5jLjFBMD8GA1UE
# AxM4RGlnaUNlcnQgVHJ1c3RlZCBHNCBDb2RlIFNpZ25pbmcgUlNBNDA5NiBTSEEz
# ODQgMjAyMSBDQTEwHhcNMjMxMDE4MDAwMDAwWhcNMjYxMjE2MjM1OTU5WjBgMQsw
# CQYDVQQGEwJHQjEPMA0GA1UEBxMGTG9uZG9uMR8wHQYDVQQKExZWaXJ0dWFsIEVu
# Z2luZSBMaW1pdGVkMR8wHQYDVQQDExZWaXJ0dWFsIEVuZ2luZSBMaW1pdGVkMIIC
# IjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAtyhrsCMi6pgLcX5sWY7I09dO
# WKweRHfDwW5AN6ffgLCYO9dqWWxvqu95FqnNVRyt1VNzEl3TevKVhRE0GGdirei3
# VqnFFjLDwD2jHhGY8qoSYyfffj/WYq2DkvNI62C3gUwSeP3FeqKRalc2c3V2v4jh
# yEYhrgG3nfnWQ/Oq2xzuiCqHy1E4U+IKKDtrXls4JX2Z4J/uAHZIAyKfrcTRQOhZ
# R4ZS1cQkeSBU9Urx578rOmxL0si0GAoaYQC49W7OimRelbahxZw/R+f5ch+C1ycU
# CpeXLg+bFhpa0+EXnkGidlILZbJiZJn7qvMQTZgipQKZ8nhX3rtJLqTeodPWzcCk
# tXQUy0q5fxhR3e6Ls7XQesq/G2yMcCMTCd6eltze0OgabvL6Xkhir5lATHaJtnmw
# FlcKzRr1YXK1k1D84hWfKSAdUg8T1O72ztimbqFLg6WoC8M2qqsHcm2DOc9hM3i2
# CWRpegikitRvZ9i1wkA9arGh7+a7UD+nLh2hnGmO06wONLNqABOEn4JOVnNrQ1gY
# eDeH9FDx7IYuAvMsfXG9Bo+I97TR2VfwDAx+ccR+UQLON3aQyFZ3BefYnvUu0gUR
# ikEAnAS4Jnc3BHizgb0voz0iWRDjFoTTmCmrInCVDGc+5KMy0xyoUwdQvYvRGAWB
# 61OCWnXBXbAEPniTZ80CAwEAAaOCAgMwggH/MB8GA1UdIwQYMBaAFGg34Ou2O/hf
# EYb7/mF7CIhl9E5CMB0GA1UdDgQWBBRuAv58K4EDYLmb7WNcxt5+r4NfnzA+BgNV
# HSAENzA1MDMGBmeBDAEEATApMCcGCCsGAQUFBwIBFhtodHRwOi8vd3d3LmRpZ2lj
# ZXJ0LmNvbS9DUFMwDgYDVR0PAQH/BAQDAgeAMBMGA1UdJQQMMAoGCCsGAQUFBwMD
# MIG1BgNVHR8Ega0wgaowU6BRoE+GTWh0dHA6Ly9jcmwzLmRpZ2ljZXJ0LmNvbS9E
# aWdpQ2VydFRydXN0ZWRHNENvZGVTaWduaW5nUlNBNDA5NlNIQTM4NDIwMjFDQTEu
# Y3JsMFOgUaBPhk1odHRwOi8vY3JsNC5kaWdpY2VydC5jb20vRGlnaUNlcnRUcnVz
# dGVkRzRDb2RlU2lnbmluZ1JTQTQwOTZTSEEzODQyMDIxQ0ExLmNybDCBlAYIKwYB
# BQUHAQEEgYcwgYQwJAYIKwYBBQUHMAGGGGh0dHA6Ly9vY3NwLmRpZ2ljZXJ0LmNv
# bTBcBggrBgEFBQcwAoZQaHR0cDovL2NhY2VydHMuZGlnaWNlcnQuY29tL0RpZ2lD
# ZXJ0VHJ1c3RlZEc0Q29kZVNpZ25pbmdSU0E0MDk2U0hBMzg0MjAyMUNBMS5jcnQw
# CQYDVR0TBAIwADANBgkqhkiG9w0BAQsFAAOCAgEAnXMg6efkBrwLIvd1Xmuh0dam
# 9FhUtDEj+P5SIqdP/U4veOv66NEQhBHLbW2Dvrdm6ec0HMj9b4e8pt4ylKFzHIPj
# fpuRffHVR9JQSx8qpryN6pP49DfCkAYeZGqjY3pGRzd/xQ0cfwcuYbUF+vwVk7tj
# q8c93VHCM0rb5M4N2hD1Ze1pvZxtaf9QnFKFzgXZjr02K6bswQc2+n5jFCp7zV1f
# KTstyb68rhSJBWKK1tMeFk6a6HXr5buTD3skluC0oyPmD7yAd97r2owjDMEveEso
# kADP/z7XQk7wqbwbpi4W6Uju2qHK/9UUsVRF5KTVEAIzVw2V1Aq/Jh3JuSV7b7C1
# 4CghNekltBb+w7YVp8/IFcj7axqnpNQ/+f7RVc3A5hyjV+MkoSwn8Sg7a7hn6SzX
# jec/TfRVvWCmG94MQHko+6206uIXrZnmQ6UQYFyOHRlyKDEozzkZhIcVlsZloUjL
# 3FZ5V/l8TIIzbc3bkEnu4iByksNvRxI6c5264OLauYlWv50ZUPwXmZ9gX8bs3BqZ
# avbGUrOW2PIjtWhtvs4zHhMBCoU1Z0OMvXcF9rUDqefmVCZK46xz3DGKVkDQnxY6
# UWQ3GL60/lEzju4YS99LJVQks2UGmP6LAzlEZ1dnGqi1aQ51OidCYEs39B75PsvO
# By2iAR8pBVi/byWBypExghpzMIIabwIBATB9MGkxCzAJBgNVBAYTAlVTMRcwFQYD
# VQQKEw5EaWdpQ2VydCwgSW5jLjFBMD8GA1UEAxM4RGlnaUNlcnQgVHJ1c3RlZCBH
# NCBDb2RlIFNpZ25pbmcgUlNBNDA5NiBTSEEzODQgMjAyMSBDQTECEAh8dPej8V5j
# mSw73gBSFOYwDQYJYIZIAWUDBAIBBQCggYQwGAYKKwYBBAGCNwIBDDEKMAigAoAA
# oQKAADAZBgkqhkiG9w0BCQMxDAYKKwYBBAGCNwIBBDAcBgorBgEEAYI3AgELMQ4w
# DAYKKwYBBAGCNwIBFTAvBgkqhkiG9w0BCQQxIgQg6drFkWtmNeAuRh04B/v2Jw5w
# SB12xySseBQ19hkcyAMwDQYJKoZIhvcNAQEBBQAEggIAm0NvwsVlUWCh+lhXcUo9
# 8DaRDQ8a2FdAeKZzuDglw5kvflYAXN6Yk6co6e5FOAEFW+3aPdTJ0OV9dmneX4s/
# YBO39tSSAM8eBlhhtcu7TqT0MGNLlGoMhxpYou/Nen1aFE19WBRJFhpd9AqfzE72
# X1XMDFehNM/4K7UCWhs1PsKeILyPeRIwtpdF7RFI7YLrU5w4pKcG04o4gNYN4MwJ
# ZtmzofHfug5+p1MTmXPU2lj7GxGGU5GjXuUFcEVNT3QSvU9c6H2ivtUkDt0MVWs4
# zV42dl6+XZe9AjIWXJAiHjzXJ6CaT84XpjSkr976VvH80tEoWVjwcKgG5LRUYOxV
# jlJHV//ZjI5J/eHGPx0uEWeZ+6+vG+VgvqCAki0AcyY4qaM3p8PxKXwhVBfts01Q
# /O2LNaNfbyPIpTIpLHPUJcT4gnPrhLvQFjUtqt0S3xeJhiFNeYo69BK0vpbNjqfA
# UfOWqZ/wIWR8A1RxEFyRYvPrIWjuphzWg13DTjHAQezoglCoJNAd6X7aRj0mOjHB
# YHcuFXHhMb9DqVYqZE2wJAYaHX7uneYLrgguq5MPoOYzL/IrBIsfw9Rstoi3iVoZ
# o47We8Xk5O0f3H/Sbn2OACtXkw99y7Dd/E8Y7R4VvDMNSAu7XeOroxGAyaEW5PU1
# uUC8/4xKgVph93f88mnsfmqhghdAMIIXPAYKKwYBBAGCNwMDATGCFywwghcoBgkq
# hkiG9w0BBwKgghcZMIIXFQIBAzEPMA0GCWCGSAFlAwQCAQUAMHgGCyqGSIb3DQEJ
# EAEEoGkEZzBlAgEBBglghkgBhv1sBwEwMTANBglghkgBZQMEAgEFAAQgZ6cPLCAN
# n5Hb2czobDljo/hIeCZIhRmYVGZQlPWNfXgCEQCIjsjEvNGeYbaF5cJROQZtGA8y
# MDI0MDIxMzEwMjIxM1qgghMJMIIGwjCCBKqgAwIBAgIQBUSv85SdCDmmv9s/X+Vh
# FjANBgkqhkiG9w0BAQsFADBjMQswCQYDVQQGEwJVUzEXMBUGA1UEChMORGlnaUNl
# cnQsIEluYy4xOzA5BgNVBAMTMkRpZ2lDZXJ0IFRydXN0ZWQgRzQgUlNBNDA5NiBT
# SEEyNTYgVGltZVN0YW1waW5nIENBMB4XDTIzMDcxNDAwMDAwMFoXDTM0MTAxMzIz
# NTk1OVowSDELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDkRpZ2lDZXJ0LCBJbmMuMSAw
# HgYDVQQDExdEaWdpQ2VydCBUaW1lc3RhbXAgMjAyMzCCAiIwDQYJKoZIhvcNAQEB
# BQADggIPADCCAgoCggIBAKNTRYcdg45brD5UsyPgz5/X5dLnXaEOCdwvSKOXejsq
# nGfcYhVYwamTEafNqrJq3RApih5iY2nTWJw1cb86l+uUUI8cIOrHmjsvlmbjaedp
# /lvD1isgHMGXlLSlUIHyz8sHpjBoyoNC2vx/CSSUpIIa2mq62DvKXd4ZGIX7ReoN
# YWyd/nFexAaaPPDFLnkPG2ZS48jWPl/aQ9OE9dDH9kgtXkV1lnX+3RChG4PBuOZS
# lbVH13gpOWvgeFmX40QrStWVzu8IF+qCZE3/I+PKhu60pCFkcOvV5aDaY7Mu6QXu
# qvYk9R28mxyyt1/f8O52fTGZZUdVnUokL6wrl76f5P17cz4y7lI0+9S769SgLDSb
# 495uZBkHNwGRDxy1Uc2qTGaDiGhiu7xBG3gZbeTZD+BYQfvYsSzhUa+0rRUGFOpi
# CBPTaR58ZE2dD9/O0V6MqqtQFcmzyrzXxDtoRKOlO0L9c33u3Qr/eTQQfqZcClhM
# AD6FaXXHg2TWdc2PEnZWpST618RrIbroHzSYLzrqawGw9/sqhux7UjipmAmhcbJs
# ca8+uG+W1eEQE/5hRwqM/vC2x9XH3mwk8L9CgsqgcT2ckpMEtGlwJw1Pt7U20clf
# CKRwo+wK8REuZODLIivK8SgTIUlRfgZm0zu++uuRONhRB8qUt+JQofM604qDy0B7
# AgMBAAGjggGLMIIBhzAOBgNVHQ8BAf8EBAMCB4AwDAYDVR0TAQH/BAIwADAWBgNV
# HSUBAf8EDDAKBggrBgEFBQcDCDAgBgNVHSAEGTAXMAgGBmeBDAEEAjALBglghkgB
# hv1sBwEwHwYDVR0jBBgwFoAUuhbZbU2FL3MpdpovdYxqII+eyG8wHQYDVR0OBBYE
# FKW27xPn783QZKHVVqllMaPe1eNJMFoGA1UdHwRTMFEwT6BNoEuGSWh0dHA6Ly9j
# cmwzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydFRydXN0ZWRHNFJTQTQwOTZTSEEyNTZU
# aW1lU3RhbXBpbmdDQS5jcmwwgZAGCCsGAQUFBwEBBIGDMIGAMCQGCCsGAQUFBzAB
# hhhodHRwOi8vb2NzcC5kaWdpY2VydC5jb20wWAYIKwYBBQUHMAKGTGh0dHA6Ly9j
# YWNlcnRzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydFRydXN0ZWRHNFJTQTQwOTZTSEEy
# NTZUaW1lU3RhbXBpbmdDQS5jcnQwDQYJKoZIhvcNAQELBQADggIBAIEa1t6gqbWY
# F7xwjU+KPGic2CX/yyzkzepdIpLsjCICqbjPgKjZ5+PF7SaCinEvGN1Ott5s1+Fg
# nCvt7T1IjrhrunxdvcJhN2hJd6PrkKoS1yeF844ektrCQDifXcigLiV4JZ0qBXqE
# KZi2V3mP2yZWK7Dzp703DNiYdk9WuVLCtp04qYHnbUFcjGnRuSvExnvPnPp44pMa
# dqJpddNQ5EQSviANnqlE0PjlSXcIWiHFtM+YlRpUurm8wWkZus8W8oM3NG6wQSbd
# 3lqXTzON1I13fXVFoaVYJmoDRd7ZULVQjK9WvUzF4UbFKNOt50MAcN7MmJ4ZiQPq
# 1JE3701S88lgIcRWR+3aEUuMMsOI5ljitts++V+wQtaP4xeR0arAVeOGv6wnLEHQ
# mjNKqDbUuXKWfpd5OEhfysLcPTLfddY2Z1qJ+Panx+VPNTwAvb6cKmx5AdzaROY6
# 3jg7B145WPR8czFVoIARyxQMfq68/qTreWWqaNYiyjvrmoI1VygWy2nyMpqy0tg6
# uLFGhmu6F/3Ed2wVbK6rr3M66ElGt9V/zLY4wNjsHPW2obhDLN9OTH0eaHDAdwrU
# AuBcYLso/zjlUlrWrBciI0707NMX+1Br/wd3H3GXREHJuEbTbDJ8WC9nR2XlG3O2
# mflrLAZG70Ee8PBf4NvZrZCARK+AEEGKMIIGrjCCBJagAwIBAgIQBzY3tyRUfNhH
# rP0oZipeWzANBgkqhkiG9w0BAQsFADBiMQswCQYDVQQGEwJVUzEVMBMGA1UEChMM
# RGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29tMSEwHwYDVQQD
# ExhEaWdpQ2VydCBUcnVzdGVkIFJvb3QgRzQwHhcNMjIwMzIzMDAwMDAwWhcNMzcw
# MzIyMjM1OTU5WjBjMQswCQYDVQQGEwJVUzEXMBUGA1UEChMORGlnaUNlcnQsIElu
# Yy4xOzA5BgNVBAMTMkRpZ2lDZXJ0IFRydXN0ZWQgRzQgUlNBNDA5NiBTSEEyNTYg
# VGltZVN0YW1waW5nIENBMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA
# xoY1BkmzwT1ySVFVxyUDxPKRN6mXUaHW0oPRnkyibaCwzIP5WvYRoUQVQl+kiPNo
# +n3znIkLf50fng8zH1ATCyZzlm34V6gCff1DtITaEfFzsbPuK4CEiiIY3+vaPcQX
# f6sZKz5C3GeO6lE98NZW1OcoLevTsbV15x8GZY2UKdPZ7Gnf2ZCHRgB720RBidx8
# ald68Dd5n12sy+iEZLRS8nZH92GDGd1ftFQLIWhuNyG7QKxfst5Kfc71ORJn7w6l
# Y2zkpsUdzTYNXNXmG6jBZHRAp8ByxbpOH7G1WE15/tePc5OsLDnipUjW8LAxE6lX
# KZYnLvWHpo9OdhVVJnCYJn+gGkcgQ+NDY4B7dW4nJZCYOjgRs/b2nuY7W+yB3iIU
# 2YIqx5K/oN7jPqJz+ucfWmyU8lKVEStYdEAoq3NDzt9KoRxrOMUp88qqlnNCaJ+2
# RrOdOqPVA+C/8KI8ykLcGEh/FDTP0kyr75s9/g64ZCr6dSgkQe1CvwWcZklSUPRR
# 8zZJTYsg0ixXNXkrqPNFYLwjjVj33GHek/45wPmyMKVM1+mYSlg+0wOI/rOP015L
# dhJRk8mMDDtbiiKowSYI+RQQEgN9XyO7ZONj4KbhPvbCdLI/Hgl27KtdRnXiYKNY
# CQEoAA6EVO7O6V3IXjASvUaetdN2udIOa5kM0jO0zbECAwEAAaOCAV0wggFZMBIG
# A1UdEwEB/wQIMAYBAf8CAQAwHQYDVR0OBBYEFLoW2W1NhS9zKXaaL3WMaiCPnshv
# MB8GA1UdIwQYMBaAFOzX44LScV1kTN8uZz/nupiuHA9PMA4GA1UdDwEB/wQEAwIB
# hjATBgNVHSUEDDAKBggrBgEFBQcDCDB3BggrBgEFBQcBAQRrMGkwJAYIKwYBBQUH
# MAGGGGh0dHA6Ly9vY3NwLmRpZ2ljZXJ0LmNvbTBBBggrBgEFBQcwAoY1aHR0cDov
# L2NhY2VydHMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0VHJ1c3RlZFJvb3RHNC5jcnQw
# QwYDVR0fBDwwOjA4oDagNIYyaHR0cDovL2NybDMuZGlnaWNlcnQuY29tL0RpZ2lD
# ZXJ0VHJ1c3RlZFJvb3RHNC5jcmwwIAYDVR0gBBkwFzAIBgZngQwBBAIwCwYJYIZI
# AYb9bAcBMA0GCSqGSIb3DQEBCwUAA4ICAQB9WY7Ak7ZvmKlEIgF+ZtbYIULhsBgu
# EE0TzzBTzr8Y+8dQXeJLKftwig2qKWn8acHPHQfpPmDI2AvlXFvXbYf6hCAlNDFn
# zbYSlm/EUExiHQwIgqgWvalWzxVzjQEiJc6VaT9Hd/tydBTX/6tPiix6q4XNQ1/t
# YLaqT5Fmniye4Iqs5f2MvGQmh2ySvZ180HAKfO+ovHVPulr3qRCyXen/KFSJ8NWK
# cXZl2szwcqMj+sAngkSumScbqyQeJsG33irr9p6xeZmBo1aGqwpFyd/EjaDnmPv7
# pp1yr8THwcFqcdnGE4AJxLafzYeHJLtPo0m5d2aR8XKc6UsCUqc3fpNTrDsdCEkP
# lM05et3/JWOZJyw9P2un8WbDQc1PtkCbISFA0LcTJM3cHXg65J6t5TRxktcma+Q4
# c6umAU+9Pzt4rUyt+8SVe+0KXzM5h0F4ejjpnOHdI/0dKNPH+ejxmF/7K9h+8kad
# dSweJywm228Vex4Ziza4k9Tm8heZWcpw8De/mADfIBZPJ/tgZxahZrrdVcA6KYaw
# mKAr7ZVBtzrVFZgxtGIJDwq9gdkT/r+k0fNX2bwE+oLeMt8EifAAzV3C+dAjfwAL
# 5HYCJtnwZXZCpimHCUcr5n8apIUP/JiW9lVUKx+A+sDyDivl1vupL0QVSucTDh3b
# NzgaoSv27dZ8/DCCBY0wggR1oAMCAQICEA6bGI750C3n79tQ4ghAGFowDQYJKoZI
# hvcNAQEMBQAwZTELMAkGA1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZ
# MBcGA1UECxMQd3d3LmRpZ2ljZXJ0LmNvbTEkMCIGA1UEAxMbRGlnaUNlcnQgQXNz
# dXJlZCBJRCBSb290IENBMB4XDTIyMDgwMTAwMDAwMFoXDTMxMTEwOTIzNTk1OVow
# YjELMAkGA1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZMBcGA1UECxMQ
# d3d3LmRpZ2ljZXJ0LmNvbTEhMB8GA1UEAxMYRGlnaUNlcnQgVHJ1c3RlZCBSb290
# IEc0MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAv+aQc2jeu+RdSjww
# IjBpM+zCpyUuySE98orYWcLhKac9WKt2ms2uexuEDcQwH/MbpDgW61bGl20dq7J5
# 8soR0uRf1gU8Ug9SH8aeFaV+vp+pVxZZVXKvaJNwwrK6dZlqczKU0RBEEC7fgvMH
# hOZ0O21x4i0MG+4g1ckgHWMpLc7sXk7Ik/ghYZs06wXGXuxbGrzryc/NrDRAX7F6
# Zu53yEioZldXn1RYjgwrt0+nMNlW7sp7XeOtyU9e5TXnMcvak17cjo+A2raRmECQ
# ecN4x7axxLVqGDgDEI3Y1DekLgV9iPWCPhCRcKtVgkEy19sEcypukQF8IUzUvK4b
# A3VdeGbZOjFEmjNAvwjXWkmkwuapoGfdpCe8oU85tRFYF/ckXEaPZPfBaYh2mHY9
# WV1CdoeJl2l6SPDgohIbZpp0yt5LHucOY67m1O+SkjqePdwA5EUlibaaRBkrfsCU
# tNJhbesz2cXfSwQAzH0clcOP9yGyshG3u3/y1YxwLEFgqrFjGESVGnZifvaAsPvo
# ZKYz0YkH4b235kOkGLimdwHhD5QMIR2yVCkliWzlDlJRR3S+Jqy2QXXeeqxfjT/J
# vNNBERJb5RBQ6zHFynIWIgnffEx1P2PsIV/EIFFrb7GrhotPwtZFX50g/KEexcCP
# orF+CiaZ9eRpL5gdLfXZqbId5RsCAwEAAaOCATowggE2MA8GA1UdEwEB/wQFMAMB
# Af8wHQYDVR0OBBYEFOzX44LScV1kTN8uZz/nupiuHA9PMB8GA1UdIwQYMBaAFEXr
# oq/0ksuCMS1Ri6enIZ3zbcgPMA4GA1UdDwEB/wQEAwIBhjB5BggrBgEFBQcBAQRt
# MGswJAYIKwYBBQUHMAGGGGh0dHA6Ly9vY3NwLmRpZ2ljZXJ0LmNvbTBDBggrBgEF
# BQcwAoY3aHR0cDovL2NhY2VydHMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0QXNzdXJl
# ZElEUm9vdENBLmNydDBFBgNVHR8EPjA8MDqgOKA2hjRodHRwOi8vY3JsMy5kaWdp
# Y2VydC5jb20vRGlnaUNlcnRBc3N1cmVkSURSb290Q0EuY3JsMBEGA1UdIAQKMAgw
# BgYEVR0gADANBgkqhkiG9w0BAQwFAAOCAQEAcKC/Q1xV5zhfoKN0Gz22Ftf3v1cH
# vZqsoYcs7IVeqRq7IviHGmlUIu2kiHdtvRoU9BNKei8ttzjv9P+Aufih9/Jy3iS8
# UgPITtAq3votVs/59PesMHqai7Je1M/RQ0SbQyHrlnKhSLSZy51PpwYDE3cnRNTn
# f+hZqPC/Lwum6fI0POz3A8eHqNJMQBk1RmppVLC4oVaO7KTVPeix3P0c2PR3WlxU
# jG/voVA9/HYJaISfb8rbII01YBwCA8sgsKxYoA5AY8WYIsGyWfVVa88nq2x2zm8j
# LfR+cWojayL/ErhULSd+2DrZ8LaHlv1b0VysGMNNn3O3AamfV6peKOK5lDGCA3Yw
# ggNyAgEBMHcwYzELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDkRpZ2lDZXJ0LCBJbmMu
# MTswOQYDVQQDEzJEaWdpQ2VydCBUcnVzdGVkIEc0IFJTQTQwOTYgU0hBMjU2IFRp
# bWVTdGFtcGluZyBDQQIQBUSv85SdCDmmv9s/X+VhFjANBglghkgBZQMEAgEFAKCB
# 0TAaBgkqhkiG9w0BCQMxDQYLKoZIhvcNAQkQAQQwHAYJKoZIhvcNAQkFMQ8XDTI0
# MDIxMzEwMjIxM1owKwYLKoZIhvcNAQkQAgwxHDAaMBgwFgQUZvArMsLCyQ+CXc6q
# isnGTxmcz0AwLwYJKoZIhvcNAQkEMSIEIKWyK770MR3W73UVEWJt1zycZ4w+Bysq
# CDFhlvcRl0gfMDcGCyqGSIb3DQEJEAIvMSgwJjAkMCIEINL25G3tdCLM0dRAV2hB
# Nm+CitpVmq4zFq9NGprUDHgoMA0GCSqGSIb3DQEBAQUABIICAFTw01MXoga0Spav
# 2p6Kcl3wJwThTy2wg1h5PchlVzfDN6yLsQQAck5chy9ZOhV2lYZnoOLYnz3riV1i
# pBwiOkXn/IfYQZtdhFRquV77FR7uChMmh4hY1O43fDa6HJD6cVgzfy+Fzj5y6AQD
# aYOHFcSXhFnh8eoP72NuA/ADQIYHhVoTXvDtJHv6rAR81DypoWcqRPkQQStuK0iR
# Z8WDyrtWzInpIyQxDRrK5BNwaog12ioRvvYS1iTyVKNkYYFBoDyAsUCJKW47G7t9
# z93BhD6m/YHdl0u7NCL4+HvXMKy8EbH1LlxZq4se6b12jBDsnVWNcnKPlY+SbrRn
# b4Y6BCWJhs4BDDXvGDCXd1HQjAYvOIR85wjGOezKT5w8pBaAj/9gFF5O5qBFnJjf
# 7BVzlszBce7oqiap07SK+4oo5uWl2fc0HJ3VA8ZbDiwlw53ncYjB3tzuyh1hCD19
# a/E1mSHXOVft4R2do6/ZHRBLbcgrB2Ec4ewx4R4Zp7XnPzdEa4oSY+o5lNfWk/yE
# lssekYdebrA6gVemVTOdyntPTR+2j/umGh+pDb3LAeW6kUSnJ9mm7o13mirdY/Kk
# isRR4OI1gEWRb+LSrMAo9emOoOROHvp1Y+6cTqceJCNxmL29qoiX7qRjSmYEcIX5
# q3BWlxfpiiHJmrd3lF8UoVvsLRHF
# SIG # End signature block