HP.Softpaq.psm1

# Copyright (C)2018 HP Inc
# All Rights Reserved.
#
# NOTICE: All information contained herein is, and remains the property of HP Inc.
#
# The intellectual and technical concepts contained herein are proprietary to HP Inc
# and may be covered by U.S. and Foreign Patents, patents in process, and are protected by
# trade secret or copyright law. Dissemination of this information or reproduction of this material
# is strictly forbidden unless prior written permission is obtained from HP Inc.



Set-StrictMode -Version 3.0
#requires -Modules "HP.Private","HP.Firmware"

<#
.SYNOPSIS
    Retrieve softpaq metadata (CVA) can retrieve a CVA or an alternate server.
 
.DESCRIPTION
    Get a softpaq metadata file (CVA) from the location specified by the URL parameter. If URL is not
    specified, the Softpaq metadata is downloaded can retrieve a CVA via HTTPS.
 
.PARAMETER Number
    The softpaq number for which to retrieve the metadata. Do not include prefix such as SP or extension such as .exe, specify the softpaq number only.
 
.PARAMETER Url
    specify an alternate location for the softpaq URL. This URL must be http, https, or ftp. The softpaq CVAs are expected to be at the location
    pointed to by this URL. If not specified, ftp.hp.com is used via HTTPS protocol.
 
.EXAMPLE
    Get-SoftpaqMetadata 1234 | Out-SoftpaqField -field Title
 
 
.LINK
    [Get-Softpaq](Get-Softpaq)
 
.LINK
    [Get-SoftpaqMetadataFile](Get-SoftpaqMetadataFile)
 
.LINK
    [Get-SoftpaqList](Get-SoftpaqList)
 
.LINK
    [Out-SoftpaqField](Out-SoftpaqField)
 
.LINK
    [Clear-SoftpaqCache](Clear-SoftpaqCache)
 
#>

function Get-SoftpaqMetadata {
  [CmdletBinding(HelpUri = "https://developers.hp.com/hp-client-management/doc/Get%E2%80%90SoftpaqMetadata")]
  param(
    [ValidatePattern('^([Ss][Pp])*([0-9]{3,9})((\.[Ee][Xx][Ee]|\.[Cc][Vv][Aa])*)$')]
    [Parameter(Position = 0,Mandatory = $true)] [string]$Number,
    [Parameter(Position = 1,Mandatory = $false)] [string]$Url,
    [Parameter(Position = 2,Mandatory = $false)] [int]$MaxRetries = 0
  )

  $no = [int]$number.ToLower().TrimStart("sp").trimend(".exe").trimend('cva')
  [System.Net.ServicePointManager]::SecurityProtocol = Get-HPPrivateAllowedHttpsProtocols
  $loc = Get-HPPrivateItemUrl $no "cva" $url
  Get-HPPrivateReadINI -file $loc -Verbose:$VerbosePreference -maxRetries $maxRetries 
}


<#
.SYNOPSIS
    Retrieve softpaq (EXE) can retrieve a CVA or an alternate server.
 
.DESCRIPTION
    This function downloads a softpaq from a the default download server (ftp.hp.com) or an alternate server.
    When using the default location, the download is performed over HTTPS, otherwise the protocol is
  dictated by the URL parameter.
   
  Get-Softpaq functionality is not supported in WinPE.
 
.PARAMETER Number
    The softpaq number for which to retrieve the metadata. Do not include prefix such as SP or extension such as .exe, specify the softpaq number only.
 
.PARAMETER SaveAs
    Provide a specific name for the saved softpaq, otherwise it is inferred based on the remote name or the softpaq metadata
    if -FriendlyName is specified.
 
.PARAMETER FriendlyName
    Create a friendly name for the downloaded softpaq, based on the softpaq number and title.
 
.PARAMETER Quiet
    Suppress non-errors such as download progress and other messages.
 
.PARAMETER Overwrite
    This parameter controls the overwrite behavior. Options may be "no" to not overwrite existing files,
    "yes" to force overwrite, and "skip" to skip existing files without an error. Default is 'no' if overwrite is
    not specified.
 
.PARAMETER Action
  Perform a specific action after download. The action may be "install" or "silentinstall".
  Silentinstall information is retrieved from the softpaq metadata (CVA) file.
 
.PARAMETER Extract
  Extracts files from a specified zipped archive file to a specified destination folder.
 
  Note:
    By default, files are extracted into a new sub-folder relative to the downloaded softpaq executable.
 
.PARAMETER DestinationPath
  Specifies the path to the folder in which you want to save extracted files.
  Enter the path to a folder, but do not specify a file name or file name extension.
 
  Note:
    By default, the files are extracted into a new sub-folder relative to the downloaded executable.
 
.PARAMETER Url
    specify an alternate location for the softpaq URL. This URL must be http, https, or ftp. The softpaqs are expected to be at the
    location pointed to by this URL. If not specified, ftp.hp.com is used via HTTPS protocol.
 
.PARAMETER KeepInvalidSigned
    this function performs an Authenticode signature check after a download, and deletes any
    downloaded file with invalid or missing signature. Use this flag to keep the file that failed the signature.
 
.EXAMPLE
    Get-Softpaq 1234
 
.EXAMPLE
    Get-Softpaq 1234 -Extract -DestinationPath "c:\staging\drivers"
 
.LINK
    [Get-SoftpaqMetadata](Get-SoftpaqMetadata)
 
.LINK
    [Get-SoftpaqMetadataFile](Get-SoftpaqMetadataFile)
 
.LINK
    [Get-SoftpaqList](Get-SoftpaqList)
 
.LINK
    [Out-SoftpaqField](Out-SoftpaqField)
 
.LINK
    [Clear-SoftpaqCache](Clear-SoftpaqCache)
 
#>

