Public/Add-LnvSUCommandLine.ps1

<#
  .SYNOPSIS
  Configure System Update AdminCommandLine
 
  .DESCRIPTION
  Run Script to set Admin command line Windows Registry settings for Lenovo System Update
 
  .PARAMETER Search
  Mandatory: True
  Data type: String
  Must be one of the following values [C, R, A]
 
  .PARAMETER Action
  Mandatory: True
  Data type: String
  Must be one of the following values [DOWNLOAD, INSTALL, LIST]
 
  .PARAMETER IncludereBootPackages
  Mandatory : False
  Data type: String
  Must be one of the following values [1, 3, 4, 5], or multiple values separated with a comma.
  1 = Update itself will force a reboot
  3 = Requires a reboot
  4 = Update itself will shut down the system after install
  5 = Within a 5 minute delay after installing one or more of these updates, will force a reboot
 
  .PARAMETER PackageTypes
  Mandatory : False
  Data type: String
  Must be one of the following values [0, 1, 2, 3, 4], or multiple values separated with a comma
  0 = Other
  1 = Application
  2 = Driver
  3 = BIOS
  4 = Firmware
 
  .PARAMETER NoReboot
  Mandatory : False
  Data type: Switch
  In normal operation, if System Update installs one or more Reboot Type 3 (Requires reboot)
  updates, it will initiate a reboot after the last installation completes. To suppress this
  reboot simply specify this parameter. This parameter only has an effect for Reboot Type 3
  packages. For Reboot Type 1 and 4, the reboot or shutdown is orchestrated by the update
  itself and is not under the control of System Update. For Reboot Type 5 packages a reboot
  must be executed immediately after the update and is forced by System Update.
 
  .PARAMETER NoIcon
  Mandatory : False
  Data type: Switch
  Suppresses the balloon tooltip from the notification area of the system tray.
 
  .PARAMETER RebootPrompt
  Mandatory : False
  Data type: Switch
  Forces the display of the reboot prompt dialog after installing updates
  that will require a restart. This parameter is only applicable when the -noicon
  parameter is used and the -noreboot parameter is NOT used. In this scenario,
  if the -rebootprompt parameter were not used, the system could be rebooted without
  any warning to the user.
 
  .PARAMETER Repository
  Mandatory : False
  Data type: String
  Must be a local folder path, a UNC file share path, or a URL to a web-hosted repository.
  If not specified, System Update will use the Lenovo Support servers.
 
  .PARAMETER ExportToWmi
  Mandatory : False
  Data type: Switch
  Causes System Update to store update history data in a WMI table:
  Root\Lenovo\Lenovo_Updates\
 
 
  .INPUTS
 
  .OUTPUTS
 
  .NOTES
  Read messages to determine the result of the script working.
#>

