VaultCommands.ps1

#requires -Version 5.1

$Script:PathDelimiter = [System.IO.Path]::DirectorySeparatorChar

function getVault {
    if (-not $Script:Context.Auth) {
        Write-Error -Message "Not Connected" -ErrorAction Stop
    }
    if (-not $Script:Context.Vault) {
        Write-Error -Message "Not Connected" -ErrorAction Stop
    }
    $Script:Context.Vault
}

function Get-KeeperLocation {
    <#
    .Synopsis
    Get current Keeper folder
#>

    [CmdletBinding()]

    [KeeperSecurity.Vault.VaultOnline]$vault = getVault

    [string]$currentFolder = $Script:Context.CurrentFolder
    [KeeperSecurity.Vault.FolderNode]$folder = $vault.RootFolder
    if ($currentFolder) {
        $vault.TryGetFolder($currentFolder, [ref]$folder) | Out-Null
    }
    exportKeeperNode $folder
}
New-Alias -Name kpwd -Value Get-KeeperLocation


function Set-KeeperLocation {
    <#
    .Synopsis
    Change current Keeper folder
 
    .Parameter Path
    New location
#>


    [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSUseShouldProcessForStateChangingFunctions", "")]
    [CmdletBinding()]
    Param (
        [Parameter(Position = 0)][string] $Path
    )
    [KeeperSecurity.Vault.VaultOnline]$vault = getVault

    if ($Path) {
        [KeeperSecurity.Vault.FolderNode]$folder = $null
        if (!$vault.TryGetFolder($Script:Context.CurrentFolder, [ref]$folder)) {
            $folder = $vault.RootFolder
        }

        $components = splitKeeperPath $Path
        $rs = parseKeeperPath $components $vault $folder
        if ($rs -and !$rs[1]) {
            $folder = $rs[0]
            $uid = $folder.FolderUid
            if ($vault.TryGetFolder($uid, [ref]$folder)) {
                $Script:Context.CurrentFolder = $uid
            }
            else {
                $Script:Context.CurrentFolder = ''
            }
        }
    }
    getVaultFolderPath $vault $Script:Context.CurrentFolder
}

$Keeper_FolderPathRecordCompleter = {
    param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameters)

    $result = @()
    [KeeperSecurity.Vault.VaultOnline]$vault = $Script:Context.Vault
    if ($vault) {
        [KeeperSecurity.Vault.FolderNode] $folder = $null
        if (!$vault.TryGetFolder($Script:Context.CurrentFolder, [ref]$folder)) {
            $folder = $vault.RootFolder
        }

        $pattern = ''
        $toComplete = $wordToComplete
        if ($toComplete.Length -ge 2) {
            if ($toComplete[0] -eq '''' -and $toComplete[-1] -eq '''') {
                $toComplete = $toComplete.Substring(1, $toComplete.Length - 2)
                $toComplete = $toComplete -replace '''', ''''
            }
        }
        if ($toComplete) {
            $components = splitKeeperPath $toComplete
            if ($components.Count -gt 1) {
                if ($components[-1]) {
                    $pattern = $components[-1]
                    $components[-1] = ''
                }
                $rs = parseKeeperPath $components $vault $folder
                if ($rs -and $rs.Count -eq 2) {
                    if (!$rs[1]) {
                        $folder = $rs[0]
                    }
                    else {
                        $folder = $null
                    }
                }
            }
            else {
                if ($components) {
                    $pattern = $components
                    $components = @('')
                }
                else {
                    $folder = $vault.RootFolder
                    $pattern = ''
                    $components = @('')
                }
            }
        }
        else {
            $components = @('')
            $pattern = $wordToComplete
        }

        if ($folder) {
            $pattern += '*'
            foreach ($uid in $folder.Subfolders) {
                $subfolder = $null
                if ($vault.TryGetFolder($uid, [ref]$subfolder)) {
                    if ($subfolder.Name -like $pattern) {
                        $path = @()
                        $components | ForEach-Object { $path += $_ }
                        $path[-1] = $subfolder.Name
                        $expansion = ($path | ForEach-Object { $_ -replace '\\', '\\' }) -join $Script:PathDelimiter
                        if ($expansion -match '[\s'']') {
                            $expansion = $expansion -replace '''', ''''''
                            $expansion = "'${expansion}'"
                        }
                        $result += $expansion
                    }
                }
            }
        }
    }
    if ($result.Count -gt 0) {
        return $result
    }
    else {
        return $null
    }
}
Register-ArgumentCompleter -CommandName Set-KeeperLocation -ParameterName Path -ScriptBlock $Keeper_FolderPathRecordCompleter
New-Alias -Name kcd -Value Set-KeeperLocation


function Get-KeeperChildItem {
    <#
    .Synopsis
    Get the content of Keeper folder. Output and parameters are similar to Get-ChildItem cmdlet
 
    .Parameter Path
    Keeper folder
 
    .Parameter Filter
    Match the string in Title, Uid, Login, and Link fields
 
    .Parameter Recursive
    Get child items in subfolders recursively
 
    .Parameter Depth
    Recursion depth
 
    .Parameter SkipGrouping
    Do not group result set by folder
 
    .Parameter ObjectType
    Limit result set to Folders or Records only
#>


    [CmdletBinding()]
    Param (
        [Parameter(Position = 0)][string] $Path,
        [string] $Filter,
        [Switch] $Recursive,
        [int] $Depth,
        [Switch] $SkipGrouping,
        [ValidateSet('Folder' , 'Record')][string] $ObjectType
    )

    $showFolder = $true
    $showRecord = $true
    if ($ObjectType) {
        $showFolder = $ObjectType -eq 'Folder'
        $showRecord = !$showFolder
    }

    [KeeperSecurity.Vault.VaultOnline]$vault = getVault

    [KeeperSecurity.Vault.FolderNode] $baseDir = $null
    if (!$vault.TryGetFolder($Script:Context.CurrentFolder, [ref]$baseDir)) {
        $baseDir = $vault.RootFolder
    }
    if ($Path) {
        $components = splitKeeperPath $Path
        $rs = parseKeeperPath $components $vault $baseDir
        $baseDir = $rs[0]
    }
    [KeeperSecurity.Vault.FolderNode[]]$folders = @($baseDir)
    if ($Recursive.IsPresent) {
        $pos = 0
        $dep = 0
        while ($pos -lt $folders.Count) {
            if ($Depth -gt 0) {
                if ($dep -ge $Depth) {
                    break
                }
            }
            $lastPos = $folders.Count
            for ($i = $pos; $i -lt $lastPos; $i++) {
                foreach ($uid in $folders[$i].Subfolders) {
                    [KeeperSecurity.Vault.FolderNode] $sf = $null;
                    if ($vault.TryGetFolder($uid, [ref]$sf)) {
                        $folders += $sf
                    }
                }
            }
            $pos = $lastPos
            $dep++
        }
    }
    $entries = @()
    $recordEntries = @{}
    for ($i = 0; $i -lt $folders.Count; $i++) {
        [KeeperSecurity.Vault.FolderNode]$f = $folders[$i]
        $path = getVaultFolderPath $vault $f.FolderUid
        if ($showFolder) {
            foreach ($uid in $f.Subfolders) {
                [KeeperSecurity.Vault.FolderNode]$sf = $null
                if ($vault.TryGetFolder($uid, [ref]$sf)) {
                    $match = $true
                    if ($Filter) {
                        $match = @($sf.Name, $sf.FolderUid) | Select-String $Filter | Select-Object -First 1
                    }
                    if ($match) {
                        $entry = [PSCustomObject]@{
                            PSTypeName  = "KeeperSecurity.Commander.FolderEntry$(if ($SkipGrouping.IsPresent) {'Flat'} else {''})"
                            Uid         = $sf.FolderUid
                            Name        = $sf.Name
                            OwnerFolder = $path
                            FolderType  = $sf.FolderType
                            Shared      = $sf.FolderType -ne [KeeperSecurity.Vault.FolderType]::UserFolder
                            SortGroup   = 0
                        }
                        $entries += $entry
                    }
                }
            }
        }
        if ($showRecord) {
            foreach ($uid in $f.Records) {
                [KeeperSecurity.Vault.KeeperRecord] $r = $null
                if ($vault.TryGetKeeperRecord($uid, [ref]$r)) {
                    if ($r.Version -ne 2 -and $r.Version -ne 3) {
                        continue
                    }
                    $match = $true
                    if ($Filter) {
                        $match = @($r.Title, $r.Uid) | Select-String $Filter | Select-Object -First 1
                    }
                    if ($match) {
                        if ($Flat.IsPresent -and $recordEntries.ContainsKey($uid)) {
                            $entry = $recordEntries[$uid]
                            $entry.OwnerFolder += $path
                        }
                        else {
                            $type = [KeeperSecurity.Utils.RecordTypesUtils]::KeeperRecordType($r)
                            $publicInfo = [KeeperSecurity.Utils.RecordTypesUtils]::KeeperRecordPublicInformation($r)
                            $entry = [PSCustomObject]@{
                                PSTypeName        = "KeeperSecurity.Commander.RecordEntry$(if ($SkipGrouping.IsPresent) {'Flat'} else {''})"
                                Uid               = $r.Uid
                                Name              = $r.Title
                                Shared            = $r.Shared
                                Owner             = $r.Owner
                                Type              = $type
                                PublicInformation = $publicInfo
                                HasAttachments    = ($vault.RecordAttachments($r).Count -gt 0)
                                SortGroup         = 1
                            }
                            if ($SkipGrouping.IsPresent) {
                                Add-Member -InputObject $entry -NotePropertyName OwnerFolder -NotePropertyValue @($path)
                            }
                            else {
                                Add-Member -InputObject $entry -NotePropertyName OwnerFolder -NotePropertyValue $path
                            }

                            $recordEntries[$uid] = $entry
                            $entry = $null
                        }
                    }
                }
            }
        }
    }
    if ($recordEntries) {
        $entries += $recordEntries.Values
    }
    if ($entries) {
        if ($SkipGrouping.IsPresent) {
            $entries | Sort-Object SortGroup, Name
        }
        else {
            $entries | Sort-Object OwnerFolder, SortGroup, Name
        }
    }
}
Register-ArgumentCompleter -CommandName Get-KeeperChildItem -ParameterName Path -ScriptBlock $Keeper_FolderPathRecordCompleter
New-Alias -Name kdir -Value Get-KeeperChildItem


function Get-KeeperObject {
    <#
    .Synopsis
    Get Keeper object by Uid
 
    .Parameter Uid
    Keeper UID
 
    .Parameter ObjectType
    One of the following Record, SharedFolder, Folder, Team
 
    .Parameter PropertyName
    Return object property not the entire object
#>

    [CmdletBinding()]
    Param (
        [Parameter(Mandatory = $true, ValueFromPipeline = $true)][string[]] $Uid,
        [string] [ValidateSet('Record' , 'SharedFolder', 'Folder', 'Team')] $ObjectType,
        [string] $PropertyName
    )

    Begin {
        [KeeperSecurity.Vault.VaultOnline]$vault = getVault

        $testRecord = if ($ObjectType) { $ObjectType -eq 'Record' } else { $true }
        $testSharedFolder = if ($ObjectType) { $ObjectType -eq 'SharedFolder' } else { $true }
        $testFolder = if ($ObjectType) { $ObjectType -eq 'Folder' } else { $true }
        $testTeam = if ($ObjectType) { $ObjectType -eq 'Team' } else { $true }
    }
    Process {
        ForEach ($oid in $Uid) {
            if ($testRecord) {
                [KeeperSecurity.Vault.KeeperRecord] $record = $null
                if ($vault.TryGetKeeperRecord($oid, [ref]$record)) {
                    if ($PropertyName) {
                        $mp = $record | Get-Member -MemberType Properties -Name $PropertyName
                        if ($mp) {
                            $record | Select-Object -ExpandProperty $PropertyName
                        }
                    }
                    else {
                        $record
                    }
                    continue
                }
            }
            if ($testSharedFolder) {
                [KeeperSecurity.Vault.SharedFolder] $sf = $null
                if ($vault.TryGetSharedFolder($oid, [ref]$sf)) {
                    if ($PropertyName) {
                        $mp = $sf | Get-Member -MemberType Properties -Name $PropertyName
                        if ($mp) {
                            $sf | Select-Object -ExpandProperty $PropertyName
                        }
                    }
                    else {
                        $sf
                    }
                    continue
                }
            }
            if ($testFolder) {
                [KeeperSecurity.Vault.FolderNode] $f = $null
                if ($vault.TryGetFolder($oid, [ref]$f)) {
                    if ($PropertyName) {
                        $mp = $f | Get-Member -MemberType Properties -Name $PropertyName
                        if ($mp) {
                            $f | Select-Object -ExpandProperty $PropertyName
                        }
                    }
                    else {
                        $f
                    }
                    continue
                }
            }
            if ($testTeam) {
                [KeeperSecurity.Vault.Team] $t = $null
                if ($vault.TryGetTeam($oid, [ref]$t)) {
                    if ($PropertyName) {
                        $mp = $t | Get-Member -MemberType Properties -Name $PropertyName
                        if ($mp) {
                            $t | Select-Object -ExpandProperty $PropertyName
                        }
                    }
                    else {
                        $t
                    }
                    continue
                }
                ensureAvalableLoaded
                [KeeperSecurity.Vault.TeamInfo] $teamInfo = $null
                $teamInfo = $Script:Context.AvailableTeams | Where-Object { $_.TeamUid -ceq $oid } | Select-Object -First 1
                if ($teamInfo) {
                    if ($PropertyName) {
                        $mp = $teamInfo | Get-Member -MemberType Properties -Name $PropertyName
                        if ($mp) {
                            $teamInfo | Select-Object -ExpandProperty $PropertyName
                        }
                    }
                    else {
                        $teamInfo
                    }
                    continue
                }
            }
        }
    }
}
New-Alias -Name ko -Value Get-KeeperObject

function parseKeeperPath {
    Param (
        [string[]]$components,
        [KeeperSecurity.Vault.VaultOnline]$vault,
        [KeeperSecurity.Vault.FolderNode]$folder
    )
    if ($components) {
        if (!$components[0]) {
            $folder = $vault.RootFolder
            $_, $components = $components
        }
        while ($components) {
            $resume = $false
            $component, $rest = $components
            if ($component -eq '..') {
                if ($folder.ParentUid) {
                    $resume = $vault.TryGetFolder($folder.ParentUid, [ref]$folder)
                }
                else {
                    $folder = $vault.RootFolder
                    $resume = $true
                }
            }
            elseif (!$component -or $component -eq '.') {
                $resume = $true
            }
            else {
                foreach ($x in $folder.Subfolders) {
                    [KeeperSecurity.Vault.FolderNode] $subfolder = $null
                    if ($vault.TryGetFolder($x, [ref]$subfolder)) {
                        if ($subfolder.Name -eq $component) {
                            $resume = $true
                            $folder = $subfolder
                            break
                        }
                    }
                }
            }

            if ($resume) {
                $components = $rest
            }
            else {
                break
            }
        }
        $folder
        $components -join $Script:PathDelimiter
    }
    else {
        $folder
        $path
    }
}

function splitKeeperPath {
    Param ([string] $path)

    [bool]$isDelimiter = $false
    [string]$component = ''
    foreach ($x in $path.ToCharArray()) {
        if ($x -eq $Script:PathDelimiter) {
            if ($isDelimiter) {
                $component += $x
                $isDelimiter = $false
            }
            else {
                $isDelimiter = $true
            }
        }
        else {
            if ($isDelimiter) {
                $component
                $component = ''
                $isDelimiter = $false
            }
            $component += $x
        }
    }
    $component
    if ($isDelimiter) {
        ''
    }
}

function exportKeeperNode {
    Param ([KeeperSecurity.Vault.FolderNode] $folder)
    [PSCustomObject]@{
        PSTypeName = 'KeeperSecurity.Commander.FolderInfo'
        FolderUid  = $folder.FolderUid
        Path       = getVaultFolderPath $vault $folder.FolderUid
        Name       = $folder.Name
        ParentUid  = $folder.ParentUid
        FolderType = $folder.FolderType
    }
}

function escapePathComponent {
    Param ([string] $component)

    $component = $component -replace '\\', '\\'
    $component = $component -replace '''', ''''''
    if ($component -match '[\s'']') {
        "'${component}'"
    }
    else {
        $component
    }
}

function getVaultFolderPath {
    Param (
        [KeeperSecurity.Vault.VaultOnline]$vault,
        [string] $folderUid
    )

    $comps = @()
    traverseFolderToRoot $vault $folderUid ([ref]$comps)
    $path = ''
    if ($comps) {
        [Array]::Reverse($comps)
        $comps += ''
        $path = ($comps | ForEach-Object { $_ -replace [Regex]::Escape($Script:PathDelimiter), "${Script:PathDelimiter}${Script:PathDelimiter}" }) -join $Script:PathDelimiter
    }
    "${Script:PathDelimiter}${path}"
}

function traverseFolderToRoot ([KeeperSecurity.Vault.VaultOnline]$vault, [string] $folderUid, [ref] $components) {
    if ($folderUid) {
        [KeeperSecurity.Vault.FolderNode]$folder = $null
        if ($vault.TryGetFolder($folderUid, [ref]$folder)) {
            $components.Value += $folder.Name
            traverseFolderToRoot $vault $folder.ParentUid $components
        }
    }
}

# SIG # Begin signature block
# MIIR1wYJKoZIhvcNAQcCoIIRyDCCEcQCAQExCzAJBgUrDgMCGgUAMGkGCisGAQQB
# gjcCAQSgWzBZMDQGCisGAQQBgjcCAR4wJgIDAQAABBAfzDtgWUsITrck0sYpfvNR
# AgEAAgEAAgEAAgEAAgEAMCEwCQYFKw4DAhoFAAQUahiInESueYV/b5LEiYRWzxmO
# 6+Kggg4jMIIGsDCCBJigAwIBAgIQCK1AsmDSnEyfXs2pvZOu2TANBgkqhkiG9w0B
# AQwFADBiMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYD
# VQQLExB3d3cuZGlnaWNlcnQuY29tMSEwHwYDVQQDExhEaWdpQ2VydCBUcnVzdGVk
# IFJvb3QgRzQwHhcNMjEwNDI5MDAwMDAwWhcNMzYwNDI4MjM1OTU5WjBpMQswCQYD
# VQQGEwJVUzEXMBUGA1UEChMORGlnaUNlcnQsIEluYy4xQTA/BgNVBAMTOERpZ2lD
# ZXJ0IFRydXN0ZWQgRzQgQ29kZSBTaWduaW5nIFJTQTQwOTYgU0hBMzg0IDIwMjEg
# Q0ExMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA1bQvQtAorXi3XdU5
# WRuxiEL1M4zrPYGXcMW7xIUmMJ+kjmjYXPXrNCQH4UtP03hD9BfXHtr50tVnGlJP
# DqFX/IiZwZHMgQM+TXAkZLON4gh9NH1MgFcSa0OamfLFOx/y78tHWhOmTLMBICXz
# ENOLsvsI8IrgnQnAZaf6mIBJNYc9URnokCF4RS6hnyzhGMIazMXuk0lwQjKP+8bq
# HPNlaJGiTUyCEUhSaN4QvRRXXegYE2XFf7JPhSxIpFaENdb5LpyqABXRN/4aBpTC
# fMjqGzLmysL0p6MDDnSlrzm2q2AS4+jWufcx4dyt5Big2MEjR0ezoQ9uo6ttmAaD
# G7dqZy3SvUQakhCBj7A7CdfHmzJawv9qYFSLScGT7eG0XOBv6yb5jNWy+TgQ5urO
# kfW+0/tvk2E0XLyTRSiDNipmKF+wc86LJiUGsoPUXPYVGUztYuBeM/Lo6OwKp7AD
# K5GyNnm+960IHnWmZcy740hQ83eRGv7bUKJGyGFYmPV8AhY8gyitOYbs1LcNU9D4
# R+Z1MI3sMJN2FKZbS110YU0/EpF23r9Yy3IQKUHw1cVtJnZoEUETWJrcJisB9IlN
# Wdt4z4FKPkBHX8mBUHOFECMhWWCKZFTBzCEa6DgZfGYczXg4RTCZT/9jT0y7qg0I
# U0F8WD1Hs/q27IwyCQLMbDwMVhECAwEAAaOCAVkwggFVMBIGA1UdEwEB/wQIMAYB
# Af8CAQAwHQYDVR0OBBYEFGg34Ou2O/hfEYb7/mF7CIhl9E5CMB8GA1UdIwQYMBaA
# FOzX44LScV1kTN8uZz/nupiuHA9PMA4GA1UdDwEB/wQEAwIBhjATBgNVHSUEDDAK
# BggrBgEFBQcDAzB3BggrBgEFBQcBAQRrMGkwJAYIKwYBBQUHMAGGGGh0dHA6Ly9v
# Y3NwLmRpZ2ljZXJ0LmNvbTBBBggrBgEFBQcwAoY1aHR0cDovL2NhY2VydHMuZGln
# aWNlcnQuY29tL0RpZ2lDZXJ0VHJ1c3RlZFJvb3RHNC5jcnQwQwYDVR0fBDwwOjA4
# oDagNIYyaHR0cDovL2NybDMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0VHJ1c3RlZFJv
# b3RHNC5jcmwwHAYDVR0gBBUwEzAHBgVngQwBAzAIBgZngQwBBAEwDQYJKoZIhvcN
# AQEMBQADggIBADojRD2NCHbuj7w6mdNW4AIapfhINPMstuZ0ZveUcrEAyq9sMCcT
# Ep6QRJ9L/Z6jfCbVN7w6XUhtldU/SfQnuxaBRVD9nL22heB2fjdxyyL3WqqQz/WT
# auPrINHVUHmImoqKwba9oUgYftzYgBoRGRjNYZmBVvbJ43bnxOQbX0P4PpT/djk9
# ntSZz0rdKOtfJqGVWEjVGv7XJz/9kNF2ht0csGBc8w2o7uCJob054ThO2m67Np37
# 5SFTWsPK6Wrxoj7bQ7gzyE84FJKZ9d3OVG3ZXQIUH0AzfAPilbLCIXVzUstG2MQ0
# HKKlS43Nb3Y3LIU/Gs4m6Ri+kAewQ3+ViCCCcPDMyu/9KTVcH4k4Vfc3iosJocsL
# 6TEa/y4ZXDlx4b6cpwoG1iZnt5LmTl/eeqxJzy6kdJKt2zyknIYf48FWGysj/4+1
# 6oh7cGvmoLr9Oj9FpsToFpFSi0HASIRLlk2rREDjjfAVKM7t8RhWByovEMQMCGQ8
# M4+uKIw8y4+ICw2/O/TOHnuO77Xry7fwdxPm5yg/rBKupS8ibEH5glwVZsxsDsrF
# hsP2JjMMB0ug0wcCampAMEhLNKhRILutG4UI4lkNbcoFUCvqShyepf2gpx8GdOfy
# 1lKQ/a+FSCH5Vzu0nAPthkX0tGFuv2jiJmCG6sivqf6UHedjGzqGVnhOMIIHazCC
# BVOgAwIBAgIQAnNTGQOIer82vZ1cJyDJDjANBgkqhkiG9w0BAQsFADBpMQswCQYD
# VQQGEwJVUzEXMBUGA1UEChMORGlnaUNlcnQsIEluYy4xQTA/BgNVBAMTOERpZ2lD
# ZXJ0IFRydXN0ZWQgRzQgQ29kZSBTaWduaW5nIFJTQTQwOTYgU0hBMzg0IDIwMjEg
# Q0ExMB4XDTIyMDIwMjAwMDAwMFoXDTI1MDIwMTIzNTk1OVowcDELMAkGA1UEBhMC
# VVMxETAPBgNVBAgTCElsbGlub2lzMRAwDgYDVQQHEwdDaGljYWdvMR0wGwYDVQQK
# ExRLZWVwZXIgU2VjdXJpdHkgSW5jLjEdMBsGA1UEAxMUS2VlcGVyIFNlY3VyaXR5
# IEluYy4wggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDNgTqmksdjUyKF
# 5zWkDyghf0PLWJWdzG0TX2j8B4J55xwt+B17zd4Xc3n0dvmSVAyPQANeN+mP1chf
# 4LTRn9h4jWb8Jsfn+JzyRhj/gYINYvBnpRpqoM0z7QC9Ebwj5T61Cogm9EKGcrG+
# Ujh+Z7pTqfSUrHD8NMXhDL/UpVn+w0Pb4qg7o7AH2o94n7u/qTlMGZCs+VCAvhNr
# wPABxvFY07YGb9t5/IZlPE8vG3p1vw2SbgREgFWSEQFj6X2CIhSrbiFCW/766/Mq
# EX6qm+RyF71fD4d3yShg39guaE9o+TBl1MqVCje4bK/wGoNxCho0I6Z1fBBKloyp
# vlx3gPpU7tJJ+KpuIiel9R9dGQuscqKzehPtbRc9Abr9ThN/HrLg1sFFVMdn2oMR
# 63QCUdz+B1NuS7Ap8Ti7XvAPJHzEuQDcdMcRbkIfllJVqrb9UXEFwOPzvRU2KrcQ
# 42Jlnn4T+WenPx5Nr3o/o08WLhLTicEK1OacEowyRLBmih4Gxpdk3fUAVCEkdvmq
# TSydQpl1Bk8V88dxCkB1wMZyFYLNcddBL4kUbwjso/z6f2TtfAVYs/iIRWqs7Xqt
# 4F2BBqobOGMymwg6VgVjjzDIgJCZSbjpq2IoVTci5vli6vxgSoZ01fccSaKa4Izm
# B7DbobIkIjLgPqpnCkqlHuJj5hQ9twIDAQABo4ICBjCCAgIwHwYDVR0jBBgwFoAU
# aDfg67Y7+F8Rhvv+YXsIiGX0TkIwHQYDVR0OBBYEFCZd3/KEdT2t5WTIFb3TUaM4
# sTikMA4GA1UdDwEB/wQEAwIHgDATBgNVHSUEDDAKBggrBgEFBQcDAzCBtQYDVR0f
# BIGtMIGqMFOgUaBPhk1odHRwOi8vY3JsMy5kaWdpY2VydC5jb20vRGlnaUNlcnRU
# cnVzdGVkRzRDb2RlU2lnbmluZ1JTQTQwOTZTSEEzODQyMDIxQ0ExLmNybDBToFGg
# T4ZNaHR0cDovL2NybDQuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0VHJ1c3RlZEc0Q29k
# ZVNpZ25pbmdSU0E0MDk2U0hBMzg0MjAyMUNBMS5jcmwwPgYDVR0gBDcwNTAzBgZn
# gQwBBAEwKTAnBggrBgEFBQcCARYbaHR0cDovL3d3dy5kaWdpY2VydC5jb20vQ1BT
# MIGUBggrBgEFBQcBAQSBhzCBhDAkBggrBgEFBQcwAYYYaHR0cDovL29jc3AuZGln
# aWNlcnQuY29tMFwGCCsGAQUFBzAChlBodHRwOi8vY2FjZXJ0cy5kaWdpY2VydC5j
# b20vRGlnaUNlcnRUcnVzdGVkRzRDb2RlU2lnbmluZ1JTQTQwOTZTSEEzODQyMDIx
# Q0ExLmNydDAMBgNVHRMBAf8EAjAAMA0GCSqGSIb3DQEBCwUAA4ICAQAGyDM3Cbxq
# Auhr8O2xwOoCSVKmFkXqicwlrugwLW44Y4WX+imvTrGfjj2S99k/4D5H8DgtW/u8
# tOxcCoehTOCIEwP5TLrieHppsqAR4jaJRcdAHOWiJ1bmwQBv/cBU9vaelL0oXxxf
# TwD9oDaQNuyq6p+nIJMqbKv33b8AWGe3zq4JwblaFjRDL5lUDNhPx3g/pm7JhnbX
# 7QTKydAJvpbuP5cqUH1GEeVMjc5vEELtGNy/fy7Ekm4dndX4IZcFXW5L0Lx8cReB
# hIZwA+pzdzTWQYvfxgRMb/j2uY+Tkb6Wz2x9BBS1UXiP2qrs3rhQv8DZRkUSqnko
# YD4uJP8gk8BXcIXIThgEF2YCq2hBiwna5Ijbwkmjn1lWwGv15SznTOTnrVApJqB1
# tB2s2ovUNV4CyKDPVr+9/CS6IQJfEZeHYcYLsIga2q5NZCrqZAasBfCwALVkALos
# DIWhs33vYLfETMSuk5Hd5JC+hLjVM3ZJwslvnc/wec2r0GNAiZ3a1aweC7NYuzRz
# 29Mi/eR/4ylmCltyZqYJ1JcC/g6eY2Q0xkdWc8P0yHfQ/3fe7+AKXXKNjfv858GW
# lg1Ck2lvwPdLqJWqj1FwJPiGRCB+WulPe0csTyWnf+ed45TXx69tZ6BZr0Xr2jXu
# ybBdJtg0NN0a62xxWrmX42CgsrzHzRm7OzGCAx4wggMaAgEBMH0waTELMAkGA1UE
# BhMCVVMxFzAVBgNVBAoTDkRpZ2lDZXJ0LCBJbmMuMUEwPwYDVQQDEzhEaWdpQ2Vy
# dCBUcnVzdGVkIEc0IENvZGUgU2lnbmluZyBSU0E0MDk2IFNIQTM4NCAyMDIxIENB
# MQIQAnNTGQOIer82vZ1cJyDJDjAJBgUrDgMCGgUAoHgwGAYKKwYBBAGCNwIBDDEK
# MAigAoAAoQKAADAZBgkqhkiG9w0BCQMxDAYKKwYBBAGCNwIBBDAcBgorBgEEAYI3
# AgELMQ4wDAYKKwYBBAGCNwIBFTAjBgkqhkiG9w0BCQQxFgQUxp9o7l0Vdo0NZjmE
# kki1rREZhP0wDQYJKoZIhvcNAQEBBQAEggIARkudfVCU5rmyo6mWT5RDtU6lVvF2
# insI2QFbecBYpNX1QbaGQfEt6R7PgEZR575hfvRb/C/RCmV8oAnMDOdtKwPosTOp
# YVHBAUsRIABvSPxqW4HkDtAvsL4BU+/2ReKitX+7MnbfJzoxKZCQf9PDQzFONpnu
# Cfwxym5ENMcAccxRuCt60JmVKzrBaUVpF2oVeUAXlKZswukEHOqLAL3m/oomIav5
# geO94QLGz+hUSZt/nUj3351R1POKBbwHJKFPPVxkxXqztNXL99kTV2quOd6FdTnp
# rfVnu7bhiykKxlOYr0doPJNz67zb+EtJGGyE3maBDpuelB1qJVNvHsmfA4cY9kMq
# S9xuWw0hSSxFtm7R1q88+qZ1dF2/9TEgswI9XJ38Ld7POJyj47W6r2XpMUGWc/3Y
# cZxLhHRidh6mBlwlaXulkdZ/EXMghIjrhqTBknUtysqT+/4IM+GAqbp5Y0kBaHAb
# rd6uG3DGZuZgr3/JVn23KGplY1Kxwc8XTIsaelT3SisWS8I68c8LaFVWj7iYSVE7
# ni52kP0Nl9KppS1jwdgirMGWxpZyWw8GV4hQ1NOH9YpgP/Z0hlbUbTKQH82OnGAa
# wW63Gpsh6nNIR7/A+bdd8O/wNzkR24N4nEoON7niK2q0sDmuBQceJuZ+7TOmJeTj
# 9RGGAhCK7MkEmDs=
# SIG # End signature block