function Get-Softpaq {
  [CmdletBinding(HelpUri = "https://developers.hp.com/hp-client-management/doc/Get%E2%80%90Softpaq", DefaultParameterSetName = "DownloadParams")]
  param(
    [Parameter(ParameterSetName = "DownloadParams")]
    [Parameter(Position = 0, Mandatory = $true)]
    [string]$Number,

    [Parameter(ParameterSetName = "DownloadParams")]
    [Parameter(Position = 1, Mandatory = $false)]
    [string]$SaveAs,

    [Parameter(ParameterSetName = "DownloadParams")]
    [Parameter(Position = 2, Mandatory = $false)]
    [switch]$FriendlyName,

    [Parameter(ParameterSetName = "DownloadParams")]
    [Parameter(Position = 3, Mandatory = $false)]
    [switch]$Quiet,

    [Parameter(ParameterSetName = "DownloadParams")]
    [ValidateSet("no", "yes", "skip")]
    [Parameter(Position = 4, Mandatory = $false)]
    [string]$Overwrite = "no",

    [parameter(Position = 5, Mandatory = $false, ParameterSetName = "DownloadParams")]
    [parameter(Position = 5, Mandatory = $false, ParameterSetName = "InstallParams")]
    [ValidateSet("install", "silentinstall")]
    [string]$Action,

    [Parameter(ParameterSetName = "DownloadParams")]
    [Parameter(Position = 6, Mandatory = $false)]
    [string]$Url,

    [Parameter(ParameterSetName = "DownloadParams")]
    [Parameter(Position = 7, Mandatory = $false)]
    [switch]$KeepInvalidSigned,

    [Parameter(ParameterSetName = "DownloadParams")]
    [Parameter(Position = 8, Mandatory = $false)]
    [int]$MaxRetries = 0,

    [parameter(Mandatory = $false, ParameterSetName = "DownloadParams")]
    [parameter(Mandatory = $false, ParameterSetName = "ExtractParams")]
    [switch]$Extract,

    [parameter(Mandatory = $false, ParameterSetName = "DownloadParams")]
    [parameter(Mandatory = $false, ParameterSetName = "ExtractParams")]
    [ValidatePattern('^[a-zA-Z]:\\')]
    [String]$DestinationPath
  )

  if((Test-WinPE) -and ($action)){
    Throw "Softpaq installation is not supported in WinPE"
  }

  [System.Net.ServicePointManager]::SecurityProtocol = Get-HPPrivateAllowedHttpsProtocols
  $no = [int]$number.ToLower().TrimStart("sp").trimend(".exe")

  if ($keepInvalidSigned.IsPresent) { $keepInvalid = $true }
  else { $keepInvalid = $false }

  if ($quiet.IsPresent) { $progress = -not $quiet }
  else { $progress = $true }

  $loc = Get-HPPrivateItemUrl -number $no -ext "exe" -url $url 
  $target = $null
  $root = $null

  if ($friendlyName.IsPresent -or $action) {
      # get softpaq metadata
      try { $root = Get-SoftpaqMetadata $no -url $url -maxRetries $maxRetries}
      catch {
        if ($progress -eq $true) {
          Write-Host -ForegroundColor Magenta "(Warning) Could not retrieve CVA file metadata for sp$no."
          Write-Host -ForegroundColor Magenta $_.Exception.Message
        }        
      }
    }

  # build the filename
  if (!$saveAs) {
    if ($friendlyName.IsPresent)
    {
      Write-Verbose "Need to get CVA data to determine Friendly Name for Softpaq file"
      $target = getfriendlyFileName -number $no -info $root -Verbose:$VerbosePreference
      $target = "$target.exe"
    }

    else { $target = "sp$no.exe" }
  }
  else { $target = $saveAs }

  $targetFile = $ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($target)

  try
  {
    Invoke-HPPrivateDownloadFile -url $loc -target $targetFile -progress $progress -NoClobber $overwrite -Verbose:$VerbosePreference -maxRetries $maxRetries
  }
  catch
  {
      Write-Host -ForegroundColor Magenta "(Warning) Could not retrieve $loc."
      Write-Host -ForegroundColor Magenta $_.Exception.Message
      throw $_.Exception
  }

  # check digital signatures
  $signed = Get-HPPrivateCheckSignature -file $targetFile -Verbose:$VerbosePreference

  if ($signed -eq $false) {
    switch ($keepInvalid) {
      $true {
        if ($progress -eq $true) {
          Write-Host -ForegroundColor Magenta "(Warning): File $targetFile has invalid or missing signature"
          return
        }
      }
      $false {
        Invoke-HPPrivateSafeRemove -path $targetFile -Verbose:$VerbosePreference
        throw [System.BadImageFormatException] "File $targetFile has invalid or missing signature and will be deleted."
        return
      }
    }
  }
  else {
    if ($progress -eq $true) {
      Write-Verbose "Digital signature is valid."
    }
  }

  # perform requested action
  if ($action -ne $null)
  {
    $PostInstallAction = if ($Extract.IsPresent) {"extract"} else {$action}
    postDownloadSoftpaqAction -downloadedFile $targetFile -Action $PostInstallAction -number $number -info $root -Destination $DestinationPath
  }
}

<#
.SYNOPSIS
    Retrieve softpaq metadata (CVA) can retrieve a CVA or an alternate server.
 
.DESCRIPTION
    Use this command to download a softpaq from a specified location. By default, the softpaq is retrieved
    from HP.COM via HTTPS.
 
.PARAMETER Number
    The softpaq number for which to retrieve the metadata. Do not include prefix such as SP or extension such as .exe.
 
.PARAMETER SaveAs
    Provide a specific name for the saved softpaq metadata, otherwise it is inferred based on the remote name or the metadata
    if -friendlyName is specified.
 
.PARAMETER FriendlyName
    Create a friendly name for the downloaded softpaq, based on the softpaq number and title.
 
.PARAMETER Quiet
    Suppress non-errors such as download progress and other messages.
 
.PARAMETER Overwrite
    This parameter controls the overwrite behavior. Options may be "no" to not overwrite existing files,
    "yes" to force overwrite, and "skip" to skip existing files without an error. Default is 'no' if overwrite is
    not specified.
 
 
.PARAMETER url
    An alternate URL where to look for the softpaq
 
.EXAMPLE
    Get-SoftpaqMetadataFile 1234
 
.LINK
    [Get-SoftpaqMetadata](Get-SoftpaqMetadata)
 
.LINK
    [Get-Softpaq](Get-Softpaq)
 
.LINK
    [Get-SoftpaqList](Get-SoftpaqList)
 
.LINK
    [Out-SoftpaqField](Out-SoftpaqField)
 
.LINK
    [Clear-SoftpaqCache](Clear-SoftpaqCache)
 
#>

function Get-SoftpaqMetadataFile {
  [CmdletBinding(HelpUri = "https://developers.hp.com/hp-client-management/doc/Get%E2%80%90SoftpaqMetadataFile")]
  param(
    [ValidatePattern('^([Ss][Pp])*([0-9]{3,9})((\.[Ee][Xx][Ee]|\.[Cc][Vv][Aa])*)$')]
    [Parameter(Position = 0,Mandatory = $true)]
    [string]$Number,
    [Parameter(Position = 1,Mandatory = $false)]
    [string]$SaveAs,
    [Parameter(Position = 2,Mandatory = $false)]
    [switch]$FriendlyName,
    [Parameter(Position = 3,Mandatory = $false)]
    [switch]$Quiet,
    [ValidateSet("No","Yes","Skip")]
    [Parameter(Position = 4,Mandatory = $false)]
    [string]$Overwrite = "No",
    [Parameter(Position = 5,Mandatory = $false)]
    [string]$Url,
    [Parameter(Position = 6,Mandatory = $false)]
    [int]$MaxRetries = 0
  )

  [System.Net.ServicePointManager]::SecurityProtocol = Get-HPPrivateAllowedHttpsProtocols
  $no = [int]$number.ToLower().TrimStart("sp").trimend(".exe").trimend('cva')

  if ($quiet.IsPresent) { $progress = -not $quiet }
  else { $progress = $true }

  $loc = Get-HPPrivateItemUrl -number $no -ext "cva"  -url $url

  $target = $null

  # get softpaq metadata. We don't need this step unless we get friendly name
  if ($friendlyName.IsPresent) {
    Write-Verbose "Need to get CVA data to determine Friendly Name for CVA file"
      try { $root = Get-SoftpaqMetadata $number -url $url -maxRetries $maxRetries }
      catch {
        if ($progress -eq $true) {
          Write-Host -ForegroundColor Magenta "(Warning) Could not retrieve CVA file metadata"
          Write-Host -ForegroundColor Magenta $_.Exception.Message
        }
        $root = $null
      }
  }

  # build the filename
  if (!$saveAs) {
    if ($friendlyName.IsPresent) { 
      Write-Verbose "Need to get CVA data to determine Friendly Name for CVA file"
      $target = getfriendlyFileName -number $no -info $root  -Verbose:$VerbosePreference
      $target = "$target.cva"
  }
    else { $target = "sp$no.cva" }
  }
  else { $target = $saveAs }

  # download the file
  $targetFile = $ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($target)
  Invoke-HPPrivateDownloadFile -url $loc -target $targetFile -progress $progress -NoClobber $overwrite -Verbose:$VerbosePreference -maxRetries $maxRetries -skipSignatureCheck
}

<#
.SYNOPSIS
    Filter a loaded softpaq metadata
 
.DESCRIPTION
    Extract the specified field from the softpaq metadata. Some of the currently supported fields are:
 
    * Title
    * Description
    * SilentInstall
    * Number
    * Platforms
  * PlatformIDs
  * Version
 
 
.PARAMETER Field
    The predefined filter to retrieve
 
.EXAMPLE
    $mysoftpaq | Out-SoftpaqField -Field Title
 
 