function Add-LnvSUCommandLine {
  param (
    [Parameter(Mandatory = $True)]
    [ValidateSet("C","R","A")]
    [string]$Search,

    [Parameter(Mandatory = $True)]
    [ValidateSet("DOWNLOAD","INSTALL","LIST")]
    [string]$Action,

    [Parameter(Mandatory = $False)]
    [string]$IncludeRebootPackages,

    [Parameter(Mandatory = $False)]
    [string]$PackageTypes,

    [Parameter(Mandatory = $False)]
    [switch]$NoReboot,

    [Parameter(Mandatory = $False)]
    [switch]$NoIcon,

    [Parameter(Mandatory = $False)]
    [switch]$RebootPrompt,

    [Parameter(Mandatory = $False)]
    [string]$Repository,

    [Parameter(Mandatory = $False)]
    [switch]$ExportToWmi
  )

  #region Common script block

  class RegistryEntry {
    [string]$Key
    [string]$Name
    [string]$ValueType
    [SimpleValue[]]$Value

    RegistryEntry(
      [string]$key,
      [string]$name,
      [string]$valueType,
      [SimpleValue[]]$value
    ) {
      $this.Key = $key
      $this.Name = $name
      $this.ValueType = $valueType
      $this.Value = $value
    }

    [String] ToString() {
      return $this.Key + " " + $this.Name
    }
  }

  class SimpleValue {
    [string]$Value

    SimpleValue(
      [string]$value
    ) {
      $this.Value = $value
    }

    [String] ToString() {
      return $this.Value
    }
  }

  class ParameterValue:SimpleValue {
    [string]$ParameterName

    ParameterValue(
      [string]$value,
      [string]$parameterName
    ):base($value) {
      $this.ParameterName = $parameterName
    }

    [String] ToString() {
      if ($this.Value) {
        return $this.ParameterName + " " + $this.Value
      }
      else {
        return $null
      }
    }
  }

  #endregion

  #region Parameters validation
  function Confirm-ParameterStringSet {
    [CmdletBinding()]
    param(
      [Parameter(Mandatory = $True)]
      [string[]]$AllowedValues,
      [Parameter(Mandatory = $True)]
      [AllowEmptyString()]
      [string]$Value,
      [Parameter(Mandatory = $True)]
      [string]$ErrorMessage
    )

    if ($Value) {
      $result = $AllowedValues.Contains($Value)

      if ($result -ne $True) {
        Write-LogError $ErrorMessage

        return
      }
    }
  }

  function Confirm-ParameterPattern {
    [CmdletBinding()]
    param(
      [Parameter(Mandatory = $True)]
      [string]$RegEx,
      [Parameter(Mandatory = $False)]
      [AllowEmptyString()]
      [string]$Value,
      [Parameter(Mandatory = $False)]
      [string]$ErrorMessage
    )

    if ($Value) {
      $result = $Value -match $RegEx

      if ($result -ne $True) {
        Write-LogError $ErrorMessage

        return
      }
    }
  }
  #endregion

  #region Registry
  function Add-RegistryEntry {
    [CmdletBinding()]
    param(
      [Parameter(Mandatory = $True)]
      [RegistryEntry]$RegistryEntry
    )

    $resultValues = @()

    foreach ($registryValue in $RegistryEntry.Value) {
      $tempValue = $registryValue.ToString()
      if ($tempValue) {
        $resultValues += $tempValue
      }
    }

    $valueString = $resultValues -join " "

    try {
      $keyExists = Confirm-RegistryKey -Key $RegistryEntry.Key
      if ($keyExists -ne $True) {
        New-Item $RegistryEntry.Key -Force -ErrorAction Stop | New-ItemProperty -Name $RegistryEntry.Name -Value $valueString -PropertyType $RegistryEntry.ValueType -Force -ErrorAction Stop | Out-Null
      }
      else {
        $registryKey = Get-Item $RegistryEntry.Key -ErrorAction Stop
        $registryKey | New-ItemProperty -Name $RegistryEntry.Name -Value $valueString -PropertyType $RegistryEntry.ValueType -Force -ErrorAction Stop | Out-Null
      }
    }
    catch {
      Write-LogError "An error occured while adding the $($RegistryEntry.Name) registryEntry:`n $_"
      return
    }
  }

  function Confirm-RegistryKey {
    [CmdletBinding()]
    param(
      [Parameter(Mandatory = $True)]
      [string]$Key
    )
    if (Get-Item $Key  -ErrorAction SilentlyContinue ) {
      return $True
    }
    else {
      return $False
    }
  }
  #endregion

  #region Messages
  function Write-LogError {
    Param(
      [Parameter(Mandatory = $True)]
      [string]$Message
    )

    Write-Output -InputObject ("[LDMM_ERROR_$((Get-Date).ToString("yyyy-MM-ddTHH:mm:ss"))]: $Message")
  }

  function Write-LogInformation {
    Param(
      [Parameter(Mandatory = $True)]
      [string]$Message
    )

    Write-Output -InputObject ("[LDMM_INFORMATION_$((Get-Date).ToString("yyyy-MM-ddTHH:mm:ss"))]: $Message")
  }
  #endregion

  function Test-ApplicationPath {
    [CmdletBinding()]
    param(
      [Parameter(Mandatory = $True)]
      [string]$Appx64Path,

      [Parameter(Mandatory = $True)]
      [string]$Appx86Path
    )

    $x64AppExists = Test-Path -Path $Appx64Path
    $x86AppExists = Test-Path -Path $Appx86Path

    if ($x64AppExists -ne $True -and $x86AppExists -ne $True) {
      return $False
    }

    return $True;
  }
  #endregion

  #region Custom script block
  function Confirm-Parameter{
    Confirm-ParameterStringSet -AllowedValues @('C', 'R', 'A') -Value $Search -ErrorMessage " Search param must be one of the following values [C, R, A]"
    Confirm-ParameterStringSet -AllowedValues @('DOWNLOAD', 'LIST', 'INSTALL') -Value $Action -ErrorMessage "Action param must be one of the following values [DOWNLOAD, LIST, INSTALL]"
    Confirm-ParameterPattern   -RegEx '^(1|3|4|5)?(?: *, *(1|3|4|5))*$' -Value $IncludeRebootPackages -ErrorMessage "IncludereBootPackages param must be one of the following values [1, 3, 4, 5], or multiple values separated with a comma"
    Confirm-ParameterPattern -RegEx '^[1-4](,[1-4])*$' -Value $PackageTypes -ErrorMessage "PackageTypes param must be one of the following values [0, 1, 2, 3, 4], or multiple values separated with a comma"
    Confirm-ParameterPattern -RegEx "^((?:~?\/)|(?:(?:\\\\\?\\)?[a-zA-Z]+\:))(?:\/?(.*))?$|\\\\[a-zA-Z0-9\.\-_]{1,}(\\[a-zA-Z0-9\-_]{1,}){1,}[\$]{0,1}|(http:\/\/www\.|https:\/\/www\.|http:\/\/|https:\/\/)?[a-z0-9]+([\-\.]{1}[a-z0-9]+)*\.[a-z]{2,5}(:[0-9]{1,5})?(\/.*)?$" -Value $Repository -ErrorMessage "Repository param must be a local folder path, a UNC file share path, or a URL to a web-hosted repository"
  }

  Write-LogInformation "Script execution started."

  try {
    Confirm-Parameter

    $x64AppPath = Join-Path -Path $Env:Programfiles -ChildPath "Lenovo\System Update\tvsu.exe"
    $x86AppPath = Join-Path -Path ${Env:Programfiles(x86)} -ChildPath "Lenovo\System Update\tvsu.exe"

    $appPath = Test-ApplicationPath -Appx64Path $x64AppPath -Appx86Path $x86AppPath

    if (!$appPath) {
      Write-LogError "Lenovo System Update was not found at the default installation path."

      return
    }

    $startValue = [SimpleValue]::new('/CM')
    $searchValue = [ParameterValue]::new( $Search, '-search')
    $actionValue = [ParameterValue]::new( $Action, '-action')
    $IncludeRebootPackagesValue = [ParameterValue]::new( $IncludeRebootPackages, '-includerebootpackages')
    $packageTypesValue = [ParameterValue]::new( $PackageTypes, '-packagetypes')
    $noRebootValue = ''
    if ($NoReboot) {
      $noRebootValue = "-noreboot"
    }
    $noIconValue = ''
    if ($NoIcon) {
      $noIconValue = "-noicon"
    }

    $noLicenseValue = "-nolicense"

    $rebootPromtValue = ''
    if ($RebootPrompt) {
      $rebootPromtValue = "-rebootprompt"
    }

    $repositoryValue = [ParameterValue]::new( $Repository, '-repository')

    $exportToWmiValue = ''
    if ($ExportToWmi) {
      $exportToWmiValue = "-exporttowmi"
    }

    $registryEntry = [RegistryEntry]::new(
      'HKLM:\SOFTWARE\Policies\Lenovo\System Update\UserSettings\General',
      'AdminCommandLine',
      'String',
      @($startValue,
        $searchValue,
        $actionValue,
        $IncludeRebootPackagesValue,
        $packageTypesValue,
        $noRebootValue,
        $noIconValue,
        $noLicenseValue,
        $rebootPromtValue,
        $repositoryValue,
        $exportToWmiValue))

    Add-RegistryEntry $registryEntry

    Write-LogInformation "Script execution finished."
  }
  catch {
    Write-LogError "Unexpected error occurred:`n $_"

    return
  }
}
#endregion
# SIG # Begin signature block
# MIItfQYJKoZIhvcNAQcCoIItbjCCLWoCAQExCzAJBgUrDgMCGgUAMGkGCisGAQQB
# gjcCAQSgWzBZMDQGCisGAQQBgjcCAR4wJgIDAQAABBAfzDtgWUsITrck0sYpfvNR
# AgEAAgEAAgEAAgEAAgEAMCEwCQYFKw4DAhoFAAQUwJKTDe3cg05MYdiZVyEU5oL2
# FhaggialMIIFjTCCBHWgAwIBAgIQDpsYjvnQLefv21DiCEAYWjANBgkqhkiG9w0B
# AQwFADBlMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYD
# VQQLExB3d3cuZGlnaWNlcnQuY29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVk
# IElEIFJvb3QgQ0EwHhcNMjIwODAxMDAwMDAwWhcNMzExMTA5MjM1OTU5WjBiMQsw
# CQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cu
# ZGlnaWNlcnQuY29tMSEwHwYDVQQDExhEaWdpQ2VydCBUcnVzdGVkIFJvb3QgRzQw
# ggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQC/5pBzaN675F1KPDAiMGkz
# 7MKnJS7JIT3yithZwuEppz1Yq3aaza57G4QNxDAf8xukOBbrVsaXbR2rsnnyyhHS
# 5F/WBTxSD1Ifxp4VpX6+n6lXFllVcq9ok3DCsrp1mWpzMpTREEQQLt+C8weE5nQ7
# bXHiLQwb7iDVySAdYyktzuxeTsiT+CFhmzTrBcZe7FsavOvJz82sNEBfsXpm7nfI
# SKhmV1efVFiODCu3T6cw2Vbuyntd463JT17lNecxy9qTXtyOj4DatpGYQJB5w3jH
# trHEtWoYOAMQjdjUN6QuBX2I9YI+EJFwq1WCQTLX2wRzKm6RAXwhTNS8rhsDdV14
# Ztk6MUSaM0C/CNdaSaTC5qmgZ92kJ7yhTzm1EVgX9yRcRo9k98FpiHaYdj1ZXUJ2
# h4mXaXpI8OCiEhtmmnTK3kse5w5jrubU75KSOp493ADkRSWJtppEGSt+wJS00mFt
# 6zPZxd9LBADMfRyVw4/3IbKyEbe7f/LVjHAsQWCqsWMYRJUadmJ+9oCw++hkpjPR
# iQfhvbfmQ6QYuKZ3AeEPlAwhHbJUKSWJbOUOUlFHdL4mrLZBdd56rF+NP8m800ER
# ElvlEFDrMcXKchYiCd98THU/Y+whX8QgUWtvsauGi0/C1kVfnSD8oR7FwI+isX4K
# Jpn15GkvmB0t9dmpsh3lGwIDAQABo4IBOjCCATYwDwYDVR0TAQH/BAUwAwEB/zAd
# BgNVHQ4EFgQU7NfjgtJxXWRM3y5nP+e6mK4cD08wHwYDVR0jBBgwFoAUReuir/SS
# y4IxLVGLp6chnfNtyA8wDgYDVR0PAQH/BAQDAgGGMHkGCCsGAQUFBwEBBG0wazAk
# BggrBgEFBQcwAYYYaHR0cDovL29jc3AuZGlnaWNlcnQuY29tMEMGCCsGAQUFBzAC
# hjdodHRwOi8vY2FjZXJ0cy5kaWdpY2VydC5jb20vRGlnaUNlcnRBc3N1cmVkSURS
# b290Q0EuY3J0MEUGA1UdHwQ+MDwwOqA4oDaGNGh0dHA6Ly9jcmwzLmRpZ2ljZXJ0
# LmNvbS9EaWdpQ2VydEFzc3VyZWRJRFJvb3RDQS5jcmwwEQYDVR0gBAowCDAGBgRV
# HSAAMA0GCSqGSIb3DQEBDAUAA4IBAQBwoL9DXFXnOF+go3QbPbYW1/e/Vwe9mqyh
# hyzshV6pGrsi+IcaaVQi7aSId229GhT0E0p6Ly23OO/0/4C5+KH38nLeJLxSA8hO
# 0Cre+i1Wz/n096wwepqLsl7Uz9FDRJtDIeuWcqFItJnLnU+nBgMTdydE1Od/6Fmo
# 8L8vC6bp8jQ87PcDx4eo0kxAGTVGamlUsLihVo7spNU96LHc/RzY9HdaXFSMb++h
# UD38dglohJ9vytsgjTVgHAIDyyCwrFigDkBjxZgiwbJZ9VVrzyerbHbObyMt9H5x
# aiNrIv8SuFQtJ37YOtnwtoeW/VvRXKwYw02fc7cBqZ9Xql4o4rmUMIIFkDCCA3ig
# AwIBAgIQBZsbV56OITLiOQe9p3d1XDANBgkqhkiG9w0BAQwFADBiMQswCQYDVQQG
# EwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNl
# cnQuY29tMSEwHwYDVQQDExhEaWdpQ2VydCBUcnVzdGVkIFJvb3QgRzQwHhcNMTMw
# ODAxMTIwMDAwWhcNMzgwMTE1MTIwMDAwWjBiMQswCQYDVQQGEwJVUzEVMBMGA1UE
# ChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29tMSEwHwYD
# VQQDExhEaWdpQ2VydCBUcnVzdGVkIFJvb3QgRzQwggIiMA0GCSqGSIb3DQEBAQUA
# A4ICDwAwggIKAoICAQC/5pBzaN675F1KPDAiMGkz7MKnJS7JIT3yithZwuEppz1Y
# q3aaza57G4QNxDAf8xukOBbrVsaXbR2rsnnyyhHS5F/WBTxSD1Ifxp4VpX6+n6lX
# FllVcq9ok3DCsrp1mWpzMpTREEQQLt+C8weE5nQ7bXHiLQwb7iDVySAdYyktzuxe
# TsiT+CFhmzTrBcZe7FsavOvJz82sNEBfsXpm7nfISKhmV1efVFiODCu3T6cw2Vbu
# yntd463JT17lNecxy9qTXtyOj4DatpGYQJB5w3jHtrHEtWoYOAMQjdjUN6QuBX2I
# 9YI+EJFwq1WCQTLX2wRzKm6RAXwhTNS8rhsDdV14Ztk6MUSaM0C/CNdaSaTC5qmg
# Z92kJ7yhTzm1EVgX9yRcRo9k98FpiHaYdj1ZXUJ2h4mXaXpI8OCiEhtmmnTK3kse
# 5w5jrubU75KSOp493ADkRSWJtppEGSt+wJS00mFt6zPZxd9LBADMfRyVw4/3IbKy
# Ebe7f/LVjHAsQWCqsWMYRJUadmJ+9oCw++hkpjPRiQfhvbfmQ6QYuKZ3AeEPlAwh
# HbJUKSWJbOUOUlFHdL4mrLZBdd56rF+NP8m800ERElvlEFDrMcXKchYiCd98THU/
# Y+whX8QgUWtvsauGi0/C1kVfnSD8oR7FwI+isX4KJpn15GkvmB0t9dmpsh3lGwID
# AQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBhjAdBgNVHQ4E
# FgQU7NfjgtJxXWRM3y5nP+e6mK4cD08wDQYJKoZIhvcNAQEMBQADggIBALth2X2p
# bL4XxJEbw6GiAI3jZGgPVs93rnD5/ZpKmbnJeFwMDF/k5hQpVgs2SV1EY+CtnJYY
# ZhsjDT156W1r1lT40jzBQ0CuHVD1UvyQO7uYmWlrx8GnqGikJ9yd+SeuMIW59mdN
# Oj6PWTkiU0TryF0Dyu1Qen1iIQqAyHNm0aAFYF/opbSnr6j3bTWcfFqK1qI4mfN4
# i/RN0iAL3gTujJtHgXINwBQy7zBZLq7gcfJW5GqXb5JQbZaNaHqasjYUegbyJLkJ
# EVDXCLG4iXqEI2FCKeWjzaIgQdfRnGTZ6iahixTXTBmyUEFxPT9NcCOGDErcgdLM
# MpSEDQgJlxxPwO5rIHQw0uA5NBCFIRUBCOhVMt5xSdkoF1BN5r5N0XWs0Mr7QbhD
# parTwwVETyw2m+L64kW4I1NsBm9nVX9GtUw/bihaeSbSpKhil9Ie4u1Ki7wb/UdK
# Dd9nZn6yW0HQO+T0O/QEY+nvwlQAUaCKKsnOeMzV6ocEGLPOr0mIr/OSmbaz5mEP
# 0oUA51Aa5BuVnRmhuZyxm7EAHu/QD09CbMkKvO5D+jpxpchNJqU1/YldvIViHTLS
# oCtU7ZpXwdv6EM8Zt4tKG48BtieVU+i2iW1bvGjUI+iLUaJW+fCmgKDWHrO8Dw9T
# dSmq6hN35N6MgSGtBxBHEa2HPQfRdbzP82Z+MIIGrjCCBJagAwIBAgIQBzY3tyRU
# fNhHrP0oZipeWzANBgkqhkiG9w0BAQsFADBiMQswCQYDVQQGEwJVUzEVMBMGA1UE
# ChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29tMSEwHwYD
# VQQDExhEaWdpQ2VydCBUcnVzdGVkIFJvb3QgRzQwHhcNMjIwMzIzMDAwMDAwWhcN
# MzcwMzIyMjM1OTU5WjBjMQswCQYDVQQGEwJVUzEXMBUGA1UEChMORGlnaUNlcnQs
# IEluYy4xOzA5BgNVBAMTMkRpZ2lDZXJ0IFRydXN0ZWQgRzQgUlNBNDA5NiBTSEEy
# NTYgVGltZVN0YW1waW5nIENBMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKC
# AgEAxoY1BkmzwT1ySVFVxyUDxPKRN6mXUaHW0oPRnkyibaCwzIP5WvYRoUQVQl+k
# iPNo+n3znIkLf50fng8zH1ATCyZzlm34V6gCff1DtITaEfFzsbPuK4CEiiIY3+va
# PcQXf6sZKz5C3GeO6lE98NZW1OcoLevTsbV15x8GZY2UKdPZ7Gnf2ZCHRgB720RB
# idx8ald68Dd5n12sy+iEZLRS8nZH92GDGd1ftFQLIWhuNyG7QKxfst5Kfc71ORJn
# 7w6lY2zkpsUdzTYNXNXmG6jBZHRAp8ByxbpOH7G1WE15/tePc5OsLDnipUjW8LAx
# E6lXKZYnLvWHpo9OdhVVJnCYJn+gGkcgQ+NDY4B7dW4nJZCYOjgRs/b2nuY7W+yB
# 3iIU2YIqx5K/oN7jPqJz+ucfWmyU8lKVEStYdEAoq3NDzt9KoRxrOMUp88qqlnNC
# aJ+2RrOdOqPVA+C/8KI8ykLcGEh/FDTP0kyr75s9/g64ZCr6dSgkQe1CvwWcZklS
# UPRR8zZJTYsg0ixXNXkrqPNFYLwjjVj33GHek/45wPmyMKVM1+mYSlg+0wOI/rOP
# 015LdhJRk8mMDDtbiiKowSYI+RQQEgN9XyO7ZONj4KbhPvbCdLI/Hgl27KtdRnXi
# YKNYCQEoAA6EVO7O6V3IXjASvUaetdN2udIOa5kM0jO0zbECAwEAAaOCAV0wggFZ
# MBIGA1UdEwEB/wQIMAYBAf8CAQAwHQYDVR0OBBYEFLoW2W1NhS9zKXaaL3WMaiCP
# nshvMB8GA1UdIwQYMBaAFOzX44LScV1kTN8uZz/nupiuHA9PMA4GA1UdDwEB/wQE
# AwIBhjATBgNVHSUEDDAKBggrBgEFBQcDCDB3BggrBgEFBQcBAQRrMGkwJAYIKwYB
# BQUHMAGGGGh0dHA6Ly9vY3NwLmRpZ2ljZXJ0LmNvbTBBBggrBgEFBQcwAoY1aHR0
# cDovL2NhY2VydHMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0VHJ1c3RlZFJvb3RHNC5j
# cnQwQwYDVR0fBDwwOjA4oDagNIYyaHR0cDovL2NybDMuZGlnaWNlcnQuY29tL0Rp
# Z2lDZXJ0VHJ1c3RlZFJvb3RHNC5jcmwwIAYDVR0gBBkwFzAIBgZngQwBBAIwCwYJ
# YIZIAYb9bAcBMA0GCSqGSIb3DQEBCwUAA4ICAQB9WY7Ak7ZvmKlEIgF+ZtbYIULh
# sBguEE0TzzBTzr8Y+8dQXeJLKftwig2qKWn8acHPHQfpPmDI2AvlXFvXbYf6hCAl
# NDFnzbYSlm/EUExiHQwIgqgWvalWzxVzjQEiJc6VaT9Hd/tydBTX/6tPiix6q4XN
# Q1/tYLaqT5Fmniye4Iqs5f2MvGQmh2ySvZ180HAKfO+ovHVPulr3qRCyXen/KFSJ
# 8NWKcXZl2szwcqMj+sAngkSumScbqyQeJsG33irr9p6xeZmBo1aGqwpFyd/EjaDn
# mPv7pp1yr8THwcFqcdnGE4AJxLafzYeHJLtPo0m5d2aR8XKc6UsCUqc3fpNTrDsd
# CEkPlM05et3/JWOZJyw9P2un8WbDQc1PtkCbISFA0LcTJM3cHXg65J6t5TRxktcm
# a+Q4c6umAU+9Pzt4rUyt+8SVe+0KXzM5h0F4ejjpnOHdI/0dKNPH+ejxmF/7K9h+
# 8kaddSweJywm228Vex4Ziza4k9Tm8heZWcpw8De/mADfIBZPJ/tgZxahZrrdVcA6
# KYawmKAr7ZVBtzrVFZgxtGIJDwq9gdkT/r+k0fNX2bwE+oLeMt8EifAAzV3C+dAj
# fwAL5HYCJtnwZXZCpimHCUcr5n8apIUP/JiW9lVUKx+A+sDyDivl1vupL0QVSucT
# Dh3bNzgaoSv27dZ8/DCCBrAwggSYoAMCAQICEAitQLJg0pxMn17Nqb2TrtkwDQYJ
# KoZIhvcNAQEMBQAwYjELMAkGA1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IElu
# YzEZMBcGA1UECxMQd3d3LmRpZ2ljZXJ0LmNvbTEhMB8GA1UEAxMYRGlnaUNlcnQg
# VHJ1c3RlZCBSb290IEc0MB4XDTIxMDQyOTAwMDAwMFoXDTM2MDQyODIzNTk1OVow
# aTELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDkRpZ2lDZXJ0LCBJbmMuMUEwPwYDVQQD
# EzhEaWdpQ2VydCBUcnVzdGVkIEc0IENvZGUgU2lnbmluZyBSU0E0MDk2IFNIQTM4
# NCAyMDIxIENBMTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBANW0L0LQ
# KK14t13VOVkbsYhC9TOM6z2Bl3DFu8SFJjCfpI5o2Fz16zQkB+FLT9N4Q/QX1x7a
# +dLVZxpSTw6hV/yImcGRzIEDPk1wJGSzjeIIfTR9TIBXEmtDmpnyxTsf8u/LR1oT
# pkyzASAl8xDTi7L7CPCK4J0JwGWn+piASTWHPVEZ6JAheEUuoZ8s4RjCGszF7pNJ
# cEIyj/vG6hzzZWiRok1MghFIUmjeEL0UV13oGBNlxX+yT4UsSKRWhDXW+S6cqgAV
# 0Tf+GgaUwnzI6hsy5srC9KejAw50pa85tqtgEuPo1rn3MeHcreQYoNjBI0dHs6EP
# bqOrbZgGgxu3amct0r1EGpIQgY+wOwnXx5syWsL/amBUi0nBk+3htFzgb+sm+YzV
# svk4EObqzpH1vtP7b5NhNFy8k0UogzYqZihfsHPOiyYlBrKD1Fz2FRlM7WLgXjPy
# 6OjsCqewAyuRsjZ5vvetCB51pmXMu+NIUPN3kRr+21CiRshhWJj1fAIWPIMorTmG
# 7NS3DVPQ+EfmdTCN7DCTdhSmW0tddGFNPxKRdt6/WMtyEClB8NXFbSZ2aBFBE1ia
# 3CYrAfSJTVnbeM+BSj5AR1/JgVBzhRAjIVlgimRUwcwhGug4GXxmHM14OEUwmU//
# Y09Mu6oNCFNBfFg9R7P6tuyMMgkCzGw8DFYRAgMBAAGjggFZMIIBVTASBgNVHRMB
# Af8ECDAGAQH/AgEAMB0GA1UdDgQWBBRoN+Drtjv4XxGG+/5hewiIZfROQjAfBgNV
# HSMEGDAWgBTs1+OC0nFdZEzfLmc/57qYrhwPTzAOBgNVHQ8BAf8EBAMCAYYwEwYD
# VR0lBAwwCgYIKwYBBQUHAwMwdwYIKwYBBQUHAQEEazBpMCQGCCsGAQUFBzABhhho
# dHRwOi8vb2NzcC5kaWdpY2VydC5jb20wQQYIKwYBBQUHMAKGNWh0dHA6Ly9jYWNl
# cnRzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydFRydXN0ZWRSb290RzQuY3J0MEMGA1Ud
# HwQ8MDowOKA2oDSGMmh0dHA6Ly9jcmwzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydFRy
# dXN0ZWRSb290RzQuY3JsMBwGA1UdIAQVMBMwBwYFZ4EMAQMwCAYGZ4EMAQQBMA0G
# CSqGSIb3DQEBDAUAA4ICAQA6I0Q9jQh27o+8OpnTVuACGqX4SDTzLLbmdGb3lHKx
# AMqvbDAnExKekESfS/2eo3wm1Te8Ol1IbZXVP0n0J7sWgUVQ/Zy9toXgdn43ccsi
# 91qqkM/1k2rj6yDR1VB5iJqKisG2vaFIGH7c2IAaERkYzWGZgVb2yeN258TkG19D
# +D6U/3Y5PZ7Umc9K3SjrXyahlVhI1Rr+1yc//ZDRdobdHLBgXPMNqO7giaG9OeE4
# Ttpuuzad++UhU1rDyulq8aI+20O4M8hPOBSSmfXdzlRt2V0CFB9AM3wD4pWywiF1
# c1LLRtjENByipUuNzW92NyyFPxrOJukYvpAHsEN/lYgggnDwzMrv/Sk1XB+JOFX3
# N4qLCaHLC+kxGv8uGVw5ceG+nKcKBtYmZ7eS5k5f3nqsSc8upHSSrds8pJyGH+PB
# VhsrI/+PteqIe3Br5qC6/To/RabE6BaRUotBwEiES5ZNq0RA443wFSjO7fEYVgcq
# LxDEDAhkPDOPriiMPMuPiAsNvzv0zh57ju+168u38HcT5ucoP6wSrqUvImxB+YJc
# FWbMbA7KxYbD9iYzDAdLoNMHAmpqQDBISzSoUSC7rRuFCOJZDW3KBVAr6kocnqX9
# oKcfBnTn8tZSkP2vhUgh+Vc7tJwD7YZF9LRhbr9o4iZghurIr6n+lB3nYxs6hlZ4
# TjCCBrwwggSkoAMCAQICEAuuZrxaun+Vh8b56QTjMwQwDQYJKoZIhvcNAQELBQAw
# YzELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDkRpZ2lDZXJ0LCBJbmMuMTswOQYDVQQD
# EzJEaWdpQ2VydCBUcnVzdGVkIEc0IFJTQTQwOTYgU0hBMjU2IFRpbWVTdGFtcGlu
# ZyBDQTAeFw0yNDA5MjYwMDAwMDBaFw0zNTExMjUyMzU5NTlaMEIxCzAJBgNVBAYT
# AlVTMREwDwYDVQQKEwhEaWdpQ2VydDEgMB4GA1UEAxMXRGlnaUNlcnQgVGltZXN0
# YW1wIDIwMjQwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQC+anOf9pUh
# q5Ywultt5lmjtej9kR8YxIg7apnjpcH9CjAgQxK+CMR0Rne/i+utMeV5bUlYYSuu
# M4vQngvQepVHVzNLO9RDnEXvPghCaft0djvKKO+hDu6ObS7rJcXa/UKvNminKQPT
# v/1+kBPgHGlP28mgmoCw/xi6FG9+Un1h4eN6zh926SxMe6We2r1Z6VFZj75MU/HN
# mtsgtFjKfITLutLWUdAoWle+jYZ49+wxGE1/UXjWfISDmHuI5e/6+NfQrxGFSKx+
# rDdNMsePW6FLrphfYtk/FLihp/feun0eV+pIF496OVh4R1TvjQYpAztJpVIfdNsE
# vxHofBf1BWkadc+Up0Th8EifkEEWdX4rA/FE1Q0rqViTbLVZIqi6viEk3RIySho1
# XyHLIAOJfXG5PEppc3XYeBH7xa6VTZ3rOHNeiYnY+V4j1XbJ+Z9dI8ZhqcaDHOoj
# 5KGg4YuiYx3eYm33aebsyF6eD9MF5IDbPgjvwmnAalNEeJPvIeoGJXaeBQjIK13S
# lnzODdLtuThALhGtyconcVuPI8AaiCaiJnfdzUcb3dWnqUnjXkRFwLtsVAxFvGqs
# xUA2Jq/WTjbnNjIUzIs3ITVC6VBKAOlb2u29Vwgfta8b2ypi6n2PzP0nVepsFk8n
# lcuWfyZLzBaZ0MucEdeBiXL+nUOGhCjl+QIDAQABo4IBizCCAYcwDgYDVR0PAQH/
# BAQDAgeAMAwGA1UdEwEB/wQCMAAwFgYDVR0lAQH/BAwwCgYIKwYBBQUHAwgwIAYD
# VR0gBBkwFzAIBgZngQwBBAIwCwYJYIZIAYb9bAcBMB8GA1UdIwQYMBaAFLoW2W1N
# hS9zKXaaL3WMaiCPnshvMB0GA1UdDgQWBBSfVywDdw4oFZBmpWNe7k+SH3agWzBa
# BgNVHR8EUzBRME+gTaBLhklodHRwOi8vY3JsMy5kaWdpY2VydC5jb20vRGlnaUNl
# cnRUcnVzdGVkRzRSU0E0MDk2U0hBMjU2VGltZVN0YW1waW5nQ0EuY3JsMIGQBggr
# BgEFBQcBAQSBgzCBgDAkBggrBgEFBQcwAYYYaHR0cDovL29jc3AuZGlnaWNlcnQu
# Y29tMFgGCCsGAQUFBzAChkxodHRwOi8vY2FjZXJ0cy5kaWdpY2VydC5jb20vRGln
# aUNlcnRUcnVzdGVkRzRSU0E0MDk2U0hBMjU2VGltZVN0YW1waW5nQ0EuY3J0MA0G
# CSqGSIb3DQEBCwUAA4ICAQA9rR4fdplb4ziEEkfZQ5H2EdubTggd0ShPz9Pce4FL
# Jl6reNKLkZd5Y/vEIqFWKt4oKcKz7wZmXa5VgW9B76k9NJxUl4JlKwyjUkKhk3aY
# x7D8vi2mpU1tKlY71AYXB8wTLrQeh83pXnWwwsxc1Mt+FWqz57yFq6laICtKjPIC
# YYf/qgxACHTvypGHrC8k1TqCeHk6u4I/VBQC9VK7iSpU5wlWjNlHlFFv/M93748Y
# TeoXU/fFa9hWJQkuzG2+B7+bMDvmgF8VlJt1qQcl7YFUMYgZU1WM6nyw23vT6QSg
# wX5Pq2m0xQ2V6FJHu8z4LXe/371k5QrN9FQBhLLISZi2yemW0P8ZZfx4zvSWzVXp
# Ab9k4Hpvpi6bUe8iK6WonUSV6yPlMwerwJZP/Gtbu3CKldMnn+LmmRTkTXpFIEB0
# 6nXZrDwhCGED+8RsWQSIXZpuG4WLFQOhtloDRWGoCwwc6ZpPddOFkM2LlTbMcqFS
# zm4cd0boGhBq7vkqI1uHRz6Fq1IX7TaRQuR+0BGOzISkcqwXu7nMpFu3mgrlgbAW
# +BzikRVQ3K2YHcGkiKjA4gi4OA/kz1YCsdhIBHXqBzR0/Zd2QwQ/l4Gxftt/8wY3
# grcc/nS//TVkej9nmUYu83BDtccHHXKibMs/yXHhDXNkoPIdynhVAku7aRZOwqw6
# pDCCB1YwggU+oAMCAQICEAH2OVfKsi5/gxE0YVpt8UAwDQYJKoZIhvcNAQELBQAw
# aTELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDkRpZ2lDZXJ0LCBJbmMuMUEwPwYDVQQD
# EzhEaWdpQ2VydCBUcnVzdGVkIEc0IENvZGUgU2lnbmluZyBSU0E0MDk2IFNIQTM4
# NCAyMDIxIENBMTAeFw0yNDAyMjgwMDAwMDBaFw0yNTA1MjQyMzU5NTlaMF4xCzAJ
# BgNVBAYTAlVTMRcwFQYDVQQIEw5Ob3J0aCBDYXJvbGluYTEUMBIGA1UEBxMLTW9y
# cmlzdmlsbGUxDzANBgNVBAoTBkxlbm92bzEPMA0GA1UEAxMGTGVub3ZvMIICIjAN
# BgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAvQG1vNNHS34jqP4ExAKEkPeGrbyn
# 0EAseZ0nI/OPSzjBWXtKkumwgsi6rqgRD7wCrxncDH5TtuW5pDbiYMffX3mwbM3Q
# uzGPgyBcnd0eWr57yHKbPQ8j53QviIP6uX2C6H0HZujjVgWfjPwPkOfF8fqgl9dh
# ZX8wVJvwJDUGLUzQMkyiIPV1/JmKUHnVi1ClEJBR4DSmv+6obQ01rlNaLK64RdeJ
# iAnZmvnw+qo1N0bwG733A+OLEVhjMSvZ47/AhFvmLYPJ95mw4wa71a4E+2dGbR4i
# N/3fX3Hr3+F4WCk1hy3F7RXTm5yr+IcNRwWtJ527jeRqxruWaWvJK8SPCwUcPWhg
# qdzlGGzakCtwonIac0XC86ChEcau+3JYXuVD/fvXt0Gv9jXYEXN53P7QOhxyTpNz
# p/ghLX3KSrK6pn4+niqb+cXPeyjbd+T29HjCQG9PGBwNRum7c8N5IcN68VHi8gdE
# ecWjSmdPO9jguXQrHRuFkmEzf1wWHw5s9BR4TMwHT2O5D6PJZPcjAZakBoqehKPE
# VAH0gMa2RICeTf1/ikuAOvFU9Q/in9F9jN0shDDz/SEoRU7OpD4C88gRGB8y0p0a
# +EKCGFRT/drApdumRPmMQmMLREZDVOxNuN9irY3w5fql74cthz5bX6NkvvFpcJds
# SD6M/bcEk6FKcyECAwEAAaOCAgMwggH/MB8GA1UdIwQYMBaAFGg34Ou2O/hfEYb7
# /mF7CIhl9E5CMB0GA1UdDgQWBBRagjd2MdLvV1XeyO0tkBGfG8YjWDA+BgNVHSAE
# NzA1MDMGBmeBDAEEATApMCcGCCsGAQUFBwIBFhtodHRwOi8vd3d3LmRpZ2ljZXJ0
# LmNvbS9DUFMwDgYDVR0PAQH/BAQDAgeAMBMGA1UdJQQMMAoGCCsGAQUFBwMDMIG1
# BgNVHR8Ega0wgaowU6BRoE+GTWh0dHA6Ly9jcmwzLmRpZ2ljZXJ0LmNvbS9EaWdp
# Q2VydFRydXN0ZWRHNENvZGVTaWduaW5nUlNBNDA5NlNIQTM4NDIwMjFDQTEuY3Js
# MFOgUaBPhk1odHRwOi8vY3JsNC5kaWdpY2VydC5jb20vRGlnaUNlcnRUcnVzdGVk
# RzRDb2RlU2lnbmluZ1JTQTQwOTZTSEEzODQyMDIxQ0ExLmNybDCBlAYIKwYBBQUH
# AQEEgYcwgYQwJAYIKwYBBQUHMAGGGGh0dHA6Ly9vY3NwLmRpZ2ljZXJ0LmNvbTBc
# BggrBgEFBQcwAoZQaHR0cDovL2NhY2VydHMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0
# VHJ1c3RlZEc0Q29kZVNpZ25pbmdSU0E0MDk2U0hBMzg0MjAyMUNBMS5jcnQwCQYD
# VR0TBAIwADANBgkqhkiG9w0BAQsFAAOCAgEAvEuiwGr7vMBryz2z53c2L9ZEaZGT
# YQfZrOy9/lpeYpHJ0ongWlyYmeGgl1vxHQP8+0xHctTtTC3ZaBVk6n/d8/53JYi2
# gmOJF6RHYnlGK+/r+Js3mm3Xu57fZkrqQciV0flPqjNlMZ5VYWtfhgbEeqM1Fnsa
# rk9owJzhpWeBAVKXUG3dnxaNWJrH5ykCCphW0lCpZ18qezRTEB6PoNas9Y/wJuCB
# 1jwuse5N9kJjbJxG01dblVm4vbQhtH2+fS6hB6+c/8rBR/vh0hubJkFCc0n0YoBe
# JRtsYxiWk2hTDklF/rRyic+WDg+nsMUKTliBnbLWLyQYYk11uLBSXAabv/Aj8ywC
# DiPsf/13l+CdBhtiDKEx+Jofkn4LTGblVSSiVbpgyuKVooUwNnH26nBKlzgAfS25
# rpLAHAxjEW9rq81jQyXBYxSuETciZjv/J2DKkK22dNOURX4RpvPrF4wUkhAj6gmj
# bRzZmhEZwWBlW6L1ljuYFXLqlx549BTiTEGf3SsBphOLkTS/I3FbQXrFbetXg42A
# znsVcor/ejFU2EL7wMBjC2lS4RZgfRixOzSiGBCbDm+yhOAPD5vAZWYsOiyzjtSG
# CDOpXjJdTqKkcWgU9NU85+p/2rCYzHCl09KuGDnOoqLmpJAI+HyzCK79s4eAlsjo
# uUwbomBag565bEMxggZCMIIGPgIBATB9MGkxCzAJBgNVBAYTAlVTMRcwFQYDVQQK
# Ew5EaWdpQ2VydCwgSW5jLjFBMD8GA1UEAxM4RGlnaUNlcnQgVHJ1c3RlZCBHNCBD
# b2RlIFNpZ25pbmcgUlNBNDA5NiBTSEEzODQgMjAyMSBDQTECEAH2OVfKsi5/gxE0
# YVpt8UAwCQYFKw4DAhoFAKB4MBgGCisGAQQBgjcCAQwxCjAIoAKAAKECgAAwGQYJ
# KoZIhvcNAQkDMQwGCisGAQQBgjcCAQQwHAYKKwYBBAGCNwIBCzEOMAwGCisGAQQB
# gjcCARUwIwYJKoZIhvcNAQkEMRYEFJCpBwOkKC5KPFEcAV/a8ukB7ehOMA0GCSqG
# SIb3DQEBAQUABIICALI4q9/w92T8XITgtAKZRZ7cdCrnflOfnpDyUyx+E4F6RtmY
# jI6i8kWng4WVnIeJUdOP/XDXvL8kN7PqflPcv98pputoUfVHWo6IZRrkIgzYKFUH
# B0tLzm4syT+cwZdnB2Zk7BZEIFA6+kmgiYEYIn68CFm4Ci75NNVjPEhWGSfBjsD4
# onMeZaGm3yHgLL4XBn2d2j1amqAI9SKURfEO5wfIrT5nPqmts5GMBlUzbPSlxC3u
# CrIjuQqBb9QbBpExcgw7RAkqPlO/uQT6eMgphJpofjkoVHMZzBKtGK6Yia04mgH3
# Mo8ZLyHgePtF+4Lp4BcLjOJSylKbTqpilmwDdZNElnaPAwAbjDhWI+bU/4TjUKI7
# 4yx6BiYgzatb1V1zSoa29qS6xiX02Ab6Kho6pFmK4ffi83bss+M4oCl8vb40r2ZU
# 1Ae52nPfSpIzekITR++nd67G9QcpCu7y5eAKE7mPA0oEKNQ/xQgupxKKldsJ5zZp
# Qt1pAHSeK+Wzw1Y7vzJ7jmJqitlBgg0VOEq5mqZ0xJLcM6+c34Ty5ovoxU95CgRR
# 5OujmbfoZPnjUjGvNkxjA/jC7a1LeBjvzXZH1BRdewaU1JHYvQV6/5K0VOLjP/yS
# x1j/ppM9Z6vhLEvhBSQ/OmVVNXVyfH9oNR0OFDqvXk8/urWRwLoD2xFjud2roYID
# IDCCAxwGCSqGSIb3DQEJBjGCAw0wggMJAgEBMHcwYzELMAkGA1UEBhMCVVMxFzAV
# BgNVBAoTDkRpZ2lDZXJ0LCBJbmMuMTswOQYDVQQDEzJEaWdpQ2VydCBUcnVzdGVk
# IEc0IFJTQTQwOTYgU0hBMjU2IFRpbWVTdGFtcGluZyBDQQIQC65mvFq6f5WHxvnp
# BOMzBDANBglghkgBZQMEAgEFAKBpMBgGCSqGSIb3DQEJAzELBgkqhkiG9w0BBwEw
# HAYJKoZIhvcNAQkFMQ8XDTI1MDIyNjE5MjAyMVowLwYJKoZIhvcNAQkEMSIEIPTw
# 6qwlwP/lkq0c83DF/WEVsOmd2FmuzCYDi0Ulq/IVMA0GCSqGSIb3DQEBAQUABIIC
# ADMii9EfFpzdLYyfelR02GJEZNwXlx2ifkuEiI9WcQsAnQxIMO7LWBs0EWV1nOOn
# jy/+2+BnAkMXzpszQby8HYVEqOlfR+0M5V3NR4vTT47eMscLjsslc1nD02KvLSsd
# PoMI5/FGTLYMjKTv0Lvzb/UHSuI6+rFP4JTpLDSslWd3Xd32RcX7/65NPcaTc9XE
# v1DzXA0ivxGuccgUyfzg/mJj3LCf9R5+k5rRSYu8AnnwECSTbDpV0Xh4gxSJ3/Gu
# izdlHr7/Z0RJ/tdTOhxOFC4e03pNG1s3DFEBzvBSl3RsJiAlZRE3m4IOSigjYjax
# AV4i2oxnK2TuXFA9RqnMuV2esV+no1MVKGppXLh7lzFeQ9uUwQzisxMXVe2R28Ji
# ZyTyvQUzpP7HcFKOEpchv+pDblT/m/iqVlLfHWlj/tvpN8YKuv9slP8EtPjewIRa
# ngSE6VPRetCa2KIPHEGGov01x45tMsq71HRbrnLUE0MoWNUN3f7dpTlB7WjD/PmN
# cG/buMjLDwhNOasvN6b4EKGi6EtW28JjflTiqcyRtWhDe0PLLHvvQwBXHmxgN73Z
# PuYQyMXis9GtczQvb53SuupzJgC6ld3DojLJKleqJzPt0FbaXEarQr+KGvvSiZ0Z
# Jk+1AGs2wVIeBAe17V+6DhabVrWr9jJ+26Cw5UsNbNWf
# SIG # End signature block