InstallMarvellQLogicFCPowerKit.psm1

#/************************************************************************
#* *
#* NOTICE *
#* *
#* COPYRIGHT 2019-2025 Marvell Semiconductor, Inc *
#* ALL RIGHTS RESERVED *
#* *
#* This computer program is CONFIDENTIAL and contains TRADE SECRETS of *
#* Marvell Semiconductor, Inc. The receipt or possession of this *
#* program does not convey any rights to reproduce or disclose its *
#* contents, or to manufacture, use, or sell anything that it may *
#* describe, in whole or in part, without the specific written consent *
#* of Marvell Semiconductor, Inc. *
#* Any reproduction of this program without the express written consent *
#* of Marvell Semiconductor, Inc is a violation of the copyright laws *
#* and may subject you to civil liability and criminal prosecution. *
#* *
#************************************************************************/


# Helper Functions

Function Out-Log {
    # This function controls console output, displaying it only if the Verbose variable is set.
    # This function now has another parameter, Level. Commands that may output messages now have a level parameter set. Key is below.
    # Level = 0 - Completely silent except for success / failure messages.
    # Level = 1 - Default if $Script:verbose is not set, about 5 output messages per added appx.
    # Level = 2 - Level 1 + Appcmd output messages, Add-AppxPackage messages
    # Level = 3 - Level 2 + Registry, XML, enabling of features, WinRM, setx, IO, netsh, etc. Very verbose.
    Param (
        [Parameter(Mandatory=$true)] [AllowNull()] $Data,
        [Parameter(Mandatory=$true)] [AllowNull()] $Level,
        [Parameter(Mandatory=$false)] [AllowNull()] $ForegroundColor,
        [Parameter(Mandatory=$false)] [AllowNull()] [Switch] $NoNewline
    )
    if ($Script:Verbose -eq $null) { $Script:Verbose = 1 }
    if ($Data -eq $null) { return }
    $Data = ($Data | Out-String)
    $Data = $Data.Substring(0, $Data.Length - 2)  # Remove extra `n from Out-String (only one)
    $Data = $Data.Replace('"', '`"')
    $expression = "Write-Host -Object `"$Data`""
    if ($ForegroundColor -ne $null) { $expression += " -ForegroundColor $ForegroundColor" }
    if ($NoNewline) { $expression += " -NoNewline" }
    if ($Level -le $Script:verbose) {
        Invoke-Expression $expression
    }
}

Function Test-ExecutionPolicy {
    # This function checks to make sure the ExecutionPolicy is not Unrestricted and if it is, prompts the user to change it to either RemoteSigned or Bypass.
    # If the ExecutionPolicy is left at Unrestricted, the user will receive one prompt for every cmdlet each time they open a new PowerShell window.
    $currentExecutionPolicy = Get-ExecutionPolicy
    if ($currentExecutionPolicy -eq 'Unrestricted') {

        $title = 'Execution Policy Configuration'
        $message = "Your current execution policy is $currentExecutionPolicy. This policy causes PowerShell to prompt you every session for every cmdlet. Set the execution policy to RemoteSigned or Bypass to avoid this."
        $remoteSigned = New-Object System.Management.Automation.Host.ChoiceDescription '&RemoteSigned', `
            'Sets the execution policy to RemoteSigned.'
        $bypass = New-Object System.Management.Automation.Host.ChoiceDescription '&Bypass', `
            'Sets the execution policy to Bypass.'
        $ignoreAndContinue = New-Object System.Management.Automation.Host.ChoiceDescription '&Ignore and Continue', `
            'Does not change the execution policy.'
        $options = [System.Management.Automation.Host.ChoiceDescription[]]($remoteSigned, $bypass, $ignoreAndContinue)
        $tempResult = $host.ui.PromptForChoice($title, $message, $options, 0)


        if ($tempResult -eq 0) { $newExecutionPolicy = 'RemoteSigned' }
        if ($tempResult -eq 1) { $newExecutionPolicy = 'Bypass' }
        if ($tempResult -eq 2) { return }

        # If the user has set a CurrentUser sceope level ExecutionPolicy, then change this to RemoteSigned/Bypass as well.
        if ((Get-ExecutionPolicy -Scope CurrentUser) -ne 'Undefined') {
            Set-ExecutionPolicy -ExecutionPolicy $newExecutionPolicy -Scope CurrentUser -Force
        }
        Set-ExecutionPolicy -ExecutionPolicy $newExecutionPolicy -Scope LocalMachine -Force

        Out-Log -Data "ExecutionPolicy changed from $currentExecutionPolicy to $(Get-ExecutionPolicy)." -Level 0
    }
}

Function Test-IfNano {
    # Check if OS is Nano or Non-Nano
    $envInfo = [Environment]::OSVersion.Version
    $envInfoCimInst = Get-CimInstance Win32_OperatingSystem
    return ( ($envInfo.Major -eq 10) -and ($envInfo.Minor -eq 0) -and ($envInfoCimInst.ProductType -eq 3) -and (($envInfoCimInst.OperatingSystemSKU -eq 143) -or ($envInfoCimInst.OperatingSystemSKU -eq 144) ) )
}

Function Test-IfServerCore {
    # Check if OS is Server Core
    $regKey = 'hklm:/software/microsoft/windows nt/currentversion'
    return (Get-ItemProperty $regKey).InstallationType -eq 'Server Core'
}

Function Get-CmdletsAppxFileName {
    if ($Script:isNano) { return '.\MRVLFCProvider-WindowsServerNano.appx' }
    else { return '.\MRVLFCProvider-WindowsServer.appx' }
}

Function Add-ManyToRegistryWriteOver {
    # This function changes multiple registry values at once.
    # Path is the path to the registry key to change, e.g. "HKLM:\Software\Classes\CLSID\{A31B8A5E-8B6A-421C-8BB1-F85C9C34E659}\InprocServer32"
    # NameTypeValueArray is an array of arrays of size 3 containing Name, Type, and Value for the registry entry, e.g.
        # @(
            # @('(Default)', 'String', "C:\Program Files\WindowsApps\MRVLFCProvider_1.0.1.0_x64_NorthAmerica_yym61f5wjvd3m\MRVLFCProvider.dll"),
            # @('ThreadingModel', 'String', 'Free')
        # )

    Param (
        [Parameter(Mandatory=$true)] [ValidateNotNullOrEmpty()] $Path,
        [Parameter(Mandatory=$true)] [ValidateNotNullOrEmpty()] $NameTypeValueArray
    )
    # if path doesn't exist, create it
    if (-not (Test-Path -LiteralPath $Path)) {
        Out-Log -Data (New-Item -Path $Path -Force 2>&1) -Level 3
    }

    $arr = $NameTypeValueArray
    if ($arr[0] -isnot [System.Array]) {
        Out-Log -Data (New-ItemProperty -LiteralPath $Path -Name $arr[0] -Type $arr[1] -Value $arr[2] -Force 2>&1) -Level 3
        return
    }
    foreach ($ntv in $arr) {
        Out-Log -Data (New-ItemProperty -LiteralPath $Path -Name $ntv[0] -Type $ntv[1] -Value $ntv[2] -Force 2>&1) -Level 3
    }
}

Function Test-RegistryValue {
    Param (
        [Parameter(Mandatory=$true)] [ValidateNotNullOrEmpty()] $Path,
        [Parameter(Mandatory=$true)] [ValidateNotNullOrEmpty()] $Value
    )

    try {
        Out-Log -Data (Get-ItemProperty -Path $Path | Select-Object -ExpandProperty $Value -ErrorAction Stop 2>&1) -Level 3
        return $true
    } catch {
        return $false
    }
}

Function ConvertFrom-SecureToPlain {
    Param( [Parameter(Mandatory=$true)] [System.Security.SecureString] $SecurePassword )
    $passwordPointer = [Runtime.InteropServices.Marshal]::SecureStringToBSTR($SecurePassword)
    $plainTextPassword = [Runtime.InteropServices.Marshal]::PtrToStringAuto($passwordPointer)
    [Runtime.InteropServices.Marshal]::ZeroFreeBSTR($passwordPointer)
    return $plainTextPassword
}

Function Convert-CredentialToHeaderAuthorizationString {
    Param ( [Parameter(Mandatory=$true)] [ValidateNotNullOrEmpty()] $Credential )
    return [System.Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes("$($Credential.UserName)`:$(ConvertFrom-SecureToPlain -SecurePassword $Credential.Password)"))
}

Function Get-AppxPackageWrapper {
    # This function tries the Get-AppxPackage command several times before really failing.
    # This is necessary to prevent some intermittent issues with Get-AppxPackage.
    # This may no longer be necessary as it seems to only happen after an Add-AppxProvisionedPackage which is no longer used.
    Param ( [Parameter(Mandatory=$true)] [ValidateNotNullOrEmpty()] $AppxName )

    $numAttempts = 3
    $numSecondsBetweenAttempts = 5
    for ($i=0; $i -lt $numAttempts; $i++) {
        $appxPkg = Get-AppxPackage | Where-Object { $_.name -eq $AppxName }
        if ($appxPkg -ne $null) { return $appxPkg }
        Out-Log -Data ("Couldn't find Appx package. Trying again in $numSecondsBetweenAttempts seconds (attempt $($i+1) of $numAttempts) ...") -ForegroundColor DarkRed -Level 1
        Start-Sleep -Seconds $numSecondsBetweenAttempts
    }
    Out-Log -Data 'Failed to find Appx package. Please try running this script again.' -ForegroundColor Red -Level 0
    Exit
}