.LINK
    [Get-SoftpaqMetadata](Get-SoftpaqMetadata)
 
.LINK
    [Get-Softpaq](Get-Softpaq)
 
.LINK
    [Get-SoftpaqList](Get-SoftpaqList)
 
.LINK
    [Get-Softpaq](Get-Softpaq)
 
.LINK
    [Clear-SoftpaqCache](Clear-SoftpaqCache)
 
 
#>

function Out-SoftpaqField {
  [CmdletBinding(HelpUri = "https://developers.hp.com/hp-client-management/doc/Out%E2%80%90SoftpaqField")]
  param(
    [ValidateSet("Install","SilentInstall","Title","Description","Number","Platforms","PlatformIDs","SoftPaqSHA256","SoftPaqMD5","Version")]
    [Parameter(Mandatory = $True)]
    [string]$Field,

    [ValidateNotNullOrEmpty()]
    [Parameter(Mandatory = $False)]
    [string]$Language = "en",

    [ValidateNotNullOrEmpty()]
    [Parameter(ValueFromPipeline = $True,Mandatory = $True)]
    [Alias('In')]
    $InputObject
  )

  begin {
    if (!$mapper.contains($field)) {
      throw [InvalidOperationException] "Field '$field' is not supported as a filter"
    }
  }
  process
  {
    $result = descendNodesAndGet $InputObject -field $field
    if ($mapper[$field] -match "%KeyValues\(.*\)$") {

      $pattern = $mapper[$field] -match "\((.*)\)"
      if ($pattern[0]) {

        return $result[$result.keys -match $matches[1]].TrimStart("0x") | Get-Unique
      }
    }
    return $result.TrimStart("0x")
  }
  end {}
}

<#
.SYNOPSIS
    Clear the softpaq cache.
 
.DESCRIPTION
 
    Clear the cache used for downloading softpaq database files.
 
    This is normally not needed as part of normal operation, the cache doesn't grow significantly over time, and is also cleared
    by norma operations that flush the user's temporary directory.
 
    The function is only intended as a debugging function.
 
 
 
.EXAMPLE
    Clear-SoftpaqCache
 
.PARAMETER cacheDir
    Custom location for caching data files. If not specified, the user-specific TEMP directory is used.
 
 
.LINK
    [Get-SoftpaqMetadata](Get-SoftpaqMetadata)
 
.LINK
    [Get-Softpaq](Get-Softpaq)
 
.LINK
    [Get-SoftpaqList](Get-SoftpaqList)
 
.LINK
    [Get-Softpaq](Get-Softpaq)
 
.LINK
    [Out-SoftpaqField](Out-SoftpaqField)
 
.NOTES
    This command removes the cached files from the user temporary directory. It cannot be used to clear the cache
    when the data files are stored inside a repository structure. Custom cache locations (other than the default)
    must be specified via the cacheDir folder.
 
#>

function Clear-SoftpaqCache {
  [CmdletBinding(HelpUri = "https://developers.hp.com/hp-client-management/doc/Clear%E2%80%90SoftpaqCache")]
  param(
    [Parameter(Mandatory = $false)]
    [System.IO.DirectoryInfo]$CacheDir
  )
  $cacheDir = Get-HPPrivateCacheDirPath  ($cacheDir)
  Invoke-HPPrivateSafeRemove -path $cacheDir -Recurse
}

<#
.SYNOPSIS
    Get a list of softpaqs for a platform
 
.DESCRIPTION
  This function gets a list of latest softpaqs for a specified platform ID, or the current computer.
   
  Get-SoftpaqList functionality is not supported in WinPE.
 
.PARAMETER Platform
    The platform ID for which to retrieve the list of softpaqs. If not available, the current platform ID is used.
 
.PARAMETER Bitness
    the platform bitness (32 or 64). If not available, the current platform bitness is used.
 
.PARAMETER Os
    filter for the specified OS. The OS may be one of win7, win8, win8.1, win10. If not specified, current platform OS is used.
 
.PARAMETER OsVer
    filter for the specified OS Version. Applies to Win10 only. This is a numeric value, usually the third part of a Windows 10 version (e.g. 10.0.xxxx)
 
.PARAMETER Category
    The category field filters to a specific category of Softpaqs. It must be one (or more) of "bios", "firmware", "driver", "software", "os", "manageability", "diagnostic", "utility", "driverpack", "dock".
 
.PARAMETER ReleaseType
    filter to specified release type. The release type must be one (or more) of "critical", "recommended", "routine".
 
.PARAMETER Url
    specify an alternate location for the softpaq URL. This URL must be http, https, or ftp. The softpaqs are expected to be at the location pointed to by
    this URL. If not specified, ftp.hp.com is used via HTTPS protocol.
 
.PARAMETER Quiet
    Suppress non-error progress messages
 
.PARAMETER Download
    download matching softpaqs
 
.PARAMETER DownloadMetadata
    also download CVA files (metadata) for matching softpaqs
 
.PARAMETER DownloadNotes
    also download note files (human readable info files) for matching softpaqs
 
.PARAMETER DownloadDirectory
    specify a directory for the downloaded files
 
.PARAMETER Friendlyname
    if specified, retrieve the softpaq metadata and create a friendly file name based on the softpaq title. Applies if -download is specified.
 
.PARAMETER Overwrite
    This parameter controls the overwrite behavior. Options may be "no" to not overwrite existing files,
    "yes" to force overwrite, and "skip" to skip existing files without an error. Default is 'no' if overwrite is
    not specified.
 
.PARAMETER Format
    Display results in a specified format (json, xml, or csv). If not specified, results are returned as powershell objects
 
.PARAMETER Characteristic
    Specify additional filter characteristics for the softpaq list. The characteristic must be one (or more) of "ssm", "dpb".
 
.PARAMETER CacheDir
    Custom location for caching data files. If not specified, the user-specific TEMP directory is used.
 
.PARAMETER MaxRetries
    Maximum number of retries allowed to obtain an exclusive lock to downloaded files. This is relevant only when
    files are downloaded into a shared directory and multiple processes may be reading or writing from the same
    location.
 
    Current default value is 10 retries, and each retry includes a 30 second pause, which means the maximum time the
    default value will wait for an exclusive logs is 300 seconds or 5 minutes.
 
 
.EXAMPLE
    Get-SoftpaqList -Download
.EXAMPLE
    Get-SoftpaqList -Bitness 64 -Os win10 -OsVer 1903
.EXAMPLE
    Get-SoftpaqList -Platform 8444 -Category Diagnostic -Format json
.EXAMPLE
    Get-SoftpaqList -Category Driverpack
.EXAMPLE
    Get-SoftpaqList -ReleaseType critical -characteristic SSM
.EXAMPLE
    Get-SoftpaqList -Platform 83b2 -Category Dock,Firmware -ReleaseType Routine,Critical
 
.LINK
    [Get-SoftpaqMetadata](Get-SoftpaqMetadata)
 
.LINK
    [Get-Softpaq](Get-Softpaq)
 
.LINK
    [Clear-SoftpaqCache](Clear-SoftpaqCache)
 
.LINK
    [Get-Softpaq](Get-Softpaq)
 
.LINK
  [Out-SoftpaqField](Out-SoftpaqField)
   
.NOTES
  The response is a record set composed of zero or more softpaq records. The definition of a record is as follows:
 
  | Field | Description |
  |---------------|-------------|
  | Id | The softpaq identifier |
  | Name | The softpaq name (title) |
  | Category | The softpaq category |
  | Version | The softpaq version |
  | Vendor | The author of the softpaq |
  | ReleaseType | The softpaq release type (TODO) |
  | SSM | This flag indicates this softpaq support unattended silent install |
  | DPB | This flag indicates this softpaq is included in driver pack builds |
  | Url | The softpaq download URL |
  | ReleaseNotes | The URL to a human-readable rendering of the softpaq release notes |
  | Metadata | The URL to the softpaq metadata (CVA) file |
  | MD5 | (where available) The MD5 value for this softpaq |
  | Size | The softpaq size, in bytes |
  | ReleaseDate | The date the softpaq was published |
 
 
