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] $currentDir = $null if (!$vault.TryGetFolder($Script:Context.CurrentFolder, [ref]$currentDir)) { $currentDir = $vault.RootFolder } [KeeperSecurity.Vault.FolderNode] $baseDir = $null if ($Path) { if (-not $vault.TryGetFolder($Path, [ref]$baseDir)) { $components = splitKeeperPath $Path $rs = parseKeeperPath $components $vault $currentDir if ($rs -is [array]) { if (-not $rs[1]) { $baseDir = $rs[0] } } } } else { $baseDir = $currentDir } if (-not $baseDir) { Write-Error -Message "Cannot find path '$Path'" -ErrorAction Stop } [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 # AgEAAgEAAgEAAgEAAgEAMCEwCQYFKw4DAhoFAAQU5hw2ON++iKVbUAwIf0g9Uc9H # xs2ggg4jMIIGsDCCBJigAwIBAgIQCK1AsmDSnEyfXs2pvZOu2TANBgkqhkiG9w0B # 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 # AgELMQ4wDAYKKwYBBAGCNwIBFTAjBgkqhkiG9w0BCQQxFgQUq2kV3+xt+e8iugVh # xpDQ5hZaVJ4wDQYJKoZIhvcNAQEBBQAEggIAMZtulHVpfiSQ3jC+UCucmFVLgByr # dqcwyTDyI+spGxE8YHjDMRZBOV/50GSbjuhquoYLul28hnskbq9OXmkhYRlHIf35 # JIuZmsMrzm5wbjPXn41s31aK6WoAtHqJEyoxSU39qWjOarNxM5BEOkDVUL7sdTqY # iS8VqL8JqQN8nfq2bkjUQ/0Z/loLZIopzIgVXMOx0OAyO6MRNty0eAWwpJDpmz+G # iU9Gs1jIflpxR/AcEmpGPmWweCeDBEIzdzZ4O/QNNKYeH+pj4s3vm+pE7RP2UNuA # h/VV40jBRa0aWZ7K2XMb4sdL3joI0IDn5bmb9oJ7Fv3MFaodmLOD5lmpXcRWyJ+Y # zKl4nZchobRkqjoGmVlZ5SSlO9zLHEDZwQ1kVNYgrPw62jexbGO2av81HEBIo9MR # Ts2e5y4gSens/AI32gGtR+T77gfCBsaeG/39fUjGiQwSJvH8CXhDJYJrWtJDwHhy # Eori5cKku04IKkMGR1xiTHbXIcWNP3/aB71mSSKqcsKrCR/ca5u3EnB6z+aledT3 # vkIXvRjGH67Az1Dn9nwnWG4p9Ood6yXZiqUFf/eNgZKOGEXZ4oRLMWZlcqBaGp+z # Ir9r/wf869/iU5moKmjVkaYgJ+HVVB9zmZLRgebfMb7snQZwf/WhZZLZ5kyrT/aJ # ixSQWcDcPcv007U= # SIG # End signature block |