Function Invoke-IOWrapper {
    # This function tries the Expression several times before really failing.
    # This is necessary to prevent some intermittent issues with certain IO commands, especially Copy-Item.
    Param (
        [Parameter(Mandatory=$true)] [ValidateNotNullOrEmpty()] $Expression,
        [Parameter(Mandatory=$false)] [ValidateNotNullOrEmpty()] $SuccessMessage,
        [Parameter(Mandatory=$false)] [ValidateNotNullOrEmpty()] $FailMessage
    )

    $numAttempts = 5
    $numSecondsBetweenAttempts = 1
    $Expression += ' -ErrorAction Stop'
    for ($i=0; $i -lt $numAttempts; $i++) {
        try {
            Invoke-Expression $Expression
            if ($i -ne 0) { Out-Log -Data 'Success!' -ForegroundColor DarkGreen -Level 3 }
            Out-Log -Data $SuccessMessage -Level 1
            return
        } catch {
            if ($i -ne 0) { Out-Log -Data 'Failed.' -ForegroundColor DarkRed -Level 3 }
            Out-Log -Data "$FailMessage Trying again in $numSecondsBetweenAttempts seconds (attempt $($i+1) of $numAttempts) ... " -ForegroundColor DarkRed -NoNewline -Level 3
            Start-Sleep -Seconds $numSecondsBetweenAttempts
            $Global:err = $_
        }
    }
    Out-Log -Data 'Failed.' -ForegroundColor DarkRed -Level 3
    Out-Log -Data "$FailMessage Failed after $numAttempts attempts with $numSecondsBetweenAttempts second(s) between each attempt." -ForegroundColor Red -Level 0
    Out-Log -Data "Failed to install MarvellQLogicFCPowerKit. Before installing MarvellQLogicFCPowerKit, uninstall the same, reboot the server and try installing again." -ForegroundColor Red -Level 0
    Exit
}


# Prompts
Function Set-WinRMConfigurationPrompt {
    if ($Script:isNano) { return $false } # Skip if Nano
    if ($ConfigureWinRM) { return $true }

    $title = 'WinRM Configuration'
    $message = 'Do you want to configure WinRM in order to connect to Linux machines remotely?'
    $yes = New-Object System.Management.Automation.Host.ChoiceDescription '&Yes', `
        'Configures WinRM to allow remote connections to Linux machines.'
    $no = New-Object System.Management.Automation.Host.ChoiceDescription '&No', `
        'Does not configure WinRM. Remote connection to Linux machines will not be possible.'
    $options = [System.Management.Automation.Host.ChoiceDescription[]]($yes, $no)
    $tempResult = $host.ui.PromptForChoice($title, $message, $options, 0)
    if ($tempResult -eq 0) { return $true } else { return $false }
}

Function Get-CredentialWrapper {
    Param ( [Parameter(Mandatory=$true)] [ValidateNotNullOrEmpty()] $AddAccountToIISResponse )
    if (-not $AddAccountToIISResponse) { return $null }

    try {
        return Get-Credential -Message 'Enter the credentials for the account you would like to be added to IIS' -UserName $env:USERNAME
    } catch {
        Out-Log -Data 'No credentials supplied; no account will be added to IIS.' -Level 1
    }
}

Function Install-PoshSSHModule {
    if ($Script:isNano) { return $false } # Skip if Nano
    if ($InstallPoshSSHModule) { return $true }

    $title = 'Install-Posh-SSH_Module'
    $message = 'Do you want to install Posh-SSH_Module in-order to manage [Linux and VMware_ESXi Remote Host Systems] or not ?'
    $yes = New-Object System.Management.Automation.Host.ChoiceDescription '&Yes', 'install Posh-SSH_Module.'
    $no = New-Object System.Management.Automation.Host.ChoiceDescription '&No', 'Does not install Posh-SSH_Module.'
    $options = [System.Management.Automation.Host.ChoiceDescription[]]($yes, $no)

    $tempResult = $host.ui.PromptForChoice($title, $message, $options, 0)
    if ($tempResult -eq 0) { return $true } else { return $false }
}

Function Install-RESTServerPrompt {
    if ($Script:isNano) { return $false } # Skip if Nano
    if ($InstallRESTServer) { return $true }

    $title = 'Install-REST Server'
    $message = 'Do you want to install the REST server to be able to invoke FC PowerShell cmdlets via REST?'
    $yes = New-Object System.Management.Automation.Host.ChoiceDescription '&Yes', 'Installs the REST server.'
    $no = New-Object System.Management.Automation.Host.ChoiceDescription '&No', 'Does not install the REST server.'
    $options = [System.Management.Automation.Host.ChoiceDescription[]]($yes, $no)

    $tempResult = $host.ui.PromptForChoice($title, $message, $options, 0)
    if ($tempResult -eq 0) { return $true } else { return $false }
}

Function Get-RESTServerProtocolPrompt {
    $defaultValue = 'http'
    while ($true) {
        $response = Read-Host -Prompt "Would you like to use http or https? [$defaultValue] ?"
        if ([string]::IsNullOrWhiteSpace($response)) {
            return $defaultValue
        }
        elseif ($response -ne 'http' -and $response -ne 'https' -and $response -ne 'both') {
            Out-Log -Data "Response must be either: 'http' or 'https'; please try again.`n" -ForegroundColor Red -Level 0
        }
        else {
            return $response
        }
    }
}

Function Get-RESTServerPortPrompt {
    Param ( [Parameter(Mandatory=$true)] [ValidateNotNullOrEmpty()] $Protocol )
    $defaultValue = '7777'
    if ($Protocol -eq 'https') {
        $defaultValue = '7778'
    }
    while ($true) {
        $response = Read-Host -Prompt "What $($Protocol.ToUpper()) port would you like to use for the REST server [$defaultValue] ?"
        if ([string]::IsNullOrWhiteSpace($response)) {
            return $defaultValue
        }
        $responseAsInt = $response -as [int]
        if ($responseAsInt -eq $null -or $responseAsInt -lt 1 -or $responseAsInt -gt 65535) {
            Out-Log -Data "Response must be a valid port number; please try again.`n" -ForegroundColor Red -Level 0
        }
        else {
            return $response
        }
    }
    return $response
}

Function Get-RESTServerExistingHTTPSCert {
    $title = 'REST Server HTTPS Certificate Preference'
    $message = "Would you like to create a self-signed HTTPS certificate?`nSelecting no will allow you to choose the location of your existing HTTPS certificate."
    $yes = New-Object System.Management.Automation.Host.ChoiceDescription '&Yes', 'Creates a self-signed HTTPS certificate.'
    $no = New-Object System.Management.Automation.Host.ChoiceDescription '&No', 'Opens file selection dialog for location of existing HTTPS certificate.'
    $options = [System.Management.Automation.Host.ChoiceDescription[]]($yes, $no)

    while ($true) {
        $tempResult = $host.ui.PromptForChoice($title, $message, $options, 0)
        if ($tempResult -eq 0) {
            return $null  # create self-signed cert
        }

        $fileBrowser = New-Object System.Windows.Forms.OpenFileDialog -Property @{
            Title = 'Select HTTPS certificate to use for the REST server'
            InitialDirectory = (Get-Location).Path
            Filter = 'Certificate File (*.pfx)|*.pfx'
            Multiselect = $false
        }
        $null = $fileBrowser.ShowDialog()

        if (-not [string]::IsNullOrWhiteSpace($fileBrowser.FileName)) {
            return $fileBrowser.FileName
        }
    }
}

Function Get-RESTServerHTTPSCertPasswordPrompt {
    while ($true) {
        $password1 = Read-Host -AsSecureString 'Please enter a password for the HTTPS certificate'
        $password2 = Read-Host -AsSecureString 'Please re-enter the password for the HTTPS certificate'
        $password1Text = ConvertFrom-SecureToPlain -SecurePassword $password1
        $password2Text = ConvertFrom-SecureToPlain -SecurePassword $password2
        if ([string]::IsNullOrWhiteSpace($password1Text) -or [string]::IsNullOrWhiteSpace($password2Text)) {
            Out-Log -Data "One or both passwords are empty; please try again.`n" -ForegroundColor Red -Level 0
        }
        elseif ($password1Text -ne $password2Text) {
            Out-Log -Data "Passwords do not match; please try again.`n" -ForegroundColor Red -Level 0
        }
        else {
            return $password1
        }
    }
}

Function Get-RESTServerHTTPSCertPasswordPromptSimple {
    $password = Read-Host -AsSecureString 'Please enter the password for the HTTPS certificate'
    return $password
}

Function Test-HTTPSCertPassword {
    Param (
        [Parameter(Mandatory=$true)] [ValidateNotNullOrEmpty()] $CertificateFilePath,
        [Parameter(Mandatory=$true)] [ValidateNotNullOrEmpty()] $SecurePassword
    )
    $passwordText = ConvertFrom-SecureToPlain -SecurePassword $SecurePassword
    $null = certutil.exe -p $passwordText -dump $CertificateFilePath 2>&1
    return $LASTEXITCODE -eq 0
}

Function Test-LastExitCode {
    Param (
        [Parameter(Mandatory=$true)] [ValidateNotNullOrEmpty()] $ExitCode,
        [Parameter(Mandatory=$true)] [ValidateNotNullOrEmpty()] $Message,
        [Parameter(Mandatory=$false)] [AllowNull()] [Switch] $ExitScriptIfBadErrorCode
    )
    if ($ExitCode -eq 0) { return; }

    if (-not $ExitScriptIfBadErrorCode) {
        Out-Log -Data "WARNING: $Message" -ForegroundColor Yellow -Level 1
    } else {
        Out-Log -Data "ERROR: $Message" -ForegroundColor Red -Level 1
        Exit
    }
}

# Main Functions

Function Add-AppxTrustedAppsRegistryKey {
    # This function remembers user's current AllowAllTrustedApps registry setting and then changes it to
    # allow installation of all trusted apps.
    if ($Script:isNano) { return } # Skip if Nano
    if ($Script:isServerCore) { return } # Skip if Server Core

    # Remember user's registry setting before modify it to be able to set it back later.
    $appxPolicyRegKey = 'HKLM:\Software\Policies\Microsoft\Windows\Appx'
    $appxPolicyRegValName = 'AllowAllTrustedApps'
    if (Test-Path -Path $appxPolicyRegKey) {
        $appxPolicyRegValData = (Get-ItemProperty -Path $appxPolicyRegKey).$appxPolicyRegValName
        if ($appxPolicyRegValData -ne $null) {
            $Script:savedAppxPolicyRegValData = $appxPolicyRegValData
        }
        Start-Sleep -Milliseconds 1000  # Otherwise access denied error when do Add-ManyToRegistryWriteOver below
    }

    Add-ManyToRegistryWriteOver -Path $appxPolicyRegKey -NameTypeValueArray @(
        @($appxPolicyRegValName, 'DWord', 1) )
}