#>

function Get-SoftpaqList {
  [CmdletBinding(HelpUri = "https://developers.hp.com/hp-client-management/doc/Get%E2%80%90SoftpaqList", DefaultParameterSetName = "ViewParams")]
  param(

    [ValidatePattern("^[a-fA-F0-9]{4}$")]
    [Parameter(ParameterSetName = "DownloadParams")]
    [Parameter(Position = 0,Mandatory = $false,ParameterSetName = "ViewParams")] [string]$Platform,

    [ValidateSet(32,64)]
    [Parameter(ParameterSetName = "DownloadParams")]
    [Parameter(Position = 1,Mandatory = $false,ParameterSetName = "ViewParams")] [int]$Bitness,

    [ValidateSet($null,"win7","win8","win8.1","win81","win10")]
    [Parameter(ParameterSetName = "DownloadParams")]
    [Parameter(Position = 2,Mandatory = $false,ParameterSetName = "ViewParams")] [string]$Os,

    [ValidateRange(1507,3007)]
    [Parameter(ParameterSetName = "DownloadParams")]
    [Parameter(Position = 3,Mandatory = $false,ParameterSetName = "ViewParams")] [int]$OsVer,

    [Parameter(ParameterSetName = "DownloadParams")]
    [Parameter(Position = 4,Mandatory = $false,ParameterSetName = "ViewParams")] [string]$Url = "https://ftp.hp.com/pub/caps-softpaq/cmit/imagepal/ref",

    [Parameter(ParameterSetName = "DownloadParams")]
    [Parameter(Position = 5,Mandatory = $false,ParameterSetName = "ViewParams")] [switch]$Quiet,

    [Parameter(ParameterSetName = "DownloadParams")]
    [ValidateSet("XML","Json","CSV")]
    [Parameter(Position = 6, ParameterSetName = "ViewParams")] [string]$Format,

    [Parameter(Position = 7, ParameterSetName = "DownloadParams")] [string]$DownloadDirectory,

    [Alias("downloadSoftpaq", "downloadPackage")]
    [Parameter(Position = 8, ParameterSetName = "DownloadParams")] [switch]$Download,

    [Alias("downloadCva")]
    [Parameter(Position = 9, ParameterSetName = "DownloadParams")] [switch]$DownloadMetadata,
    [Parameter(Position = 10, ParameterSetName = "DownloadParams")] [switch]$DownloadNotes,
    [Parameter(Position = 11, ParameterSetName = "DownloadParams")] [switch]$FriendlyName,

    [ValidateSet("No","Yes","Skip")]
    [Parameter(ParameterSetName = "DownloadParams")]
    [Parameter(Position = 12,Mandatory = $false,ParameterSetName = "ViewParams")]  [string]$Overwrite = "no",


    [ValidateSet("BIOS","Firmware","Driver","Software","OS","Manageability","Diagnostic","Utility","Driverpack","Dock")]
    [Parameter(ParameterSetName = "DownloadParams")]  
    [Parameter(Position = 13,ParameterSetName = "ViewParams")]  [string[]] $Category = $null,


    [ValidateSet("Critical","Recommended","Routine")] 
    [Parameter(ParameterSetName = "DownloadParams")]    
    [Parameter(Position = 14,ParameterSetName = "ViewParams")] [string[]] $ReleaseType = $null,


    [ValidateSet("SSM","DPB")]
    [Parameter(ParameterSetName = "DownloadParams")]    
    [Parameter(Position = 15,ParameterSetName = "ViewParams")] [string[]] $Characteristic = $null,


    [Parameter(ParameterSetName = "DownloadParams")]
    [Parameter(Position = 16,ParameterSetName = "ViewParams")] [System.IO.DirectoryInfo]$CacheDir,

    [Parameter(ParameterSetName = "DownloadParams")]
    [Parameter(Position = 17,Mandatory=$false,ParameterSetName = "ViewParams")] [int]$MaxRetries = 0
  )

  if(Test-WinPE){
    Throw "Softpaq List generation is not supported in WinPE"
  }

  [System.Net.ServicePointManager]::SecurityProtocol = Get-HPPrivateAllowedHttpsProtocols
  $ver = ""
  $progress = $true
  $cacheDir = Get-HPPrivateCacheDirPath  ($cacheDir)

  if ($quiet.IsPresent) { $progress = -not $quiet }
  if (-not $platform) { $platform = Get-HPDeviceProductID }
  $platform = $platform.ToLower()

  if (!$bitness) { if ([environment]::Is64BitOperatingSystem -eq $true) { $bitness = 64 } else { $bitness = 32 } }
  if (!$os)
  {
    switch ([string][System.Environment]::OSVersion.Version.Major + "." + [string][System.Environment]::OSVersion.Version.Minor) {
      "10.0" { $os = "win10" }
      "6.3" { $os = "win81" }
      "6.2" { $os = "win8" }
      "6.1" { $os = "win7" }
    }
  }

  if (([System.Environment]::OSVersion.Version.Major -eq 10) -and (!$osver))
  {
    $osver = (Get-ItemProperty 'HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion' -Name ReleaseID | Select-Object ReleaseID).ReleaseId
  }

  switch ($os)
  {
    "win10" { $ver = "10.0." + $osver.ToString() }
    "win81" { $ver = "6.3" }
    "win8.1" { $ver = "6.3" }
    "win8" { $ver = "6.2" }
    "win7" { $ver = "6.1" }
    default { throw [NotSupportedException] "Unsupported operating system: " + $_ }
  }

  $fn = "$($platform)_$($bitness)_$($ver)"
  $qurl = "$url/$platform/$fn.cab"
  $qfile = Get-HPPrivateTemporaryFileName -filename "$fn.cab" -cacheDir $cacheDir
  $downloadedFile = "$qfile.dir\$fn.xml"
  $result = $null

  try { 
      $result = Test-HPPrivateIsDownloadNeeded -url $qurl -file $qfile -Verbose:$VerbosePreference
  }
  catch {
      if (-not $quiet) {
            Write-Host -ForegroundColor Magenta $_.Exception.Message
        }
    throw [System.Net.WebException] "Could not find a data file for this platform."
  }

  if ($result[1] -eq $true) {
    Write-Verbose "Cleaning cached data and downloading the data file."
    Invoke-HPPrivateDeleteCachedItem -cab $qfile
    Invoke-HPPrivateDownloadFile -url $qurl -target $qfile -progress $progress -NoClobber $overwrite -Verbose:$VerbosePreference -maxRetries $maxRetries
    (Get-Item $qfile).CreationTime = ($result[0])
    (Get-Item $qfile).LastWriteTime = ($result[0])
  }

  # Need to make sure that the expanded data file exists and is not corrupted.
  # Otherwise, expand the cab file.
  if (-not (Test-Path $downloadedFile) -or (-not (Test-HPPrivateIsValidXmlFile -file $downloadedFile)))
  {
    Write-Verbose "Extracting the data file, looking for $downloadedFile."
    $file = Invoke-HPPrivateExpandCAB -cab $qfile -expectedFile $downloadedFile
  }

  Write-Verbose "Reading XML document $downloadedFile"
  [xml]$data = Get-Content $downloadedFile
  Write-Verbose "Parsing the document"

  $d = Select-Xml -Xml $data -XPath "//ImagePal/Solutions/UpdateInfo"

  $results = $d | ForEach-Object {
    if (($releaseType -eq $null) -or ($_.Node.releaseType -in $releaseType)) {
      if (matchCategory -cat $_.Node.category -allowed $category -EQ $true) {
        if (($characteristic -eq $null) -or (matchAnyCharacteristic $characteristic -SSM $_.Node.SSMCompliant -DPB $_.Node.DPBCompliant)) {
          $pso = New-Object PSObject
          $pso | Add-Member NoteProperty -Name "Id" -Value $_.Node.id -Passthru
          $pso | Add-Member NoteProperty -Name "Name" -Value $_.Node.Name -Passthru
          $pso | Add-Member NoteProperty -Name "Category" -Value $_.Node.category -Passthru
          $pso | Add-Member NoteProperty -Name "Version" -Value $_.Node.Version.TrimStart("0") -Passthru
          $pso | Add-Member NoteProperty -Name "Vendor" -Value $_.Node.Vendor -Passthru
          $pso | Add-Member NoteProperty -Name "ReleaseType" -Value $_.Node.releaseType -Passthru
          $pso | Add-Member NoteProperty -Name "SSM" -Value $_.Node.SSMCompliant -Passthru
          $pso | Add-Member NoteProperty -Name "DPB" -Value $_.Node.DPBCompliant -Passthru
          $pso | Add-Member NoteProperty -Name "Url" -Value $_.Node.Url -Passthru
          $pso | Add-Member NoteProperty -Name "ReleaseNotes" -Value $_.Node.ReleaseNotesUrl -Passthru
          $pso | Add-Member NoteProperty -Name "Metadata" -Value $_.Node.CvaUrl -Passthru
          $pso | Add-Member NoteProperty -Name "MD5" -Value $_.Node.md5 -Passthru
          $pso | Add-Member NoteProperty -Name "Size" -Value $_.Node.Size -Passthru
          $pso | Add-Member NoteProperty -Name "ReleaseDate" -Value $_.Node.DateReleased -Passthru

          if ($download.IsPresent)
          {

            if ($friendlyName.IsPresent) { 
                Write-Verbose "Need to get CVA data to determine Friendly Name for download file"
                $target = getfriendlyFileName -number $pso.id.ToLower().TrimStart("sp") -From $pso.Name   -Verbose:$VerbosePreference 
            }
            else { $target = $pso.id }

            if ($downloadDirectory) { $target = "$downloadDirectory\$target" }
            else {
              $cwd = Convert-Path .
              $target = "$cwd\$target"
            }

            
            if ($downloadMetadata.IsPresent)
            {
              Invoke-HPPrivateDownloadFile -url ("https://" + $pso.Metadata) -target "$target.cva" -progress $progress -NoClobber $overwrite -Verbose:$VerbosePreference -skipSignatureCheck -maxRetries $maxRetries
            }

            Invoke-HPPrivateDownloadFile -url ("https://" + $pso.Url) -target "$target.exe" -progress $progress -NoClobber $overwrite -Verbose:$VerbosePreference -maxRetries $maxRetries
          
            if ($downloadNotes.IsPresent)
            {
              Invoke-HPPrivateDownloadFile -url ("https://" + $pso.ReleaseNotes) -target "$target.htm" -progress $progress -NoClobber $overwrite -Verbose:$VerbosePreference -skipSignatureCheck -maxRetries $maxRetries
            }
          }
        }
      }
    }
  }

  $result = $results | Select-Object * -Unique
  switch ($format)
  {
    "xml" { $result | ConvertTo-Xml -As String}
    "json" { $result | ConvertTo-Json }
    "csv" { $result | ConvertTo-Csv -NoTypeInformation }
    default { return $result }
  }
}



# private functionality below



function matchCategory ([string]$cat,[string[]]$allowed)
{
  if ($allowed -eq $null) { return $true }
  if ($cat.startsWith("Driver -") -eq $true) { return $allowed -eq  "driver" }
  if ($cat.startsWith("Operating System -") -eq $true) { return $allowed -eq "os" }
  if ($cat.StartsWith("Manageability - Driver Pack") -eq $true) { return $allowed -eq "driverpack"}
  if ($cat.StartsWith("Manageability -") -eq $true) { return $allowed -eq "manageability"}
  if ($cat.StartsWith("Utility -") -eq $true) { return $allowed -eq "utility"}
  if ($cat.StartsWith("Dock -") -eq $true) { return $allowed -eq "dock"}
  if (($cat -eq "BIOS") -Or ($cat.StartsWith("BIOS -") -eq $true)) { return $allowed -eq "BIOS"}
  if ($cat -eq "firmware") { return $allowed -eq "firmware"}
  if ($cat -eq "diagnostic") { return $allowed -eq "diagnostic"}
  else {
    return $allowed -eq "software"
  } 
  return $false
}

function matchAnyCharacteristic ([string[]]$targetedCharacteristic,[string]$SSM,[string]$DPB)
{
  if ($targetedCharacteristic -eq $null) { return $true }
  if ($targetedCharacteristic.Count -eq 0) { return $true }

  foreach ($characteristic in $targetedCharacteristic)
  {
    if (($characteristic.Trim().ToLower() -eq "ssm") -and ($SSM.Trim().ToLower() -eq "true"))
    {
      return $true
    }
    if (($characteristic.Trim().ToLower() -eq "dpb") -and ($DPB.Trim().ToLower() -eq "true"))
    {
      return $true
    }
  }
  return $false
}



function Release-Ref ($ref) {
  [System.Runtime.InteropServices.Marshal]::ReleaseComObject([System.__ComObject]$ref) | Out-Null
  [System.GC]::Collect()
  [System.GC]::WaitForPendingFinalizers()
}


# perform an action after a softpaq download completed
function postDownloadSoftpaqAction ([string]$downloadedFile,[string]$action,[string]$number,$info,[string]$Destination)
{
  $cmd = $null

  switch ($action) {
    "extract" {
      if(!$Destination){
        $Destination = (Get-Item $downloadedFile).DirectoryName
        $Destination = Join-Path -Path $Destination -ChildPath (Get-Item $downloadedFile).Basename
      }
      Write-Verbose -Message "Extracting $downloadedFile to: $Destination"
      Start-Process -Wait "$downloadedFile" -ArgumentList "-e -f $Destination", "-s"
    }
    "install" {
      #$cmd = descendNodesAndGet $info -field "install"
      Start-Process -Wait "$downloadedFile"
    }
    "silentinstall" {
      # Get the silent install command from the metadata
      if (!$info) { $info = Get-SoftpaqMetadata $number }

      $cmd = descendNodesAndGet $info -field "silentinstall"
      Start-Process -Wait "$downloadedFile" -ArgumentList "-s","-e cmd.exe","-a","/c $cmd"
    }
  }
}

# create a friendly name from softpaq metadata (CVA)
function getfriendlyFileName 
{
   [CmdletBinding()]
    param(
        [int]$number,
        $info,  
        [string]$from
        )

  try {
    $title = "sp$number"

    #if title was passed in, we use it
    if ($from) { $title = $from }

    #else if object was passed in, we use it
    elseif ($info -ne $null) { $title = ($info | Out-SoftpaqField Title) }

    #else use a default
    else { $title = "(No description available)"}

    $pass1 = removeInvalidCharacters $title
    $pass2 = $pass1.Trim()
    $pass3 = $pass2 -replace ('\s+','_')
    return $number.ToString("sp######") + "-" + $pass3
  }
  catch {
    Write-Host -ForegroundColor Magenta "Could not determine friendly name, using softpaq number."
    Write-Host -ForegroundColor Magenta $_.Exception.Message
    return "sp$number"
  }
}

# remove invalid characters from a filename
function removeInvalidCharacters ([string]$Name) {
  $invalidChars = [IO.Path]::GetInvalidFileNameChars() -join ''
  $re = "[{0}]" -f [regex]::Escape($invalidChars)
  return ($Name -replace $re)
}