Function Remove-CLSIDRegistryPaths {
    # if (-not $Script:isNano) { return } # Skip if NOT Nano
    if (Test-Path -Path "Registry::HKCR\CLSID\$Script:CLSID") {
        Remove-Item -Path "Registry::HKCR\CLSID\$Script:CLSID" -Recurse
    }
    if (Test-Path -Path "HKLM:\Software\Classes\CLSID\$Script:CLSID") {
        Remove-Item -Path "HKLM:\Software\Classes\CLSID\$Script:CLSID" -Recurse
    }
}

Function Install-ForServerCore {
    Param ( [Parameter(Mandatory=$true)] [ValidateNotNullOrEmpty()] $AppxFileName )

    $serverCoreInstallPath = "$env:ProgramFiles\Marvell_Semiconductor_Inc\FC_PowerKit"
    $appxFileNameSimple = $AppxFileName.Replace('.\', '')
    $appxZipFileName = $appxFileNameSimple.Replace('.appx', '.zip')

    # Create installation directory if doesn't exist and copy Appx file to it (renamed to .zip)
    if (-not (Test-Path $serverCoreInstallPath)) {
        New-Item -Path $serverCoreInstallPath -Type Directory
        Copy-Item -Path $AppxFileName -Destination "$serverCoreInstallPath\$appxZipFileName"
    }

    # Unzip appx file to PowerKit directory
    Expand-Archive -Path "$serverCoreInstallPath\$appxZipFileName" -DestinationPath $serverCoreInstallPath

    # Register CIMProvider
    Register-CimProvider.exe -Namespace Root/qlgcfc -ProviderName MRVLFCProvider -Path "$serverCoreInstallPath\MRVLFCProvider.dll" -verbose -ForceUpdate -HostingModel LocalSystemHost
}

Function Add-AppxPackageWrapper {
    Param ( [Parameter(Mandatory=$true)] [ValidateNotNullOrEmpty()] $AppxFileName )

    $savedProgressPreference = $Global:ProgressPreference
    $Global:ProgressPreference = 'SilentlyContinue'
    Out-Log -Data (Add-AppxPackage $AppxFileName 2>&1) -Level 2
    $Global:ProgressPreference = $savedProgressPreference
    Out-Log -Data "Added '$($AppxFileName.Replace('.\', ''))' AppxPackage." -Level 1
}

Function Add-CLSIDRegistryPathsAndMofCompInstall {
    # if ($Script:isNano) { return } # Skip if Nano
    # Perform steps of Register-CimProvider.exe -Namespace Root/qlgcfc -ProviderName MRVLFCProvider -Path MRVLFCProvider.dll -verbose -ForceUpdate -HostingModel LocalSystemHost
    $appxPath = (Get-AppxPackageWrapper -AppxName 'MRVLFCProvider').InstallLocation
    $mofcompOutput = & mofcomp.exe -N:root\qlgcfc $appxPath\MRVLFCProvider.mof 2>&1
    if ($mofcompOutput -match 'Error') {
        Out-Log -Data "Failed to register '$appxPath\MRVLFCProvider.mof': $($mofcompOutput -match 'Error')" -ForegroundColor Red -Level 1
    }

    Add-ManyToRegistryWriteOver -Path "HKLM:\Software\Classes\CLSID\$Script:CLSID" -NameTypeValueArray @(
        @('(Default)', 'String', 'MRVLFCProvider') )
    Add-ManyToRegistryWriteOver -Path "HKLM:\Software\Classes\CLSID\$Script:CLSID\InprocServer32" -NameTypeValueArray @(
        @('(Default)', 'String', "$appxPath\MRVLFCProvider.dll"),
        @('ThreadingModel', 'String', 'Free') )
}

Function Remove-AppxTrustedAppsRegistryKey {
    if ($Script:isNano) { return } # Skip if Nano
    if ($Script:isServerCore) { return } # Skip if Server Core

    $appxPolicyRegKey = 'HKLM:\Software\Policies\Microsoft\Windows\Appx'
    $appxPolicyRegValName = 'AllowAllTrustedApps'

    # If user previously had this registry key, set it back to what it was before. Otherwise, just delete the registry key.
    if ($Script:savedAppxPolicyRegValData -ne $null) {
        Add-ManyToRegistryWriteOver -Path $appxPolicyRegKey -NameTypeValueArray @(
            @($appxPolicyRegValName, 'DWord', $Script:savedAppxPolicyRegValData) )
    } else {
        Remove-ItemProperty -Path $appxPolicyRegKey -Name $appxPolicyRegValName
    }
}

Function Copy-CmdletsToProgramData {
    # Check for ProgramData directories with old versions and remove them
    $oldPaths = (Get-ChildItem $env:ProgramData | Where-Object { ($_.Name).StartsWith('MRVLFCProvider_') }).FullName
    foreach ($oldPath in $oldPaths) {
        Remove-Item $oldPath -Recurse -Force 2>&1 | Out-Null
    }

    # Copy cmdlets to
    $appxPkg = Get-AppxPackageWrapper -AppxName 'MRVLFCProvider'
    $cmdletsPath = $appxPkg.InstallLocation + '\Cmdlets\'
    if (Test-Path $cmdletsPath) {
        $cmdletsDestPath = "C:\ProgramData\MarvellQLogicFCCmdlets"
        if (-not (Test-Path $cmdletsDestPath)) {
            Out-Log -Data (New-Item -ItemType Directory -Path $cmdletsDestPath -Force 2>&1) -Level 3
        }
        Invoke-IOWrapper -Expression "Copy-Item -Path `"$cmdletsPath\*`" -Destination $cmdletsDestPath -Recurse -Force" `
            -FailMessage "Failed to copy cmdlets."
        Import-Module -force $cmdletsDestPath\MarvellQLogicFCCmdlets.psd1
    }
}

Function Copy-CmdletsToWindowsPowerShellModules {
    # Copy Cmdlets to "C:\Program Files\WindowsPowerShell\Modules"
    $appxPkg = Get-AppxPackageWrapper -AppxName 'MRVLFCProvider'
    $cmdletsPath = $appxPkg.InstallLocation + '\Cmdlets\'
    if (Test-Path $cmdletsPath) {
        $cmdletsDestPath = "C:\Program Files\WindowsPowerShell\Modules\MarvellQLogicFCCmdlets"
        if(-not (Test-Path $cmdletsDestPath)) {
            Out-Log -Data (New-Item -ItemType Directory -Path $cmdletsDestPath -Force 2>&1) -Level 3
        }
        Write-Host $cmdletsPath
        Write-Host $cmdletsDestPath
        Invoke-IOWrapper -Expression "Copy-Item -Path `"$cmdletsPath\*`" -Destination `"$cmdletsDestPath\`" -Recurse -Force" `
            -FailMessage "Failed to copy cmdlets."
    }
}

Function Copy-XMLTemplatesToProgramData {
    # Copy cmdlets to ProgramData
    $appxPkg = Get-AppxPackageWrapper -AppxName 'MRVLFCProvider'
    $xmlsTemplatesPath = $appxPkg.InstallLocation + '\Cmdlets\XML'
    if (Test-Path $xmlsTemplatesPath) {
        $xmlsTemplatesDestPath = "C:\Program Files\Marvell_Semiconductor_Inc\FC_PowerKit\XML"
        if (Test-Path $xmlsTemplatesDestPath){
            Remove-Item $xmlsTemplatesDestPath -Recurse -Force
        }
        
        Invoke-IOWrapper -Expression "Copy-Item -Path `"$xmlsTemplatesPath`" -Destination `"$xmlsTemplatesDestPath`" -Recurse -Force" `
            -SuccessMessage "XML Templates copied to '$xmlsTemplatesDestPath'." `
            -FailMessage "Failed to copy XML Templates."
    }
}

Function Copy-DependentLibsAndFilesToWbemPath 
{
    $appxPkg = Get-AppxPackageWrapper -AppxName 'MRVLFCProvider'
    $dependentLibPath = $appxPkg.InstallLocation

    if (Test-Path $dependentLibPath)
    {
        $dependentLibDestPath = "C:\Windows\System32\wbem"
        
        # Copy ql2xhai2.dll to "C:\Windows\System32\wbem"
        Invoke-IOWrapper -Expression "Copy-Item -Path `"$dependentLibPath\ql2xhai2.dll`" -Destination `"$dependentLibDestPath`" -Recurse -Force" `
            -SuccessMessage "Copied dependent library to Wbem path : '$dependentLibDestPath'." `
            -FailMessage "Failed to copy dependent library : '$dependentLibDestPath'. Please uninstall the old version if present. Please stop 'WMI Provider Host' if running."
            

        # Copy adapters_adapter_provider.properties to "C:\Windows\System32\wbem"
        Invoke-IOWrapper -Expression "Copy-Item -Path `"$dependentLibPath\adapters_adapter_provider.properties`" -Destination `"$dependentLibDestPath`" -Recurse -Force" `
            -SuccessMessage "Copied dependent file - adapters_adapter_provider.properties to Wbem path : '$dependentLibDestPath'." `
            -FailMessage "Failed to copy dependent file - adapters_adapter_provider.properties : '$dependentLibDestPath'. Please uninstall the old version if present. Please stop 'WMI Provider Host' if running."
            
                        
            
        # Copy nvramdefs to "C:\Windows\System32\wbem"
        Invoke-IOWrapper -Expression "Copy-Item -Path `"$dependentLibPath\nvramdefs`" -Destination `"$dependentLibDestPath\nvramdefs`" -Recurse -Force" `
            -SuccessMessage "Copied dependent files - nvramdefs to Wbem path : '$dependentLibDestPath'." `
            -FailMessage "Failed to copy dependent files - nvramdefs : '$dependentLibDestPath'. Please uninstall the old version if present. Please stop 'WMI Provider Host' if running."              
    }
}


Function Add-ChangedCmdletsPathToPSModulePath {
    # This function adds cmdlet module path to system PSModulePath environment variable permanently.
    #$cmdletPath = "C:\Program Files\WindowsPowerShell\Modules\MarvellQLogicFCCmdlets"
    if ($Script:isServerCore) {
        $appxCmdletsPath = "$env:ProgramFiles\Marvell_Semiconductor_Inc\FC_PowerKit\Cmdlets"
    } else {
        $appxCmdletsPath = (Get-AppxPackageWrapper -AppxName 'MRVLFCProvider').InstallLocation + '\Cmdlets'
    }

    if (Test-RegistryValue -Path 'HKLM:\System\CurrentControlSet\Control\Session Manager\Environment' -Value 'PSModulePath') {
        $currPSModulePathArr = ((Get-ItemProperty -Path 'HKLM:\System\CurrentControlSet\Control\Session Manager\Environment').PSModulePath).Split(';')
        $newPSModulePathArr = @()
        # Remove any old paths with MRVLFCProvider from PSModulePath
        foreach ($path in $currPSModulePathArr) {
            if (-not $path.Contains('MRVLFCProvider')) {
                $newPSModulePathArr += $path
            }
        }
        # Add new cmdlet path to PSModulePath
        $newPSModulePathArr += $cmdletPath  # Skip for uninstall script
        $newPSModulePathStr = $newPSModulePathArr -join ';'

        # Write PSModulePath to registry and set local session variable
        Out-Log -Data (& setx /s $env:COMPUTERNAME PSModulePath $newPSModulePathStr /m 2>&1) -Level 3
        $env:PSModulePath = $newPSModulePathStr
        # Out-Log -Data "Added '$cmdletPath' to PSModulePath." -Level 1

    } else {
        # No PSModulePath registry key/value exists, so don't worry about modifying it. Just create a new one.
        Out-Log -Data (& setx /s $env:COMPUTERNAME PSModulePath $cmdletPath /m 2>&1) -Level 3
        $env:PSModulePath = $cmdletPath
        # Out-Log -Data "Added '$cmdletPath' to PSModulePath." -Level 1
    }
}

Function Add-CmdletsPathToPSModulePath {
    # This function adds cmdlet module path to system PSModulePath environment variable permanently.

    if ($Script:isServerCore) {
        $appxCmdletsPath = "$env:ProgramFiles\Marvell_Semiconductor_Inc\FC_PowerKit\Cmdlets"
    } else {
        #$appxCmdletsPath = (Get-AppxPackageWrapper -AppxName 'MRVLFCProvider').InstallLocation + '\Cmdlets'
        #$appxPkg = Get-AppxPackageWrapper -AppxName 'MRVLFCProvider'
        #$appxCmdletsPath = "$env:ProgramData\$($appxPkg.PackageFullName)"
        $appxCmdletsPath = "C:\ProgramData\MarvellQLogicFCCmdlets"
    }

    if (Test-RegistryValue -Path 'HKLM:\System\CurrentControlSet\Control\Session Manager\Environment' -Value 'PSModulePath') {
        $currPSModulePathArr = ((Get-ItemProperty -Path 'HKLM:\System\CurrentControlSet\Control\Session Manager\Environment').PSModulePath).Split(';')
        $newPSModulePathArr = @()
        # Remove any old paths with MRVLFCProvider from PSModulePath
        foreach ($path in $currPSModulePathArr) {
            if (-not $path.Contains('MRVLFCProvider')) {
                $newPSModulePathArr += $path
            }
        }
        # Add new cmdlet path to PSModulePath
        $newPSModulePathArr += $appxCmdletsPath  # Skip for uninstall script
        $newPSModulePathStr = $newPSModulePathArr -join ';'

        # Write PSModulePath to registry and set local session variable
        Out-Log -Data (& setx /s $env:COMPUTERNAME PSModulePath $newPSModulePathStr /m 2>&1) -Level 3
        $env:PSModulePath = $newPSModulePathStr
        # Out-Log -Data "Added '$appxCmdletsPath' to PSModulePath." -Level 1

    } else {
        # No PSModulePath registry key/value exists, so don't worry about modifying it. Just create a new one.
        Out-Log -Data (& setx /s $env:COMPUTERNAME PSModulePath $appxCmdletsPath /m 2>&1) -Level 3
        $env:PSModulePath = $appxCmdletsPath
        # Out-Log -Data "Added '$appxCmdletsPath' to PSModulePath." -Level 1
    }
}

Function Set-WinRMConfiguration {
    Param ( [Parameter(Mandatory=$true)] [ValidateNotNullOrEmpty()] $SetWinRMConfigurationResponse )
    if ($SetWinRMConfigurationResponse) {
        Out-Log -Data (& winrm set winrm/config/client '@{AllowUnencrypted="true"}' 2>&1) -Level 3
        Out-Log -Data (& winrm set winrm/config/client '@{TrustedHosts="*"}' 2>&1) -Level 3
        Out-Log -Data 'WinRM successfully configured.' -Level 1
    }
}

Function Test-PowerShellVersion {
    # This function tests the version of PowerShell / Windows Management Framework and prompts the user
      # to download a newer version if necessary.
    if ($PSVersionTable.PSVersion.Major -ge 4) { return }

    Out-Log -Data 'Failed to install FC REST API PowerKit: Windows Management Framework 4.0+ required.' -ForegroundColor Red -Level 0
    Out-Log -Data ' Please install WMF 4.0 or higher and run this script again.' -ForegroundColor Red -Level 0
    Out-Log -Data ' URL for WMF 4.0: https://www.microsoft.com/en-us/download/details.aspx?id=40855' -ForegroundColor Red -Level 0
    Out-Log -Data ' URL for WMF 5.0: https://www.microsoft.com/en-us/download/details.aspx?id=50395' -ForegroundColor Red -NoNewline -Level 0

    $title = 'Download Windows Management Framework 4.0+'
    $message = 'Windows Management Framework 4.0+ is required for FC REST API PowerKit. Which WMF version would you like to download?'
    $4 = New-Object System.Management.Automation.Host.ChoiceDescription '&4.0', `
        'Windows Management Framework 4.0 (https://www.microsoft.com/en-us/download/details.aspx?id=40855)'
    $5 = New-Object System.Management.Automation.Host.ChoiceDescription '&5.0', `
        'Windows Management Framework 5.0 (https://www.microsoft.com/en-us/download/details.aspx?id=50395)'
    $none = New-Object System.Management.Automation.Host.ChoiceDescription '&None', `
        'None'
    $options = [System.Management.Automation.Host.ChoiceDescription[]]($4, $5, $none)
    $tempResult = $host.ui.PromptForChoice($title, $message, $options, 1)
    if ($tempResult -eq 0) {
        Start-Process 'https://www.microsoft.com/en-us/download/details.aspx?id=40855'
    } elseif ($tempResult -eq 1) {
        Start-Process 'https://www.microsoft.com/en-us/download/details.aspx?id=50395'
    }
    # TODO - Add script exiting message.
    Exit
}

Function Test-RESTAPI {
    Param (
        [Parameter(Mandatory=$true)] [ValidateNotNullOrEmpty()] $Url,
        [Parameter(Mandatory=$false)] [AllowNull()] $EncodedAuthString
    )

    try {
        if ($encodedAuthString -eq $null) {
            $response = (Invoke-RestMethod -Method Get -Uri $Url).value
        } else {
            $response = (Invoke-RestMethod -Method Get -Headers @{'Authorization' = "Basic $EncodedAuthString"} -Uri $Url).value
        }
        $response = ($response | Out-String).Trim()
        Out-Log -Data "`n$response`n" -Level 2
    }
    catch {
        $Global:errRest = $_
        Out-Log -Data "REST API test failed" -ForegroundColor Red -Level 0
        Out-Log -Data " StatusCode: $($_.Exception.Response.StatusCode.value__)" -ForegroundColor Red -Level 0
        Out-Log -Data " StatusDescription: $($_.Exception.Response.StatusDescription)" -ForegroundColor Red -Level 0
    }
}

Function Import-AllCmdlets {
    # This function imports all QLGCFC cmdlets into the current PowerShell session.
    # This function is necessary to update any cmdlet and cmdlet help changes that have occurred from one PowerKit
    # version to another. One consequence of force importing the QLGCFC cmdlets is the user will be prompted if they
    # want to run software from an unpublished publisher when this function is called rather than the first time
    # intellisense is invoked after the PowerKit is installed. Note that this prompt only occurs if it is the user's
    # first time installing the PowerKit. Of course, in this scenario this function call is not necessary, since the
    # cmdlets will not need to be refreshed.
    # TODO - Determine if this is the first time PowerKit has been installed / if user already trusts QLGC publisher.
    #$moduleNames = (Get-Module -ListAvailable | Where-Object { $_.Name.StartsWith('QLGCFC_') }).Name
    #foreach ($moduleName in $moduleNames)
    #{
    # try
    # {
    # Import-Module `"C:\Program Files\WindowsPowerShell\Modules\MarvellQLogicFCCmdlets\MarvellQLogicFCCmdlets.psd1`" -Force
    # #2>&1 | Out-Null
    # }
    # catch
    # {
    # # ignore any exception when importing. ErrorAction and 2>&1 | Out-Null in above command does not work!
    # # Out-Log -Data " Error occured while performing : Import-Module $moduleName -Force" -ForegroundColor Red -Level 0
    # }
    #}
    
}


Function Get-LocalAgentPropertiesFilePath {
    $retFilePath = $null
    $filePath = $null
    $appxPkg = $null
    $localInstallPath = $null
    $agentPropertiesFileName = "agent.properties"

    $appxPkg = Get-AppxPackageWrapper -AppxName 'MRVLFCProvider'
    $localInstallPath = $appxPkg.InstallLocation
    $localInstallPath = $localInstallPath.Trim(' ', '"')

    $filePath = $localInstallPath + "\" + $agentPropertiesFileName
    if (Test-Path -Path $filePath) {
        $retFilePath = $filePath
    }

    return $retFilePath
}

Function Set-AdministratorsPrivilegesOnLocalAgentPropertiesFilePath {
    $ret = $false
    $functionName = $MyInvocation.InvocationName

    $filePath = Get-LocalAgentPropertiesFilePath
    if (-not([String]::IsNullOrEmpty($filePath))) {
        $Group = New-Object System.Security.Principal.NTAccount("Builtin", "Administrators")
        if ($null -ne $Group) {
            $AccessRule = New-Object System.Security.AccessControl.FileSystemAccessRule($Group, "FullControl", "Allow")
            if ($null -ne $AccessRule) {
                $ACL = Get-ACL $filePath
                if ($null -ne $ACL) {
                    $ACL.SetOwner($Group)
                    $ACL.SetAccessRule($AccessRule)
                    Set-Acl -Path $filePath -AclObject $ACL
                    $ret = $true
                }
            }
        }
    } else {
        Out-Log -Data "[$functionName]: Cmdlet 'Get-LocalAgentPropertiesFilePath' failed to get local 'agent.properties' file path." -Level 3
    }

    if ($false -eq $ret) {
        Out-Log -Data "[$functionName]: Failed to configure required administrators privileges on file[$filePath]" -ForegroundColor Red -Level 3
    } else {
        Out-Log -Data "Administrators privileges configured successfully on file[agent.properties]." -Level 3
    }
}


Function Set-VmwarePowerCLIPrompt {
    $title = 'Installation of module VMware.VimAutomation.Core'
    $message = 'VMware.VimAutomation.Core is required to communicate with ESXi8.0 U2 for Advanced Authentication.'
    $yes = New-Object System.Management.Automation.Host.ChoiceDescription '&Yes', `
        'Proceeding to install VMware.VimAutomation.Core.'
    $no = New-Object System.Management.Automation.Host.ChoiceDescription '&No', `
        'Communication to ESXi8.0 U2 cannot be done with Advanced Authentication.'
    $options = [System.Management.Automation.Host.ChoiceDescription[]]($yes, $no)
    $tempResult = $host.ui.PromptForChoice($title, $message, $options, 0)
    if ($tempResult -eq 0) { return $true } else { return $false }
}

Function Install-VMwarePowerCli {
    if(Get-Module -ListAvailable -Name "VMware.VimAutomation.Core") {
        Out-Log -Data "VMware.VimAutomation.Core is already installed.`n" -Level 0
    }
    else {
        $response = Set-VmwarePowerCLIPrompt
        if($response) {
            Install-Module -Name VMware.VimAutomation.Core -Allowclobber
            if(Get-Module -ListAvailable -Name "VMware.Vim") {
                if(Get-Module -ListAvailable -Name "VMware.VimAutomation.Cis.Core") {
                    if(Get-Module -ListAvailable -Name "VMware.VimAutomation.Common") {
                        if(Get-Module -ListAvailable -Name "VMware.VimAutomation.Core") {
                            if(Get-Module -ListAvailable -Name "VMware.VimAutomation.Sdk") {
                                Add-ManyToRegistryWriteOver -Path "HKLM:\Software\Classes\CLSID\$Script:VMCORE_INSTALLED" -NameTypeValueArray @(
                                @('(Default)', 'String', 'FC_POWERKIT_INSTALLED_VMCORE') )
                                Out-Log -Data "Successfully installated module VMware.VimAutomation.Core `n" -Level 0
                            }
                            else {
                                Out-Log -Data "Failed to installat module VMware.VimAutomation.Sdk `n" -ForegroundColor Red
                                Exit                              
                            }
                        }
                        else {
                            Out-Log -Data "Failed to installat module VMware.VimAutomation.Core `n" -ForegroundColor Red
                            Exit
                        }
                    }
                    else {
                        Out-Log -Data "Failed to installat module VMware.VimAutomation.Common `n" -ForegroundColor Red
                        Exit
                    }
                }
                else {
                    Out-Log -Data "Failed to installat module VMware.VimAutomation.Cis.Core `n" -ForegroundColor Red
                    Exit
                }
            }
            else {
                Out-Log -Data "Failed to installat module VMware.Vim `n" -ForegroundColor Red
                Exit
            }
        }       
    }
}


Function Install-FCPowerKit {
    Param ( $response0, $response1, $response2 )
    # Encapulate installer logic here
    #--------------------------------------------------------------------------------------------------
    # Globals
    $Script:CLSID = '{A31B8A5E-8B6A-421C-8BB1-F85C9C34E659}'
    $Script:VMCORE_INSTALLED = '{D3624213-6190-4875-9d79-283941a91792}'
    $Script:savedAppxPolicyRegValData = $null
    $cmdletsAppxFileName = Get-CmdletsAppxFileName
    # $restAppxFileName = '.\MRVLFCProviderREST-WindowsServer.appx'
    $Script:appcmd = "$env:SystemRoot\system32\inetsrv\appcmd.exe"
    $Script:netsh = "$env:windir\system32\netsh.exe"
    $Script:Verbose = 1


    #--------------------------------------------------------------------------------------------------
    # User Input
    Test-ExecutionPolicy
    if ((Get-AppxPackage -Name 'MRVLFCProvider') -ne $null) {
       Test-LastExitCode -ExitCode 1 -Message "Could not install PowerKit because it is already installed! Please uninstall the PowerKit first." -ExitScriptIfBadErrorCode
    }
    else {

        $response0 = Set-WinRMConfigurationPrompt

        #$response1 = Install-PowerShellODataPrompt
        #$response2 = Add-AccountToIISPrompt -InstallPSODataResponse $response1
        #$response0 = $WinRMConfiguration
        #$response1 = $Configure_IIS_OData_RESTAPI
        #$response2 = $AddAccountToIIS
        #$cred = $Credential


        #--------------------------------------------------------------------------------------------------
        # Script - Cmdlets
        $Script:CurrentInstallation = 'MRVL FC PowerKit'
        Add-AppxTrustedAppsRegistryKey
        Remove-CLSIDRegistryPaths
        Add-AppxPackageWrapper -AppxFileName $cmdletsAppxFileName
        Add-CLSIDRegistryPathsAndMofCompInstall
        Remove-AppxTrustedAppsRegistryKey
        Copy-DependentLibsAndFilesToWbemPath
        Copy-CmdletsToProgramData
        Add-CmdletsPathToPSModulePath
        Set-AdministratorsPrivilegesOnLocalAgentPropertiesFilePath
        Install-VMwarePowerCli 
        Set-WinRMConfiguration -SetWinRMConfigurationResponse $response0
        Out-Log -Data "Successfully installed MarvellQLogicFCPowerKit.`n" -ForegroundColor Green -Level 0
     
        #--------------------------------------------------------------------------------------------------
        #Import-AllCmdlets
        Out-Log -Data "Note: if this was an upgrade from a previous version, you may need to restart the system for the new provider to be loaded." -ForegroundColor Yellow -Level 0
    }
}

Export-ModuleMember -function Install-FCPowerKit

# SIG # Begin signature block
# MIIouQYJKoZIhvcNAQcCoIIoqjCCKKYCAQExDzANBglghkgBZQMEAgEFADB5Bgor
# BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG
# KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCCSCo3nlfJ7gaQM
# f/8zfUuATR0MqFjULPND5r8ff2V7daCCDcIwggawMIIEmKADAgECAhAIrUCyYNKc
# TJ9ezam9k67ZMA0GCSqGSIb3DQEBDAUAMGIxCzAJBgNVBAYTAlVTMRUwEwYDVQQK
# EwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xITAfBgNV
# BAMTGERpZ2lDZXJ0IFRydXN0ZWQgUm9vdCBHNDAeFw0yMTA0MjkwMDAwMDBaFw0z
# NjA0MjgyMzU5NTlaMGkxCzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5EaWdpQ2VydCwg
# SW5jLjFBMD8GA1UEAxM4RGlnaUNlcnQgVHJ1c3RlZCBHNCBDb2RlIFNpZ25pbmcg
# UlNBNDA5NiBTSEEzODQgMjAyMSBDQTEwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAw
# ggIKAoICAQDVtC9C0CiteLdd1TlZG7GIQvUzjOs9gZdwxbvEhSYwn6SOaNhc9es0
# JAfhS0/TeEP0F9ce2vnS1WcaUk8OoVf8iJnBkcyBAz5NcCRks43iCH00fUyAVxJr
# Q5qZ8sU7H/Lvy0daE6ZMswEgJfMQ04uy+wjwiuCdCcBlp/qYgEk1hz1RGeiQIXhF
# LqGfLOEYwhrMxe6TSXBCMo/7xuoc82VokaJNTIIRSFJo3hC9FFdd6BgTZcV/sk+F
# LEikVoQ11vkunKoAFdE3/hoGlMJ8yOobMubKwvSnowMOdKWvObarYBLj6Na59zHh
# 3K3kGKDYwSNHR7OhD26jq22YBoMbt2pnLdK9RBqSEIGPsDsJ18ebMlrC/2pgVItJ
# wZPt4bRc4G/rJvmM1bL5OBDm6s6R9b7T+2+TYTRcvJNFKIM2KmYoX7BzzosmJQay
# g9Rc9hUZTO1i4F4z8ujo7AqnsAMrkbI2eb73rQgedaZlzLvjSFDzd5Ea/ttQokbI
# YViY9XwCFjyDKK05huzUtw1T0PhH5nUwjewwk3YUpltLXXRhTT8SkXbev1jLchAp
# QfDVxW0mdmgRQRNYmtwmKwH0iU1Z23jPgUo+QEdfyYFQc4UQIyFZYIpkVMHMIRro
# OBl8ZhzNeDhFMJlP/2NPTLuqDQhTQXxYPUez+rbsjDIJAsxsPAxWEQIDAQABo4IB
# WTCCAVUwEgYDVR0TAQH/BAgwBgEB/wIBADAdBgNVHQ4EFgQUaDfg67Y7+F8Rhvv+
# YXsIiGX0TkIwHwYDVR0jBBgwFoAU7NfjgtJxXWRM3y5nP+e6mK4cD08wDgYDVR0P
# AQH/BAQDAgGGMBMGA1UdJQQMMAoGCCsGAQUFBwMDMHcGCCsGAQUFBwEBBGswaTAk
# BggrBgEFBQcwAYYYaHR0cDovL29jc3AuZGlnaWNlcnQuY29tMEEGCCsGAQUFBzAC
# hjVodHRwOi8vY2FjZXJ0cy5kaWdpY2VydC5jb20vRGlnaUNlcnRUcnVzdGVkUm9v
# dEc0LmNydDBDBgNVHR8EPDA6MDigNqA0hjJodHRwOi8vY3JsMy5kaWdpY2VydC5j
# b20vRGlnaUNlcnRUcnVzdGVkUm9vdEc0LmNybDAcBgNVHSAEFTATMAcGBWeBDAED
# MAgGBmeBDAEEATANBgkqhkiG9w0BAQwFAAOCAgEAOiNEPY0Idu6PvDqZ01bgAhql
# +Eg08yy25nRm95RysQDKr2wwJxMSnpBEn0v9nqN8JtU3vDpdSG2V1T9J9Ce7FoFF
# UP2cvbaF4HZ+N3HLIvdaqpDP9ZNq4+sg0dVQeYiaiorBtr2hSBh+3NiAGhEZGM1h
# mYFW9snjdufE5BtfQ/g+lP92OT2e1JnPSt0o618moZVYSNUa/tcnP/2Q0XaG3Ryw
# YFzzDaju4ImhvTnhOE7abrs2nfvlIVNaw8rpavGiPttDuDPITzgUkpn13c5Ubdld
# AhQfQDN8A+KVssIhdXNSy0bYxDQcoqVLjc1vdjcshT8azibpGL6QB7BDf5WIIIJw
# 8MzK7/0pNVwfiThV9zeKiwmhywvpMRr/LhlcOXHhvpynCgbWJme3kuZOX956rEnP
# LqR0kq3bPKSchh/jwVYbKyP/j7XqiHtwa+aguv06P0WmxOgWkVKLQcBIhEuWTatE
# QOON8BUozu3xGFYHKi8QxAwIZDwzj64ojDzLj4gLDb879M4ee47vtevLt/B3E+bn
# KD+sEq6lLyJsQfmCXBVmzGwOysWGw/YmMwwHS6DTBwJqakAwSEs0qFEgu60bhQji
# WQ1tygVQK+pKHJ6l/aCnHwZ05/LWUpD9r4VIIflXO7ScA+2GRfS0YW6/aOImYIbq
# yK+p/pQd52MbOoZWeE4wggcKMIIE8qADAgECAhALi7vYa4DhzUCZEXB/H3UKMA0G
# CSqGSIb3DQEBCwUAMGkxCzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5EaWdpQ2VydCwg
# SW5jLjFBMD8GA1UEAxM4RGlnaUNlcnQgVHJ1c3RlZCBHNCBDb2RlIFNpZ25pbmcg
# UlNBNDA5NiBTSEEzODQgMjAyMSBDQTEwHhcNMjMwODA0MDAwMDAwWhcNMjUwODA5
# MjM1OTU5WjCBkTELMAkGA1UEBhMCVVMxEzARBgNVBAgTCkNhbGlmb3JuaWExFDAS
# BgNVBAcTC1NhbnRhIENsYXJhMSQwIgYDVQQKExtNYXJ2ZWxsIFNlbWljb25kdWN0
# b3IsIEluYy4xCzAJBgNVBAsTAklUMSQwIgYDVQQDExtNYXJ2ZWxsIFNlbWljb25k
# dWN0b3IsIEluYy4wggGiMA0GCSqGSIb3DQEBAQUAA4IBjwAwggGKAoIBgQC4AP3I
# syZbqVD1IOou/bzXF4vv2H0QQcGNwig5VtbuD3kJtEzVLCStPuW/ayDaKJge77+k
# EVBB8rrsXvONwix+NaWzriN6PZfscMVUSWmwmn16BOWbnAIaD6DYgxXH5rj1KYgZ
# WsYe0neONApfJHGoDX4HUzWweUoijTi9bfIVQOKcq+3QAPaWvkMr8o5iLPgieuUr
# ubkPfkO1YOVv2TUxfpzgnhUrW0xq/dozYcxJSmAOkRNNu8/L14ZxKL1P9Voc3Rke
# 3gOX7SK55/L8ho8NyuwmnQe0pror2F7/Ktd9uC/PLsJ0b1PzENAzmWbTiWAN/v5Y
# G4VVODQroUjkd7xkqQM5hEOpd/eKZFBfHr0u+FNFCDtmS9mt8c+nCxI+TVGJpJB8
# T3q7utVWqn8lA5iaaIiZFvjEjmlgI+knZaja4F7uocbPzx1lDZEDT0MYgNsnAT7W
# FEh5Gq9xz1yyrZ8y0SnXAnaQJcP30TbsfI1xPUj8jAxR4YTVA4NpOW6z888CAwEA
# AaOCAgMwggH/MB8GA1UdIwQYMBaAFGg34Ou2O/hfEYb7/mF7CIhl9E5CMB0GA1Ud
# DgQWBBTvp91X8QV1PehmAWhFX18cRwQMSzAOBgNVHQ8BAf8EBAMCB4AwEwYDVR0l
# BAwwCgYIKwYBBQUHAwMwgbUGA1UdHwSBrTCBqjBToFGgT4ZNaHR0cDovL2NybDMu
# ZGlnaWNlcnQuY29tL0RpZ2lDZXJ0VHJ1c3RlZEc0Q29kZVNpZ25pbmdSU0E0MDk2
# U0hBMzg0MjAyMUNBMS5jcmwwU6BRoE+GTWh0dHA6Ly9jcmw0LmRpZ2ljZXJ0LmNv
# bS9EaWdpQ2VydFRydXN0ZWRHNENvZGVTaWduaW5nUlNBNDA5NlNIQTM4NDIwMjFD
# QTEuY3JsMD4GA1UdIAQ3MDUwMwYGZ4EMAQQBMCkwJwYIKwYBBQUHAgEWG2h0dHA6
# Ly93d3cuZGlnaWNlcnQuY29tL0NQUzCBlAYIKwYBBQUHAQEEgYcwgYQwJAYIKwYB
# BQUHMAGGGGh0dHA6Ly9vY3NwLmRpZ2ljZXJ0LmNvbTBcBggrBgEFBQcwAoZQaHR0
# cDovL2NhY2VydHMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0VHJ1c3RlZEc0Q29kZVNp
# Z25pbmdSU0E0MDk2U0hBMzg0MjAyMUNBMS5jcnQwCQYDVR0TBAIwADANBgkqhkiG
# 9w0BAQsFAAOCAgEApMzvSUmDDcq+1ybCXokN7/HCKR09P34wfmlU0vebCBz7BLjo
# PV1qHhrkjgNB4z2wEns+YRI5h0mqTg0ho7hMmR82CstZaETNnNNWFwZVy+Mcm2ld
# fu48pCsMNXoHo2oIGMzjhRl+2b2U2OwTg9sgY+kBmBUx6oZpNnY55MH/5nY0gB/F
# d7BmoRsOH0UduSVjI+s2Bjc6H97qmE+TmSCwrxXBvPkzNhGuLjD0W9936Hlt9x3Z
# w9IeRlJWOKit2gwi4xhZWyKWq/hXnJs0mXw7K1EkkXnlaBro5FXUuggIdok9Vr4m
# pgMwa5pfBymFVfTzkX5krRqe7IFTG4bR5B4GlkYtXKJ6xzpI5Tl8zPqYxP9nt9gC
# YEiTbgVVzKBWY7cyoUpKCH95NLUqMpM3dg17oM5ZJCWMio4RhUBik/JU9Cdm7FZp
# r9siDSjoS0qDoOvu5c+DH+Ty7CoZvl3LRPh+CBLho4GEyJEkzRvFQ01wR4lvTcNV
# 9zUcwGML5Ww8+ZNvDh73iq6KIdQwkTmbNCzDYx4Jmws4UtYLk9q2d/bNZP4IawS8
# dgzyKCjCfUScm+6jMkOFpgshV6JgeaxQg45S6vGTxfFGpGeOmaNnBwg9/8TNeH79
# S5ATYMey0lq6ryTn3MhoimrDZgZubtPyVzBXo/3XBySbTNJtkK0WkRJjNcMxghpN
# MIIaSQIBATB9MGkxCzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5EaWdpQ2VydCwgSW5j
# LjFBMD8GA1UEAxM4RGlnaUNlcnQgVHJ1c3RlZCBHNCBDb2RlIFNpZ25pbmcgUlNB
# NDA5NiBTSEEzODQgMjAyMSBDQTECEAuLu9hrgOHNQJkRcH8fdQowDQYJYIZIAWUD
# BAIBBQCggeQwGQYJKoZIhvcNAQkDMQwGCisGAQQBgjcCAQQwHAYKKwYBBAGCNwIB
# CzEOMAwGCisGAQQBgjcCARUwLwYJKoZIhvcNAQkEMSIEIKBtDJWkSDAqlIl+jyBl
# P+heixNi40xMCXg1JE4Fk9tgMHgGCisGAQQBgjcCAQwxajBooEyASgBNAGEAcgB2
# AGUAbABsACAAUQBMAG8AZwBpAGMAIABGAGkAYgByAGUAIABDAGgAYQBuAG4AZQBs
# ACAAUABvAHcAZQByAEsAaQB0oRiAFmh0dHA6Ly93d3cubWFydmVsbC5jb20wDQYJ
# KoZIhvcNAQEBBQAEggGAfnFNNVGwaIte9X42MxXtrs2fms2u6LMuF/IQEH8Avin0
# ea01eOuhyfi9EhMKpUBnxXI+7UZr3zRbNZMqrlaAIVdkJIDhpt2q1vNjOrFzsEAw
# 1tfef/AdqxBXsFX7M80vYX7x0dMgC+X0ch9bKtDP11QSHlX0oc0A/u9YuVI7O3nN
# uaHrNMC5Q2dL7yNu/ItF070YhN7ftJ/4X/edH0LqUF1FeWF/oqiE9LFIa9weQHHm
# sG0cC9YBWtwtYVMwSD1s301t1fJt5Y5SXXMWenD0LXEXkiGvDW5VYNJaEfICSnNu
# It1TozvaKMX06fEu6j+8N261svmYWUT5j2Qru5zefa/625KtNV5CCOKVFVlzWJjo
# 3JlFNmi24fHKu9fIGO1DIJG2hadeCBtAnbKXdwxsRU2AaorZduMuvJSG/rVuQEFw
# EWAUZSucjxdBDZEKG5my+SbC1ZZdnOu6Rf357TJ9I/IOUasJBOH8jDiE1xtMaRn0
# D5R3vTk3uty8Zzptpe2loYIXOjCCFzYGCisGAQQBgjcDAwExghcmMIIXIgYJKoZI
# hvcNAQcCoIIXEzCCFw8CAQMxDzANBglghkgBZQMEAgEFADB4BgsqhkiG9w0BCRAB
# BKBpBGcwZQIBAQYJYIZIAYb9bAcBMDEwDQYJYIZIAWUDBAIBBQAEIIRdRBjXebkm
# hh66qINKKbgLZUqFzWdq90Qq13CPKf/kAhEAz96LmuaxhMlgQwhUDXdlvBgPMjAy
# NTA2MjUxMDI3MTJaoIITAzCCBrwwggSkoAMCAQICEAuuZrxaun+Vh8b56QTjMwQw
# DQYJKoZIhvcNAQELBQAwYzELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDkRpZ2lDZXJ0
# LCBJbmMuMTswOQYDVQQDEzJEaWdpQ2VydCBUcnVzdGVkIEc0IFJTQTQwOTYgU0hB
# MjU2IFRpbWVTdGFtcGluZyBDQTAeFw0yNDA5MjYwMDAwMDBaFw0zNTExMjUyMzU5
# NTlaMEIxCzAJBgNVBAYTAlVTMREwDwYDVQQKEwhEaWdpQ2VydDEgMB4GA1UEAxMX
# RGlnaUNlcnQgVGltZXN0YW1wIDIwMjQwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAw
# ggIKAoICAQC+anOf9pUhq5Ywultt5lmjtej9kR8YxIg7apnjpcH9CjAgQxK+CMR0
# Rne/i+utMeV5bUlYYSuuM4vQngvQepVHVzNLO9RDnEXvPghCaft0djvKKO+hDu6O
# bS7rJcXa/UKvNminKQPTv/1+kBPgHGlP28mgmoCw/xi6FG9+Un1h4eN6zh926SxM
# e6We2r1Z6VFZj75MU/HNmtsgtFjKfITLutLWUdAoWle+jYZ49+wxGE1/UXjWfISD
# mHuI5e/6+NfQrxGFSKx+rDdNMsePW6FLrphfYtk/FLihp/feun0eV+pIF496OVh4
# R1TvjQYpAztJpVIfdNsEvxHofBf1BWkadc+Up0Th8EifkEEWdX4rA/FE1Q0rqViT
# bLVZIqi6viEk3RIySho1XyHLIAOJfXG5PEppc3XYeBH7xa6VTZ3rOHNeiYnY+V4j
# 1XbJ+Z9dI8ZhqcaDHOoj5KGg4YuiYx3eYm33aebsyF6eD9MF5IDbPgjvwmnAalNE
# eJPvIeoGJXaeBQjIK13SlnzODdLtuThALhGtyconcVuPI8AaiCaiJnfdzUcb3dWn
# qUnjXkRFwLtsVAxFvGqsxUA2Jq/WTjbnNjIUzIs3ITVC6VBKAOlb2u29Vwgfta8b
# 2ypi6n2PzP0nVepsFk8nlcuWfyZLzBaZ0MucEdeBiXL+nUOGhCjl+QIDAQABo4IB
# izCCAYcwDgYDVR0PAQH/BAQDAgeAMAwGA1UdEwEB/wQCMAAwFgYDVR0lAQH/BAww
# CgYIKwYBBQUHAwgwIAYDVR0gBBkwFzAIBgZngQwBBAIwCwYJYIZIAYb9bAcBMB8G
# A1UdIwQYMBaAFLoW2W1NhS9zKXaaL3WMaiCPnshvMB0GA1UdDgQWBBSfVywDdw4o
# FZBmpWNe7k+SH3agWzBaBgNVHR8EUzBRME+gTaBLhklodHRwOi8vY3JsMy5kaWdp
# Y2VydC5jb20vRGlnaUNlcnRUcnVzdGVkRzRSU0E0MDk2U0hBMjU2VGltZVN0YW1w
# aW5nQ0EuY3JsMIGQBggrBgEFBQcBAQSBgzCBgDAkBggrBgEFBQcwAYYYaHR0cDov
# L29jc3AuZGlnaWNlcnQuY29tMFgGCCsGAQUFBzAChkxodHRwOi8vY2FjZXJ0cy5k
# aWdpY2VydC5jb20vRGlnaUNlcnRUcnVzdGVkRzRSU0E0MDk2U0hBMjU2VGltZVN0
# YW1waW5nQ0EuY3J0MA0GCSqGSIb3DQEBCwUAA4ICAQA9rR4fdplb4ziEEkfZQ5H2
# EdubTggd0ShPz9Pce4FLJl6reNKLkZd5Y/vEIqFWKt4oKcKz7wZmXa5VgW9B76k9
# NJxUl4JlKwyjUkKhk3aYx7D8vi2mpU1tKlY71AYXB8wTLrQeh83pXnWwwsxc1Mt+
# FWqz57yFq6laICtKjPICYYf/qgxACHTvypGHrC8k1TqCeHk6u4I/VBQC9VK7iSpU
# 5wlWjNlHlFFv/M93748YTeoXU/fFa9hWJQkuzG2+B7+bMDvmgF8VlJt1qQcl7YFU
# MYgZU1WM6nyw23vT6QSgwX5Pq2m0xQ2V6FJHu8z4LXe/371k5QrN9FQBhLLISZi2
# yemW0P8ZZfx4zvSWzVXpAb9k4Hpvpi6bUe8iK6WonUSV6yPlMwerwJZP/Gtbu3CK
# ldMnn+LmmRTkTXpFIEB06nXZrDwhCGED+8RsWQSIXZpuG4WLFQOhtloDRWGoCwwc
# 6ZpPddOFkM2LlTbMcqFSzm4cd0boGhBq7vkqI1uHRz6Fq1IX7TaRQuR+0BGOzISk
# cqwXu7nMpFu3mgrlgbAW+BzikRVQ3K2YHcGkiKjA4gi4OA/kz1YCsdhIBHXqBzR0
# /Zd2QwQ/l4Gxftt/8wY3grcc/nS//TVkej9nmUYu83BDtccHHXKibMs/yXHhDXNk
# oPIdynhVAku7aRZOwqw6pDCCBq4wggSWoAMCAQICEAc2N7ckVHzYR6z9KGYqXlsw
# DQYJKoZIhvcNAQELBQAwYjELMAkGA1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0
# IEluYzEZMBcGA1UECxMQd3d3LmRpZ2ljZXJ0LmNvbTEhMB8GA1UEAxMYRGlnaUNl
# cnQgVHJ1c3RlZCBSb290IEc0MB4XDTIyMDMyMzAwMDAwMFoXDTM3MDMyMjIzNTk1
# OVowYzELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDkRpZ2lDZXJ0LCBJbmMuMTswOQYD
# VQQDEzJEaWdpQ2VydCBUcnVzdGVkIEc0IFJTQTQwOTYgU0hBMjU2IFRpbWVTdGFt
# cGluZyBDQTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMaGNQZJs8E9
# cklRVcclA8TykTepl1Gh1tKD0Z5Mom2gsMyD+Vr2EaFEFUJfpIjzaPp985yJC3+d
# H54PMx9QEwsmc5Zt+FeoAn39Q7SE2hHxc7Gz7iuAhIoiGN/r2j3EF3+rGSs+Qtxn
# jupRPfDWVtTnKC3r07G1decfBmWNlCnT2exp39mQh0YAe9tEQYncfGpXevA3eZ9d
# rMvohGS0UvJ2R/dhgxndX7RUCyFobjchu0CsX7LeSn3O9TkSZ+8OpWNs5KbFHc02
# DVzV5huowWR0QKfAcsW6Th+xtVhNef7Xj3OTrCw54qVI1vCwMROpVymWJy71h6aP
# TnYVVSZwmCZ/oBpHIEPjQ2OAe3VuJyWQmDo4EbP29p7mO1vsgd4iFNmCKseSv6De
# 4z6ic/rnH1pslPJSlRErWHRAKKtzQ87fSqEcazjFKfPKqpZzQmiftkaznTqj1QPg
# v/CiPMpC3BhIfxQ0z9JMq++bPf4OuGQq+nUoJEHtQr8FnGZJUlD0UfM2SU2LINIs
# VzV5K6jzRWC8I41Y99xh3pP+OcD5sjClTNfpmEpYPtMDiP6zj9NeS3YSUZPJjAw7
# W4oiqMEmCPkUEBIDfV8ju2TjY+Cm4T72wnSyPx4JduyrXUZ14mCjWAkBKAAOhFTu
# zuldyF4wEr1GnrXTdrnSDmuZDNIztM2xAgMBAAGjggFdMIIBWTASBgNVHRMBAf8E
# CDAGAQH/AgEAMB0GA1UdDgQWBBS6FtltTYUvcyl2mi91jGogj57IbzAfBgNVHSME
# GDAWgBTs1+OC0nFdZEzfLmc/57qYrhwPTzAOBgNVHQ8BAf8EBAMCAYYwEwYDVR0l
# BAwwCgYIKwYBBQUHAwgwdwYIKwYBBQUHAQEEazBpMCQGCCsGAQUFBzABhhhodHRw
# Oi8vb2NzcC5kaWdpY2VydC5jb20wQQYIKwYBBQUHMAKGNWh0dHA6Ly9jYWNlcnRz
# LmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydFRydXN0ZWRSb290RzQuY3J0MEMGA1UdHwQ8
# MDowOKA2oDSGMmh0dHA6Ly9jcmwzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydFRydXN0
# ZWRSb290RzQuY3JsMCAGA1UdIAQZMBcwCAYGZ4EMAQQCMAsGCWCGSAGG/WwHATAN
# BgkqhkiG9w0BAQsFAAOCAgEAfVmOwJO2b5ipRCIBfmbW2CFC4bAYLhBNE88wU86/
# GPvHUF3iSyn7cIoNqilp/GnBzx0H6T5gyNgL5Vxb122H+oQgJTQxZ822EpZvxFBM
# Yh0MCIKoFr2pVs8Vc40BIiXOlWk/R3f7cnQU1/+rT4osequFzUNf7WC2qk+RZp4s
# nuCKrOX9jLxkJodskr2dfNBwCnzvqLx1T7pa96kQsl3p/yhUifDVinF2ZdrM8HKj
# I/rAJ4JErpknG6skHibBt94q6/aesXmZgaNWhqsKRcnfxI2g55j7+6adcq/Ex8HB
# anHZxhOACcS2n82HhyS7T6NJuXdmkfFynOlLAlKnN36TU6w7HQhJD5TNOXrd/yVj
# mScsPT9rp/Fmw0HNT7ZAmyEhQNC3EyTN3B14OuSereU0cZLXJmvkOHOrpgFPvT87
# eK1MrfvElXvtCl8zOYdBeHo46Zzh3SP9HSjTx/no8Zhf+yvYfvJGnXUsHicsJttv
# FXseGYs2uJPU5vIXmVnKcPA3v5gA3yAWTyf7YGcWoWa63VXAOimGsJigK+2VQbc6
# 1RWYMbRiCQ8KvYHZE/6/pNHzV9m8BPqC3jLfBInwAM1dwvnQI38AC+R2AibZ8GV2
# QqYphwlHK+Z/GqSFD/yYlvZVVCsfgPrA8g4r5db7qS9EFUrnEw4d2zc4GqEr9u3W
# fPwwggWNMIIEdaADAgECAhAOmxiO+dAt5+/bUOIIQBhaMA0GCSqGSIb3DQEBDAUA
# MGUxCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsT
# EHd3dy5kaWdpY2VydC5jb20xJDAiBgNVBAMTG0RpZ2lDZXJ0IEFzc3VyZWQgSUQg
# Um9vdCBDQTAeFw0yMjA4MDEwMDAwMDBaFw0zMTExMDkyMzU5NTlaMGIxCzAJBgNV
# BAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdp
# Y2VydC5jb20xITAfBgNVBAMTGERpZ2lDZXJ0IFRydXN0ZWQgUm9vdCBHNDCCAiIw
# DQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAL/mkHNo3rvkXUo8MCIwaTPswqcl
# LskhPfKK2FnC4SmnPVirdprNrnsbhA3EMB/zG6Q4FutWxpdtHauyefLKEdLkX9YF
# PFIPUh/GnhWlfr6fqVcWWVVyr2iTcMKyunWZanMylNEQRBAu34LzB4TmdDttceIt
# DBvuINXJIB1jKS3O7F5OyJP4IWGbNOsFxl7sWxq868nPzaw0QF+xembud8hIqGZX
# V59UWI4MK7dPpzDZVu7Ke13jrclPXuU15zHL2pNe3I6PgNq2kZhAkHnDeMe2scS1
# ahg4AxCN2NQ3pC4FfYj1gj4QkXCrVYJBMtfbBHMqbpEBfCFM1LyuGwN1XXhm2Tox
# RJozQL8I11pJpMLmqaBn3aQnvKFPObURWBf3JFxGj2T3wWmIdph2PVldQnaHiZdp
# ekjw4KISG2aadMreSx7nDmOu5tTvkpI6nj3cAORFJYm2mkQZK37AlLTSYW3rM9nF
# 30sEAMx9HJXDj/chsrIRt7t/8tWMcCxBYKqxYxhElRp2Yn72gLD76GSmM9GJB+G9
# t+ZDpBi4pncB4Q+UDCEdslQpJYls5Q5SUUd0viastkF13nqsX40/ybzTQRESW+UQ
# UOsxxcpyFiIJ33xMdT9j7CFfxCBRa2+xq4aLT8LWRV+dIPyhHsXAj6KxfgommfXk
# aS+YHS312amyHeUbAgMBAAGjggE6MIIBNjAPBgNVHRMBAf8EBTADAQH/MB0GA1Ud
# DgQWBBTs1+OC0nFdZEzfLmc/57qYrhwPTzAfBgNVHSMEGDAWgBRF66Kv9JLLgjEt
# UYunpyGd823IDzAOBgNVHQ8BAf8EBAMCAYYweQYIKwYBBQUHAQEEbTBrMCQGCCsG
# AQUFBzABhhhodHRwOi8vb2NzcC5kaWdpY2VydC5jb20wQwYIKwYBBQUHMAKGN2h0
# dHA6Ly9jYWNlcnRzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydEFzc3VyZWRJRFJvb3RD
# QS5jcnQwRQYDVR0fBD4wPDA6oDigNoY0aHR0cDovL2NybDMuZGlnaWNlcnQuY29t
# L0RpZ2lDZXJ0QXNzdXJlZElEUm9vdENBLmNybDARBgNVHSAECjAIMAYGBFUdIAAw
# DQYJKoZIhvcNAQEMBQADggEBAHCgv0NcVec4X6CjdBs9thbX979XB72arKGHLOyF
# XqkauyL4hxppVCLtpIh3bb0aFPQTSnovLbc47/T/gLn4offyct4kvFIDyE7QKt76
# LVbP+fT3rDB6mouyXtTP0UNEm0Mh65ZyoUi0mcudT6cGAxN3J0TU53/oWajwvy8L
# punyNDzs9wPHh6jSTEAZNUZqaVSwuKFWjuyk1T3osdz9HNj0d1pcVIxv76FQPfx2
# CWiEn2/K2yCNNWAcAgPLILCsWKAOQGPFmCLBsln1VWvPJ6tsds5vIy30fnFqI2si
# /xK4VC0nftg62fC2h5b9W9FcrBjDTZ9ztwGpn1eqXijiuZQxggN2MIIDcgIBATB3
# MGMxCzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5EaWdpQ2VydCwgSW5jLjE7MDkGA1UE
# AxMyRGlnaUNlcnQgVHJ1c3RlZCBHNCBSU0E0MDk2IFNIQTI1NiBUaW1lU3RhbXBp
# bmcgQ0ECEAuuZrxaun+Vh8b56QTjMwQwDQYJYIZIAWUDBAIBBQCggdEwGgYJKoZI
# hvcNAQkDMQ0GCyqGSIb3DQEJEAEEMBwGCSqGSIb3DQEJBTEPFw0yNTA2MjUxMDI3
# MTJaMCsGCyqGSIb3DQEJEAIMMRwwGjAYMBYEFNvThe5i29I+e+T2cUhQhyTVhltF
# MC8GCSqGSIb3DQEJBDEiBCDljxypFzW+MpsZgirNmTqdIdY2N0ti1oVzNU2PmZqp
# 0zA3BgsqhkiG9w0BCRACLzEoMCYwJDAiBCB2dp+o8mMvH0MLOiMwrtZWdf7Xc9sF
# 1mW5BZOYQ4+a2zANBgkqhkiG9w0BAQEFAASCAgBCMy3HesJzr25e7QTBKoCgMQ94
# qIVDVSJy37gJrCLgoFZm5MrdUsHnSHS8O6JQLN+s/So34aORAmznLusojJ7OY/xc
# CJCUmxF+n1+1d32DQyqPmjlgPqvaiSPW9UmMelLe/Pmha1MOkgllp3PUgCiGJGhA
# 8U58D4f0FvgUJz5DgwXReyIoNgy08Q55jrqChemujxc6bUc/yM98BA2/hc2kN3kP
# 6GDxXx5Kn/u8exbnO2r/108ja9CE39/A7BqyR5psFeeyCxph7alFFMdYt6feELkG
# 6dBHnW31/Ca5y8+Q7fPY0L0FicVpZ76PVZeeogG8mm2X/5iJD7JUiQaHMCS2nqTb
# PDXDfazxiM3lYSZtaW5wLf04whiznLZPA/Nr4o7mu54ke4jp6PPQTsH7JwoYfMKI
# rDg49Ez/xQM8PRV3dR4hK58LB4In4zuGZ2zj2ixLl8+ny0Rxe7unu2axCbb8WPWr
# PSyrKmJmYJHgZ//kAZOzFgdyoOBdSXZSMwG1419wXoS1KZU5EEKXbsggXLf0wfhD
# BwDVLIjwFbvJnqK6dG4mO2nIYMBBUovZFZqiQG9kqP6XeBXTkyL3WH2Q4dEMERDM
# Ha07qh3K2gWAzBKALByEbYPb7Vhd/bKNfVq81d1aGx2tBHmvL5KCMQjsL6QUg7Z+
# NNCYWxN6iinn4uPemg==
# SIG # End signature block