#shortcuts to various sections of CVA file
$mapper = @{
  "Install" = "Install Execution|Install";
  "SilentInstall" = "Install Execution|SilentInstall";
  "Number" = "Softpaq|SoftpaqNumber";
  "Title" = "Software Title|%lang";
  "Description" = "%lang.Software Description|_body";
  "Platforms" = "System Information|%KeyValues(^SysName.*$)";
  "PlatformIDs" = "System Information|%KeyValues(^SysId.*$)";
  "SoftPaqSHA256" = "Softpaq|SoftPaqSHA256";
  "SoftPaqMD5" = "Softpaq|SoftPaqMD5";
  "Version" = "General|VendorVersion";
};

#ISO to CVA language mapper
$lang_mapper = @{
  "en" = "us";
};


# navigate a CVA structure
function descendNodesAndGet ($root,$field,$lang = "en")
{
  $f1 = $mapper[$field].Replace("%lang",$lang_mapper[$lang])
  $f = $f1.Split("|")
  $node = $root

  foreach ($c in $f) {
    if ($c -match "^%KeyValues\(.*\)$") { return $node }
    if ($c -match "^%Keys\(.*\)$") { return $node }
    $node = $node[$c]
  }
  $node
}


# SIG # Begin signature block
# MIIcNwYJKoZIhvcNAQcCoIIcKDCCHCQCAQExDzANBglghkgBZQMEAgEFADB5Bgor
# BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG
# KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCDxJuIuDcabGcJA
# gINj75+wvT1B7dyoQajahNZhGd4aT6CCCo0wggU2MIIEHqADAgECAhAM1s71mz4i
# 3j/UnuaI4vzeMA0GCSqGSIb3DQEBCwUAMHYxCzAJBgNVBAYTAlVTMRUwEwYDVQQK
# EwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xNTAzBgNV
# BAMTLERpZ2lDZXJ0IFNIQTIgSGlnaCBBc3N1cmFuY2UgQ29kZSBTaWduaW5nIENB
# MB4XDTE5MDQyMjAwMDAwMFoXDTIwMDQyOTEyMDAwMFowdTELMAkGA1UEBhMCVVMx
# EzARBgNVBAgTCkNhbGlmb3JuaWExEjAQBgNVBAcTCVBhbG8gQWx0bzEQMA4GA1UE
# ChMHSFAgSW5jLjEZMBcGA1UECxMQSFAgQ3liZXJzZWN1cml0eTEQMA4GA1UEAxMH
# SFAgSW5jLjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBANEwuTFpw7fQ
# 3Ds5fvexal46Gg9TNMvdiJu7qMqDZnDJNl7ECdEPyLxsioGS7/yomOS9RXdXMJOm
# tyV4/wIPbBaGC8E2tbLTbQQ4IJbgvC+Vc46vbo+sI8YTG6qBICOovFw9VhUNXXEy
# SwHMoBNk8JS8R1slPpJKmNGB10HSatMGaHja0Lbqos0QuEx/tx2OXe+mzepIo66T
# dtSv2MfPy2tcVcXIdiJGn7f4otxoj6T9X7hVIl78r5Y2XWHYtDK8KaV1E/qkiNXK
# 1Xw5S53zv2VsZl6i1LZwt3d1Q9pUmm1AZe2YdhSGvwMP2LYBJGXIBbyLYnxS4HKB
# R7MYZyz7H2kCAwEAAaOCAb8wggG7MB8GA1UdIwQYMBaAFGedDyAJDMyKOuWCRnJi
# /PHMkOVAMB0GA1UdDgQWBBSnSAWgK15kcBLxsg4XNsT7ncH29zAOBgNVHQ8BAf8E
# BAMCB4AwEwYDVR0lBAwwCgYIKwYBBQUHAwMwbQYDVR0fBGYwZDAwoC6gLIYqaHR0
# cDovL2NybDMuZGlnaWNlcnQuY29tL3NoYTItaGEtY3MtZzEuY3JsMDCgLqAshipo
# dHRwOi8vY3JsNC5kaWdpY2VydC5jb20vc2hhMi1oYS1jcy1nMS5jcmwwTAYDVR0g
# BEUwQzA3BglghkgBhv1sAwswKjAoBggrBgEFBQcCARYcaHR0cHM6Ly93d3cuZGln
# aWNlcnQuY29tL0NQUzAIBgZngQwBBAEwgYgGCCsGAQUFBwEBBHwwejAkBggrBgEF
# BQcwAYYYaHR0cDovL29jc3AuZGlnaWNlcnQuY29tMFIGCCsGAQUFBzAChkZodHRw
# Oi8vY2FjZXJ0cy5kaWdpY2VydC5jb20vRGlnaUNlcnRTSEEySGlnaEFzc3VyYW5j
# ZUNvZGVTaWduaW5nQ0EuY3J0MAwGA1UdEwEB/wQCMAAwDQYJKoZIhvcNAQELBQAD
# ggEBAJQblkFw+UYKYSY2M/CIEpJxZDnf+cDhodKAy+goI3XfExRHhyLu3Gc2ibFB
# Y4wyz/sJSfHehtNPYckXxR9k/FB/GfYtEACug9xXxJ+iLxWUNQ4KPt3bXY/kmDxW
# D1QXJFLbW5Dop3w/K0DL3fxnjOfYCcxsYodbeEiCJprCdNi3zd6x/J8Y35GDbLA5
# p7RfIAzKrmBLPHFGDWr/jWTfwPfUNz6jYJ51m0Ba9j81kzpxNUD0yBIZXBkVvSkx
# A09KxzMSSvxvV9DSqSezQBVgWnl9TbElouYUQwk64i0GzL4lTsphK4rQJJ2uuKtH
# wN4E0ibpm0uIqbLhgk+3ic8fHTIwggVPMIIEN6ADAgECAhALfhCQPDhJD/ovZ5qH
# oae5MA0GCSqGSIb3DQEBCwUAMGwxCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdp
# Q2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xKzApBgNVBAMTIkRp
# Z2lDZXJ0IEhpZ2ggQXNzdXJhbmNlIEVWIFJvb3QgQ0EwHhcNMTMxMDIyMTIwMDAw
# WhcNMjgxMDIyMTIwMDAwWjB2MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNl
# cnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29tMTUwMwYDVQQDEyxEaWdp
# Q2VydCBTSEEyIEhpZ2ggQXNzdXJhbmNlIENvZGUgU2lnbmluZyBDQTCCASIwDQYJ
# KoZIhvcNAQEBBQADggEPADCCAQoCggEBALRKXn0HD0HexPV2Fja9cf/PP09zS5zR
# Df5Ky1dYXoUW3QIVVJnwjzwvTQJ4EGjI2DVLP8H3Z86YHK4zuS0dpApUk8SFot81
# sfXxPKezNPtdSMlGyWJEvEiZ6yhJU8M9j8AO3jWY6WJR3z1rQGHuBEHaz6dcVpbR
# +Uy3RISHmGnlgrkT5lW/yJJwkgoxb3+LMqvPa1qfYsQ+7r7tWaRTfwvxUoiKewpn
# JMuQzezSTTRMsOG1n5zG9m8szebKU3QBn2c13jhJLc7tOUSCGXlOGrK1+7t48Elm
# p8/6XJZ1kosactn/UJJTzD7CQzIJGoYTaTz7gTIzMmR1cygmHQgwOwcCAwEAAaOC
# AeEwggHdMBIGA1UdEwEB/wQIMAYBAf8CAQAwDgYDVR0PAQH/BAQDAgGGMBMGA1Ud
# JQQMMAoGCCsGAQUFBwMDMH8GCCsGAQUFBwEBBHMwcTAkBggrBgEFBQcwAYYYaHR0
# cDovL29jc3AuZGlnaWNlcnQuY29tMEkGCCsGAQUFBzAChj1odHRwOi8vY2FjZXJ0
# cy5kaWdpY2VydC5jb20vRGlnaUNlcnRIaWdoQXNzdXJhbmNlRVZSb290Q0EuY3J0
# MIGPBgNVHR8EgYcwgYQwQKA+oDyGOmh0dHA6Ly9jcmw0LmRpZ2ljZXJ0LmNvbS9E
# aWdpQ2VydEhpZ2hBc3N1cmFuY2VFVlJvb3RDQS5jcmwwQKA+oDyGOmh0dHA6Ly9j
# cmwzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydEhpZ2hBc3N1cmFuY2VFVlJvb3RDQS5j
# cmwwTwYDVR0gBEgwRjA4BgpghkgBhv1sAAIEMCowKAYIKwYBBQUHAgEWHGh0dHBz
# Oi8vd3d3LmRpZ2ljZXJ0LmNvbS9DUFMwCgYIYIZIAYb9bAMwHQYDVR0OBBYEFGed
# DyAJDMyKOuWCRnJi/PHMkOVAMB8GA1UdIwQYMBaAFLE+w2kD+L9HAdSYJhoIAu9j
# ZCvDMA0GCSqGSIb3DQEBCwUAA4IBAQBqDv9+E3wGpUvALoz5U2QJ4rpYkTBQ7Myf
# 4dOoL0hGNhgp0HgoX5hWQA8eur2xO4dc3FvYIA3tGhZN1REkIUvxJ2mQE+sRoQHa
# /bVOeVl1vTgqasP2jkEriqKL1yxRUdmcoMjjTrpsqEfSTtFoH4wCVzuzKWqOaiAq
# ufIAYmS6yOkA+cyk1LqaNdivLGVsFnxYId5KMND66yRdBsmdFretSkXTJeIM8ECq
# XE2sfs0Ggrl2RmkI2DK2gv7jqVg0QxuOZ2eXP2gxFjY4lT6H98fDr516dxnZ3pO1
# /W4r/JT5PbdMEjUsML7ojZ4FcJpIE/SM1ucerDjnqPOtDLd67GftMYIRADCCEPwC
# AQEwgYowdjELMAkGA1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZMBcG
# A1UECxMQd3d3LmRpZ2ljZXJ0LmNvbTE1MDMGA1UEAxMsRGlnaUNlcnQgU0hBMiBI
# aWdoIEFzc3VyYW5jZSBDb2RlIFNpZ25pbmcgQ0ECEAzWzvWbPiLeP9Se5oji/N4w
# DQYJYIZIAWUDBAIBBQCgfDAQBgorBgEEAYI3AgEMMQIwADAZBgkqhkiG9w0BCQMx
# DAYKKwYBBAGCNwIBBDAcBgorBgEEAYI3AgELMQ4wDAYKKwYBBAGCNwIBFTAvBgkq
# hkiG9w0BCQQxIgQg4RqaGA25A/Q2SZRr1TmtKMiRHVkt/Hd7m4+TFikduD4wDQYJ
# KoZIhvcNAQEBBQAEggEAm4gDywRqcGyQM2nPVeb45ELdVr9Vj4kbSvGd3DHJlOXP
# K0kTFBOMkRixomXMh7sY63AuArhzfHc+ZXxcz3e4fpDLgMcrEGFBqZWJowzvmbuV
# Z8+yPNTHSXHRjBfQrrDRcxVGC7Wa6uOVBR7LLL5UousSSc+We5V2C+DCFHiy+WX5
# BOL3pD3RpHmGZSd22fJ/ipxxOqxym1wGgC8UZDe/M8O3p34T1PPkommBYefGaqJk
# jWhoS0qsRPBmInXG06d0vmLEEvcnAowdr0a6kGfKXlMT51YjEKOTc05qcsaXidx/
# rwSYl5CHw3AgBAozDQpnTW/2m7XnZKgqDV6mFKTRBKGCDsgwgg7EBgorBgEEAYI3
# AwMBMYIOtDCCDrAGCSqGSIb3DQEHAqCCDqEwgg6dAgEDMQ8wDQYJYIZIAWUDBAIB
# BQAwdwYLKoZIhvcNAQkQAQSgaARmMGQCAQEGCWCGSAGG/WwHATAxMA0GCWCGSAFl
# AwQCAQUABCAlEQnP/EcdQN2X+ZlL4trQiOZ+L28N1WQcak/7u0Y6zAIQJEWsAUbC
# o8Nli396tCKzhxgPMjAyMDAyMTQyMjUwMzBaoIILuzCCBoIwggVqoAMCAQICEATN
# P4VornbGG7D+cWDMp20wDQYJKoZIhvcNAQELBQAwcjELMAkGA1UEBhMCVVMxFTAT
# BgNVBAoTDERpZ2lDZXJ0IEluYzEZMBcGA1UECxMQd3d3LmRpZ2ljZXJ0LmNvbTEx
# MC8GA1UEAxMoRGlnaUNlcnQgU0hBMiBBc3N1cmVkIElEIFRpbWVzdGFtcGluZyBD
# QTAeFw0xOTEwMDEwMDAwMDBaFw0zMDEwMTcwMDAwMDBaMEwxCzAJBgNVBAYTAlVT
# MRcwFQYDVQQKEw5EaWdpQ2VydCwgSW5jLjEkMCIGA1UEAxMbVElNRVNUQU1QLVNI
# QTI1Ni0yMDE5LTEwLTE1MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA
# 6WQ1nPqpmGVkG+QX3LgpNsxnCViFTTDgyf/lOzwRKFCvBzHiXQkYwvaJjGkIBCPg
# dy2dFeW46KFqjv/UrtJ6Fu/4QbUdOXXBzy+nrEV+lG2sAwGZPGI+fnr9RZcxtPq3
# 2UI+p1Wb31pPWAKoMmkiE76Lgi3GmKtrm7TJ8mURDHQNsvAIlnTE6LJIoqEUpfj6
# 4YlwRDuN7/uk9MO5vRQs6wwoJyWAqxBLFhJgC2kijE7NxtWyZVkh4HwsEo1wDo+K
# yuDT17M5d1DQQiwues6cZ3o4d1RA/0+VBCDU68jOhxQI/h2A3dDnK3jqvx9wxu5C
# FlM2RZtTGUlinXoCm5UUowIDAQABo4IDODCCAzQwDgYDVR0PAQH/BAQDAgeAMAwG
# A1UdEwEB/wQCMAAwFgYDVR0lAQH/BAwwCgYIKwYBBQUHAwgwggG/BgNVHSAEggG2
# MIIBsjCCAaEGCWCGSAGG/WwHATCCAZIwKAYIKwYBBQUHAgEWHGh0dHBzOi8vd3d3
# LmRpZ2ljZXJ0LmNvbS9DUFMwggFkBggrBgEFBQcCAjCCAVYeggFSAEEAbgB5ACAA
# dQBzAGUAIABvAGYAIAB0AGgAaQBzACAAQwBlAHIAdABpAGYAaQBjAGEAdABlACAA
# YwBvAG4AcwB0AGkAdAB1AHQAZQBzACAAYQBjAGMAZQBwAHQAYQBuAGMAZQAgAG8A
# ZgAgAHQAaABlACAARABpAGcAaQBDAGUAcgB0ACAAQwBQAC8AQwBQAFMAIABhAG4A
# ZAAgAHQAaABlACAAUgBlAGwAeQBpAG4AZwAgAFAAYQByAHQAeQAgAEEAZwByAGUA
# ZQBtAGUAbgB0ACAAdwBoAGkAYwBoACAAbABpAG0AaQB0ACAAbABpAGEAYgBpAGwA
# aQB0AHkAIABhAG4AZAAgAGEAcgBlACAAaQBuAGMAbwByAHAAbwByAGEAdABlAGQA
# IABoAGUAcgBlAGkAbgAgAGIAeQAgAHIAZQBmAGUAcgBlAG4AYwBlAC4wCwYJYIZI
# AYb9bAMVMB8GA1UdIwQYMBaAFPS24SAd/imu0uRhpbKiJbLIFzVuMB0GA1UdDgQW
# BBRWUw/BxgenTdfYbldygFBM5OyewTBxBgNVHR8EajBoMDKgMKAuhixodHRwOi8v
# Y3JsMy5kaWdpY2VydC5jb20vc2hhMi1hc3N1cmVkLXRzLmNybDAyoDCgLoYsaHR0
# cDovL2NybDQuZGlnaWNlcnQuY29tL3NoYTItYXNzdXJlZC10cy5jcmwwgYUGCCsG
# AQUFBwEBBHkwdzAkBggrBgEFBQcwAYYYaHR0cDovL29jc3AuZGlnaWNlcnQuY29t
# ME8GCCsGAQUFBzAChkNodHRwOi8vY2FjZXJ0cy5kaWdpY2VydC5jb20vRGlnaUNl
# cnRTSEEyQXNzdXJlZElEVGltZXN0YW1waW5nQ0EuY3J0MA0GCSqGSIb3DQEBCwUA
# A4IBAQAug6FEBUoE47kyUvrZgfAau/gJjSO5PdiSoeZGHEovbno8Y243F6Mav1gj
# skOclINOOQmwLOjH4eLM7ct5a87eIwFH7ZVUgeCAexKxrwKGqTpzav74n8GN0SGM
# 5CmCw4oLYAACnR9HxJ+0CmhTf1oQpvgi5vhTkjFf2IKDLW0TQq6DwRBOpCT0R5ze
# DyJyd1x/T+k5mCtXkkTX726T2UPHBDNjUTdWnkcEEcOjWFQh2OKOVtdJP1f8Cp8j
# Xnv0lI3dnRq733oqptJFplUMj/ZMivKWz4lG3DGykZCjXzMwYFX1/GswrKHt5EdO
# M55naii1TcLtW5eC+MupCGxTCbT3MIIFMTCCBBmgAwIBAgIQCqEl1tYyG35B5AXa
# NpfCFTANBgkqhkiG9w0BAQsFADBlMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGln
# aUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29tMSQwIgYDVQQDExtE
# aWdpQ2VydCBBc3N1cmVkIElEIFJvb3QgQ0EwHhcNMTYwMTA3MTIwMDAwWhcNMzEw
# MTA3MTIwMDAwWjByMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5j
# MRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29tMTEwLwYDVQQDEyhEaWdpQ2VydCBT
# SEEyIEFzc3VyZWQgSUQgVGltZXN0YW1waW5nIENBMIIBIjANBgkqhkiG9w0BAQEF
# AAOCAQ8AMIIBCgKCAQEAvdAy7kvNj3/dqbqCmcU5VChXtiNKxA4HRTNREH3Q+X1N
# aH7ntqD0jbOI5Je/YyGQmL8TvFfTw+F+CNZqFAA49y4eO+7MpvYyWf5fZT/gm+vj
# RkcGGlV+Cyd+wKL1oODeIj8O/36V+/OjuiI+GKwR5PCZA207hXwJ0+5dyJoLVOOo
# CXFr4M8iEA91z3FyTgqt30A6XLdR4aF5FMZNJCMwXbzsPGBqrC8HzP3w6kfZiFBe
# /WZuVmEnKYmEUeaC50ZQ/ZQqLKfkdT66mA+Ef58xFNat1fJky3seBdCEGXIX8RcG
# 7z3N1k3vBkL9olMqT4UdxB08r8/arBD13ays6Vb/kwIDAQABo4IBzjCCAcowHQYD
# VR0OBBYEFPS24SAd/imu0uRhpbKiJbLIFzVuMB8GA1UdIwQYMBaAFEXroq/0ksuC
# MS1Ri6enIZ3zbcgPMBIGA1UdEwEB/wQIMAYBAf8CAQAwDgYDVR0PAQH/BAQDAgGG
# MBMGA1UdJQQMMAoGCCsGAQUFBwMIMHkGCCsGAQUFBwEBBG0wazAkBggrBgEFBQcw
# AYYYaHR0cDovL29jc3AuZGlnaWNlcnQuY29tMEMGCCsGAQUFBzAChjdodHRwOi8v
# Y2FjZXJ0cy5kaWdpY2VydC5jb20vRGlnaUNlcnRBc3N1cmVkSURSb290Q0EuY3J0
# MIGBBgNVHR8EejB4MDqgOKA2hjRodHRwOi8vY3JsNC5kaWdpY2VydC5jb20vRGln
# aUNlcnRBc3N1cmVkSURSb290Q0EuY3JsMDqgOKA2hjRodHRwOi8vY3JsMy5kaWdp
# Y2VydC5jb20vRGlnaUNlcnRBc3N1cmVkSURSb290Q0EuY3JsMFAGA1UdIARJMEcw
# OAYKYIZIAYb9bAACBDAqMCgGCCsGAQUFBwIBFhxodHRwczovL3d3dy5kaWdpY2Vy
# dC5jb20vQ1BTMAsGCWCGSAGG/WwHATANBgkqhkiG9w0BAQsFAAOCAQEAcZUS6VGH
# VmnN793afKpjerN4zwY3QITvS4S/ys8DAv3Fp8MOIEIsr3fzKx8MIVoqtwU0HWqu
# mfgnoma/Capg33akOpMP+LLR2HwZYuhegiUexLoceywh4tZbLBQ1QwRostt1AuBy
# x5jWPGTlH0gQGF+JOGFNYkYkh2OMkVIsrymJ5Xgf1gsUpYDXEkdws3XVk4WTfraS
# Z/tTYYmo9WuWwPRYaQ18yAGxuSh1t5ljhSKMYcp5lH5Z/IwP42+1ASa2bKXuh1Eh
# 5Fhgm7oMLSttosR+u8QlK0cCCHxJrhO24XxCQijGGFbPQTS2Zl22dHv1VjMiLyI2
# skuiSpXY9aaOUjGCAk0wggJJAgEBMIGGMHIxCzAJBgNVBAYTAlVTMRUwEwYDVQQK
# EwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xMTAvBgNV
# BAMTKERpZ2lDZXJ0IFNIQTIgQXNzdXJlZCBJRCBUaW1lc3RhbXBpbmcgQ0ECEATN
# P4VornbGG7D+cWDMp20wDQYJYIZIAWUDBAIBBQCggZgwGgYJKoZIhvcNAQkDMQ0G
# CyqGSIb3DQEJEAEEMBwGCSqGSIb3DQEJBTEPFw0yMDAyMTQyMjUwMzBaMCsGCyqG
# SIb3DQEJEAIMMRwwGjAYMBYEFAMlvVBe2pYwLcIvT6AeTCi+KDTFMC8GCSqGSIb3
# DQEJBDEiBCBsIrcq7Hz20E3Xc3DQG96yCCz53Goy8zTZ894ptzz1uDANBgkqhkiG
# 9w0BAQEFAASCAQCw+5tR5NajbxePumXp2x1TlRn692fE3yqaGBKSHL7AmMotrI02
# pcJxVryAX3nxP5yO55NaqvR9VCcZQQWqD07J9Vbr8VzaQBp5x+1ygMpnSOmPVqut
# TNuxFLE3ZF3Y6lLDLmKzEihSAj9JprF4Ipra0GmgLR3un/zgaQr9PLkJeGb2ud1p
# T2KaadelO2AuNkKtBatb2WsiJFXEils1+leWVHb01kgLtGHdcdtWn8MqvRN2UpYj
# QJeDAteMhLSWICo+ktECJ7c+b8p3/3T2AJGFkgFxBfGYENeqmYG6MF+28oOeI1Bq
# unGkjiM4tBUtvmxn7KX/S9VwJMMfNPTqRV+u
# SIG # End signature block