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 # MIIngQYJKoZIhvcNAQcCoIIncjCCJ24CAQExDzANBglghkgBZQMEAgEFADB5Bgor # BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG # KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCCZANkTfISmh2uw # kx7zg5mGJtF8wPOMXtMO0/SrKpKFPaCCIQQwggWNMIIEdaADAgECAhAOmxiO+dAt # 5+/bUOIIQBhaMA0GCSqGSIb3DQEBDAUAMGUxCzAJBgNVBAYTAlVTMRUwEwYDVQQK # EwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xJDAiBgNV # BAMTG0RpZ2lDZXJ0IEFzc3VyZWQgSUQgUm9vdCBDQTAeFw0yMjA4MDEwMDAwMDBa # Fw0zMTExMDkyMzU5NTlaMGIxCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2Vy # dCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xITAfBgNVBAMTGERpZ2lD # ZXJ0IFRydXN0ZWQgUm9vdCBHNDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoC # ggIBAL/mkHNo3rvkXUo8MCIwaTPswqclLskhPfKK2FnC4SmnPVirdprNrnsbhA3E # MB/zG6Q4FutWxpdtHauyefLKEdLkX9YFPFIPUh/GnhWlfr6fqVcWWVVyr2iTcMKy # unWZanMylNEQRBAu34LzB4TmdDttceItDBvuINXJIB1jKS3O7F5OyJP4IWGbNOsF # xl7sWxq868nPzaw0QF+xembud8hIqGZXV59UWI4MK7dPpzDZVu7Ke13jrclPXuU1 # 5zHL2pNe3I6PgNq2kZhAkHnDeMe2scS1ahg4AxCN2NQ3pC4FfYj1gj4QkXCrVYJB # MtfbBHMqbpEBfCFM1LyuGwN1XXhm2ToxRJozQL8I11pJpMLmqaBn3aQnvKFPObUR # WBf3JFxGj2T3wWmIdph2PVldQnaHiZdpekjw4KISG2aadMreSx7nDmOu5tTvkpI6 # nj3cAORFJYm2mkQZK37AlLTSYW3rM9nF30sEAMx9HJXDj/chsrIRt7t/8tWMcCxB # YKqxYxhElRp2Yn72gLD76GSmM9GJB+G9t+ZDpBi4pncB4Q+UDCEdslQpJYls5Q5S # UUd0viastkF13nqsX40/ybzTQRESW+UQUOsxxcpyFiIJ33xMdT9j7CFfxCBRa2+x # q4aLT8LWRV+dIPyhHsXAj6KxfgommfXkaS+YHS312amyHeUbAgMBAAGjggE6MIIB # NjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBTs1+OC0nFdZEzfLmc/57qYrhwP # TzAfBgNVHSMEGDAWgBRF66Kv9JLLgjEtUYunpyGd823IDzAOBgNVHQ8BAf8EBAMC # AYYweQYIKwYBBQUHAQEEbTBrMCQGCCsGAQUFBzABhhhodHRwOi8vb2NzcC5kaWdp # Y2VydC5jb20wQwYIKwYBBQUHMAKGN2h0dHA6Ly9jYWNlcnRzLmRpZ2ljZXJ0LmNv # bS9EaWdpQ2VydEFzc3VyZWRJRFJvb3RDQS5jcnQwRQYDVR0fBD4wPDA6oDigNoY0 # aHR0cDovL2NybDMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0QXNzdXJlZElEUm9vdENB # LmNybDARBgNVHSAECjAIMAYGBFUdIAAwDQYJKoZIhvcNAQEMBQADggEBAHCgv0Nc # Vec4X6CjdBs9thbX979XB72arKGHLOyFXqkauyL4hxppVCLtpIh3bb0aFPQTSnov # Lbc47/T/gLn4offyct4kvFIDyE7QKt76LVbP+fT3rDB6mouyXtTP0UNEm0Mh65Zy # oUi0mcudT6cGAxN3J0TU53/oWajwvy8LpunyNDzs9wPHh6jSTEAZNUZqaVSwuKFW # juyk1T3osdz9HNj0d1pcVIxv76FQPfx2CWiEn2/K2yCNNWAcAgPLILCsWKAOQGPF # mCLBsln1VWvPJ6tsds5vIy30fnFqI2si/xK4VC0nftg62fC2h5b9W9FcrBjDTZ9z # twGpn1eqXijiuZQwggauMIIElqADAgECAhAHNje3JFR82Ees/ShmKl5bMA0GCSqG # SIb3DQEBCwUAMGIxCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMx # GTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xITAfBgNVBAMTGERpZ2lDZXJ0IFRy # dXN0ZWQgUm9vdCBHNDAeFw0yMjAzMjMwMDAwMDBaFw0zNzAzMjIyMzU5NTlaMGMx # CzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5EaWdpQ2VydCwgSW5jLjE7MDkGA1UEAxMy # RGlnaUNlcnQgVHJ1c3RlZCBHNCBSU0E0MDk2IFNIQTI1NiBUaW1lU3RhbXBpbmcg # Q0EwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDGhjUGSbPBPXJJUVXH # JQPE8pE3qZdRodbSg9GeTKJtoLDMg/la9hGhRBVCX6SI82j6ffOciQt/nR+eDzMf # UBMLJnOWbfhXqAJ9/UO0hNoR8XOxs+4rgISKIhjf69o9xBd/qxkrPkLcZ47qUT3w # 1lbU5ygt69OxtXXnHwZljZQp09nsad/ZkIdGAHvbREGJ3HxqV3rwN3mfXazL6IRk # tFLydkf3YYMZ3V+0VAshaG43IbtArF+y3kp9zvU5EmfvDqVjbOSmxR3NNg1c1eYb # qMFkdECnwHLFuk4fsbVYTXn+149zk6wsOeKlSNbwsDETqVcplicu9Yemj052FVUm # cJgmf6AaRyBD40NjgHt1biclkJg6OBGz9vae5jtb7IHeIhTZgirHkr+g3uM+onP6 # 5x9abJTyUpURK1h0QCirc0PO30qhHGs4xSnzyqqWc0Jon7ZGs506o9UD4L/wojzK # QtwYSH8UNM/STKvvmz3+DrhkKvp1KCRB7UK/BZxmSVJQ9FHzNklNiyDSLFc1eSuo # 80VgvCONWPfcYd6T/jnA+bIwpUzX6ZhKWD7TA4j+s4/TXkt2ElGTyYwMO1uKIqjB # Jgj5FBASA31fI7tk42PgpuE+9sJ0sj8eCXbsq11GdeJgo1gJASgADoRU7s7pXche # MBK9Rp6103a50g5rmQzSM7TNsQIDAQABo4IBXTCCAVkwEgYDVR0TAQH/BAgwBgEB # /wIBADAdBgNVHQ4EFgQUuhbZbU2FL3MpdpovdYxqII+eyG8wHwYDVR0jBBgwFoAU # 7NfjgtJxXWRM3y5nP+e6mK4cD08wDgYDVR0PAQH/BAQDAgGGMBMGA1UdJQQMMAoG # CCsGAQUFBwMIMHcGCCsGAQUFBwEBBGswaTAkBggrBgEFBQcwAYYYaHR0cDovL29j # c3AuZGlnaWNlcnQuY29tMEEGCCsGAQUFBzAChjVodHRwOi8vY2FjZXJ0cy5kaWdp # Y2VydC5jb20vRGlnaUNlcnRUcnVzdGVkUm9vdEc0LmNydDBDBgNVHR8EPDA6MDig # NqA0hjJodHRwOi8vY3JsMy5kaWdpY2VydC5jb20vRGlnaUNlcnRUcnVzdGVkUm9v # dEc0LmNybDAgBgNVHSAEGTAXMAgGBmeBDAEEAjALBglghkgBhv1sBwEwDQYJKoZI # hvcNAQELBQADggIBAH1ZjsCTtm+YqUQiAX5m1tghQuGwGC4QTRPPMFPOvxj7x1Bd # 4ksp+3CKDaopafxpwc8dB+k+YMjYC+VcW9dth/qEICU0MWfNthKWb8RQTGIdDAiC # qBa9qVbPFXONASIlzpVpP0d3+3J0FNf/q0+KLHqrhc1DX+1gtqpPkWaeLJ7giqzl # /Yy8ZCaHbJK9nXzQcAp876i8dU+6WvepELJd6f8oVInw1YpxdmXazPByoyP6wCeC # RK6ZJxurJB4mwbfeKuv2nrF5mYGjVoarCkXJ38SNoOeY+/umnXKvxMfBwWpx2cYT # gAnEtp/Nh4cku0+jSbl3ZpHxcpzpSwJSpzd+k1OsOx0ISQ+UzTl63f8lY5knLD0/ # a6fxZsNBzU+2QJshIUDQtxMkzdwdeDrknq3lNHGS1yZr5Dhzq6YBT70/O3itTK37 # xJV77QpfMzmHQXh6OOmc4d0j/R0o08f56PGYX/sr2H7yRp11LB4nLCbbbxV7HhmL # NriT1ObyF5lZynDwN7+YAN8gFk8n+2BnFqFmut1VwDophrCYoCvtlUG3OtUVmDG0 # YgkPCr2B2RP+v6TR81fZvAT6gt4y3wSJ8ADNXcL50CN/AAvkdgIm2fBldkKmKYcJ # RyvmfxqkhQ/8mJb2VVQrH4D6wPIOK+XW+6kvRBVK5xMOHds3OBqhK/bt1nz8MIIG # sDCCBJigAwIBAgIQCK1AsmDSnEyfXs2pvZOu2TANBgkqhkiG9w0BAQwFADBiMQsw # CQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cu # ZGlnaWNlcnQuY29tMSEwHwYDVQQDExhEaWdpQ2VydCBUcnVzdGVkIFJvb3QgRzQw # HhcNMjEwNDI5MDAwMDAwWhcNMzYwNDI4MjM1OTU5WjBpMQswCQYDVQQGEwJVUzEX # MBUGA1UEChMORGlnaUNlcnQsIEluYy4xQTA/BgNVBAMTOERpZ2lDZXJ0IFRydXN0 # ZWQgRzQgQ29kZSBTaWduaW5nIFJTQTQwOTYgU0hBMzg0IDIwMjEgQ0ExMIICIjAN # BgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA1bQvQtAorXi3XdU5WRuxiEL1M4zr # PYGXcMW7xIUmMJ+kjmjYXPXrNCQH4UtP03hD9BfXHtr50tVnGlJPDqFX/IiZwZHM # gQM+TXAkZLON4gh9NH1MgFcSa0OamfLFOx/y78tHWhOmTLMBICXzENOLsvsI8Irg # nQnAZaf6mIBJNYc9URnokCF4RS6hnyzhGMIazMXuk0lwQjKP+8bqHPNlaJGiTUyC # EUhSaN4QvRRXXegYE2XFf7JPhSxIpFaENdb5LpyqABXRN/4aBpTCfMjqGzLmysL0 # p6MDDnSlrzm2q2AS4+jWufcx4dyt5Big2MEjR0ezoQ9uo6ttmAaDG7dqZy3SvUQa # khCBj7A7CdfHmzJawv9qYFSLScGT7eG0XOBv6yb5jNWy+TgQ5urOkfW+0/tvk2E0 # XLyTRSiDNipmKF+wc86LJiUGsoPUXPYVGUztYuBeM/Lo6OwKp7ADK5GyNnm+960I # HnWmZcy740hQ83eRGv7bUKJGyGFYmPV8AhY8gyitOYbs1LcNU9D4R+Z1MI3sMJN2 # FKZbS110YU0/EpF23r9Yy3IQKUHw1cVtJnZoEUETWJrcJisB9IlNWdt4z4FKPkBH # X8mBUHOFECMhWWCKZFTBzCEa6DgZfGYczXg4RTCZT/9jT0y7qg0IU0F8WD1Hs/q2 # 7IwyCQLMbDwMVhECAwEAAaOCAVkwggFVMBIGA1UdEwEB/wQIMAYBAf8CAQAwHQYD # VR0OBBYEFGg34Ou2O/hfEYb7/mF7CIhl9E5CMB8GA1UdIwQYMBaAFOzX44LScV1k # TN8uZz/nupiuHA9PMA4GA1UdDwEB/wQEAwIBhjATBgNVHSUEDDAKBggrBgEFBQcD # AzB3BggrBgEFBQcBAQRrMGkwJAYIKwYBBQUHMAGGGGh0dHA6Ly9vY3NwLmRpZ2lj # ZXJ0LmNvbTBBBggrBgEFBQcwAoY1aHR0cDovL2NhY2VydHMuZGlnaWNlcnQuY29t # L0RpZ2lDZXJ0VHJ1c3RlZFJvb3RHNC5jcnQwQwYDVR0fBDwwOjA4oDagNIYyaHR0 # cDovL2NybDMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0VHJ1c3RlZFJvb3RHNC5jcmww # HAYDVR0gBBUwEzAHBgVngQwBAzAIBgZngQwBBAEwDQYJKoZIhvcNAQEMBQADggIB # ADojRD2NCHbuj7w6mdNW4AIapfhINPMstuZ0ZveUcrEAyq9sMCcTEp6QRJ9L/Z6j # fCbVN7w6XUhtldU/SfQnuxaBRVD9nL22heB2fjdxyyL3WqqQz/WTauPrINHVUHmI # moqKwba9oUgYftzYgBoRGRjNYZmBVvbJ43bnxOQbX0P4PpT/djk9ntSZz0rdKOtf # JqGVWEjVGv7XJz/9kNF2ht0csGBc8w2o7uCJob054ThO2m67Np375SFTWsPK6Wrx # oj7bQ7gzyE84FJKZ9d3OVG3ZXQIUH0AzfAPilbLCIXVzUstG2MQ0HKKlS43Nb3Y3 # LIU/Gs4m6Ri+kAewQ3+ViCCCcPDMyu/9KTVcH4k4Vfc3iosJocsL6TEa/y4ZXDlx # 4b6cpwoG1iZnt5LmTl/eeqxJzy6kdJKt2zyknIYf48FWGysj/4+16oh7cGvmoLr9 # Oj9FpsToFpFSi0HASIRLlk2rREDjjfAVKM7t8RhWByovEMQMCGQ8M4+uKIw8y4+I # Cw2/O/TOHnuO77Xry7fwdxPm5yg/rBKupS8ibEH5glwVZsxsDsrFhsP2JjMMB0ug # 0wcCampAMEhLNKhRILutG4UI4lkNbcoFUCvqShyepf2gpx8GdOfy1lKQ/a+FSCH5 # Vzu0nAPthkX0tGFuv2jiJmCG6sivqf6UHedjGzqGVnhOMIIGvDCCBKSgAwIBAgIQ # C65mvFq6f5WHxvnpBOMzBDANBgkqhkiG9w0BAQsFADBjMQswCQYDVQQGEwJVUzEX # MBUGA1UEChMORGlnaUNlcnQsIEluYy4xOzA5BgNVBAMTMkRpZ2lDZXJ0IFRydXN0 # ZWQgRzQgUlNBNDA5NiBTSEEyNTYgVGltZVN0YW1waW5nIENBMB4XDTI0MDkyNjAw # MDAwMFoXDTM1MTEyNTIzNTk1OVowQjELMAkGA1UEBhMCVVMxETAPBgNVBAoTCERp # Z2lDZXJ0MSAwHgYDVQQDExdEaWdpQ2VydCBUaW1lc3RhbXAgMjAyNDCCAiIwDQYJ # KoZIhvcNAQEBBQADggIPADCCAgoCggIBAL5qc5/2lSGrljC6W23mWaO16P2RHxjE # iDtqmeOlwf0KMCBDEr4IxHRGd7+L660x5XltSVhhK64zi9CeC9B6lUdXM0s71EOc # Re8+CEJp+3R2O8oo76EO7o5tLuslxdr9Qq82aKcpA9O//X6QE+AcaU/byaCagLD/ # GLoUb35SfWHh43rOH3bpLEx7pZ7avVnpUVmPvkxT8c2a2yC0WMp8hMu60tZR0Cha # V76Nhnj37DEYTX9ReNZ8hIOYe4jl7/r419CvEYVIrH6sN00yx49boUuumF9i2T8U # uKGn9966fR5X6kgXj3o5WHhHVO+NBikDO0mlUh902wS/Eeh8F/UFaRp1z5SnROHw # SJ+QQRZ1fisD8UTVDSupWJNstVkiqLq+ISTdEjJKGjVfIcsgA4l9cbk8Smlzddh4 # EfvFrpVNnes4c16Jidj5XiPVdsn5n10jxmGpxoMc6iPkoaDhi6JjHd5ibfdp5uzI # Xp4P0wXkgNs+CO/CacBqU0R4k+8h6gYldp4FCMgrXdKWfM4N0u25OEAuEa3Jyidx # W48jwBqIJqImd93NRxvd1aepSeNeREXAu2xUDEW8aqzFQDYmr9ZONuc2MhTMizch # NULpUEoA6Vva7b1XCB+1rxvbKmLqfY/M/SdV6mwWTyeVy5Z/JkvMFpnQy5wR14GJ # cv6dQ4aEKOX5AgMBAAGjggGLMIIBhzAOBgNVHQ8BAf8EBAMCB4AwDAYDVR0TAQH/ # BAIwADAWBgNVHSUBAf8EDDAKBggrBgEFBQcDCDAgBgNVHSAEGTAXMAgGBmeBDAEE # AjALBglghkgBhv1sBwEwHwYDVR0jBBgwFoAUuhbZbU2FL3MpdpovdYxqII+eyG8w # HQYDVR0OBBYEFJ9XLAN3DigVkGalY17uT5IfdqBbMFoGA1UdHwRTMFEwT6BNoEuG # SWh0dHA6Ly9jcmwzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydFRydXN0ZWRHNFJTQTQw # OTZTSEEyNTZUaW1lU3RhbXBpbmdDQS5jcmwwgZAGCCsGAQUFBwEBBIGDMIGAMCQG # CCsGAQUFBzABhhhodHRwOi8vb2NzcC5kaWdpY2VydC5jb20wWAYIKwYBBQUHMAKG # TGh0dHA6Ly9jYWNlcnRzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydFRydXN0ZWRHNFJT # QTQwOTZTSEEyNTZUaW1lU3RhbXBpbmdDQS5jcnQwDQYJKoZIhvcNAQELBQADggIB # AD2tHh92mVvjOIQSR9lDkfYR25tOCB3RKE/P09x7gUsmXqt40ouRl3lj+8QioVYq # 3igpwrPvBmZdrlWBb0HvqT00nFSXgmUrDKNSQqGTdpjHsPy+LaalTW0qVjvUBhcH # zBMutB6HzeledbDCzFzUy34VarPnvIWrqVogK0qM8gJhh/+qDEAIdO/KkYesLyTV # OoJ4eTq7gj9UFAL1UruJKlTnCVaM2UeUUW/8z3fvjxhN6hdT98Vr2FYlCS7Mbb4H # v5swO+aAXxWUm3WpByXtgVQxiBlTVYzqfLDbe9PpBKDBfk+rabTFDZXoUke7zPgt # d7/fvWTlCs30VAGEsshJmLbJ6ZbQ/xll/HjO9JbNVekBv2Tgem+mLptR7yIrpaid # RJXrI+UzB6vAlk/8a1u7cIqV0yef4uaZFORNekUgQHTqddmsPCEIYQP7xGxZBIhd # mm4bhYsVA6G2WgNFYagLDBzpmk9104WQzYuVNsxyoVLObhx3RugaEGru+SojW4dH # PoWrUhftNpFC5H7QEY7MhKRyrBe7ucykW7eaCuWBsBb4HOKRFVDcrZgdwaSIqMDi # CLg4D+TPVgKx2EgEdeoHNHT9l3ZDBD+XgbF+23/zBjeCtxz+dL/9NWR6P2eZRi7z # cEO1xwcdcqJsyz/JceENc2Sg8h3KeFUCS7tpFk7CrDqkMIIHSTCCBTGgAwIBAgIQ # BaOjGrg1T58olh09AgdhuDANBgkqhkiG9w0BAQsFADBpMQswCQYDVQQGEwJVUzEX # MBUGA1UEChMORGlnaUNlcnQsIEluYy4xQTA/BgNVBAMTOERpZ2lDZXJ0IFRydXN0 # ZWQgRzQgQ29kZSBTaWduaW5nIFJTQTQwOTYgU0hBMzg0IDIwMjEgQ0ExMB4XDTI0 # MTIzMTAwMDAwMFoXDTI1MTIzMDIzNTk1OVowgdExEzARBgsrBgEEAYI3PAIBAxMC # VVMxGTAXBgsrBgEEAYI3PAIBAhMIRGVsYXdhcmUxHTAbBgNVBA8MFFByaXZhdGUg # T3JnYW5pemF0aW9uMRAwDgYDVQQFEwczNDA3OTg1MQswCQYDVQQGEwJVUzERMA8G # A1UECBMISWxsaW5vaXMxEDAOBgNVBAcTB0NoaWNhZ28xHTAbBgNVBAoTFEtlZXBl # ciBTZWN1cml0eSBJbmMuMR0wGwYDVQQDExRLZWVwZXIgU2VjdXJpdHkgSW5jLjCC # AaIwDQYJKoZIhvcNAQEBBQADggGPADCCAYoCggGBAM7/rBevApUP+XJjlSxdyASA # AnLFQ1r4NFXPo/S0RaTv1OCahApEeSN6oy+0OwbLNlwaQeooOanMcZhh64/+fF8S # zCMHDc/Pv8aBsd1B2XIw/VT+Nawfj0NxAX1zpKPp/tPqavm6smRDMOAeOo7qLxzI # u68bS2EnqvST1367tMpxhggrVl3GYKPhdCPeNDRskwheCSxI2czR8oe7mguo2nVa # ZR5VEq4xYkMZwTuT7RN8ER4r5crOSbJFyabp79SgYP7NyKmDcYZ6XJ26AfZsEDZr # e4VhzaqO0rl8i5HBmVmDKwU0PaIoAUdyeultIaS5oe0FjcTjGtrkBl+B7TCtvN1J # RE9Tmy3spnqLyvlRhrVJdDKCGovQKKJk87BAjIoiNSmEXs0H0PbB1ZYOA6m4ce7/ # BOmUafliYWBqrWHmHixqi/ha5ZKxKlYxGlikD4p1WlMmDEBhg3RPodW1Z5eGq92Z # exMGOWsfOQp3YhTDdMOA7tjWP2XzAaebGxCeOENEpQIDAQABo4ICAjCCAf4wHwYD # VR0jBBgwFoAUaDfg67Y7+F8Rhvv+YXsIiGX0TkIwHQYDVR0OBBYEFOcovsKg6xAz # zjzRmmWQRpa7p47MMD0GA1UdIAQ2MDQwMgYFZ4EMAQMwKTAnBggrBgEFBQcCARYb # aHR0cDovL3d3dy5kaWdpY2VydC5jb20vQ1BTMA4GA1UdDwEB/wQEAwIHgDATBgNV # HSUEDDAKBggrBgEFBQcDAzCBtQYDVR0fBIGtMIGqMFOgUaBPhk1odHRwOi8vY3Js # My5kaWdpY2VydC5jb20vRGlnaUNlcnRUcnVzdGVkRzRDb2RlU2lnbmluZ1JTQTQw # OTZTSEEzODQyMDIxQ0ExLmNybDBToFGgT4ZNaHR0cDovL2NybDQuZGlnaWNlcnQu # Y29tL0RpZ2lDZXJ0VHJ1c3RlZEc0Q29kZVNpZ25pbmdSU0E0MDk2U0hBMzg0MjAy # MUNBMS5jcmwwgZQGCCsGAQUFBwEBBIGHMIGEMCQGCCsGAQUFBzABhhhodHRwOi8v # b2NzcC5kaWdpY2VydC5jb20wXAYIKwYBBQUHMAKGUGh0dHA6Ly9jYWNlcnRzLmRp # Z2ljZXJ0LmNvbS9EaWdpQ2VydFRydXN0ZWRHNENvZGVTaWduaW5nUlNBNDA5NlNI # QTM4NDIwMjFDQTEuY3J0MAkGA1UdEwQCMAAwDQYJKoZIhvcNAQELBQADggIBALIq # AoEjkKZluMiOffwU+V+wiKkmDblKIZymyszEZot+niB6g7tRXrWkQo6gn8OG2qG6 # IO8L+o0VvwW0+V08p6gVqb0jeR9kCm7kDZk2RmzevhZDrRbZj0Q7Kb3pIeD9KEuc # RfEF0UGqgp0q7jerFXPzKtQk5kJpP65sSRV7bghIMWtq5sdHn/iGUMj+8Fd9AExq # 4kR+dyTw/6p1ZFiY7pIv4YAjjDrjkyUMSogt6ej9YGwTC8yVXJsjarLq2F+svwn8 # NlU+T03U/ZjXc/ZxDc5g3iqrl5Gm9QCaLhG2aLIrGRXN59Pcokp7JFNa6nkkWSSg # h4w01tz+xRSyiqKWAXNs2lHTD2F9ceGlz9Uw/RvPhPcl6bILqJcR6RUkzZtrKHNK # j85PBm/Kmurx0co5xRxXsXsF3tmp2r+Tt11veA9je+pyzuqE/kRQPn5hF8fIRuea # h7JVMaaHBTMbRaDcVFioGmCGHUx270yhLapA0eYXpZJv0n62QIMoX9NPcW2EcwhL # WGAV1IW+TIo/xcprAXBtXCO/mhscgInbMzesdg0uWsboiy4HfeTEzCe9ld54biUK # TJQu4wqbzkN5SGewOKTd/+c4k5w6yzuUWsk3YZpjWqsgpTlA3zU591uvMFsq0FYd # A3Py8YsVabLwTxz9d7kpBAHTPRYwDcsKNLGMPc+6MYIF0zCCBc8CAQEwfTBpMQsw # CQYDVQQGEwJVUzEXMBUGA1UEChMORGlnaUNlcnQsIEluYy4xQTA/BgNVBAMTOERp # Z2lDZXJ0IFRydXN0ZWQgRzQgQ29kZSBTaWduaW5nIFJTQTQwOTYgU0hBMzg0IDIw # MjEgQ0ExAhAFo6MauDVPnyiWHT0CB2G4MA0GCWCGSAFlAwQCAQUAoIGEMBgGCisG # AQQBgjcCAQwxCjAIoAKAAKECgAAwGQYJKoZIhvcNAQkDMQwGCisGAQQBgjcCAQQw # HAYKKwYBBAGCNwIBCzEOMAwGCisGAQQBgjcCARUwLwYJKoZIhvcNAQkEMSIEIE5C # VIe2TnmVTprKPBpWetzC3NA3i81XnZJN2X9T646zMA0GCSqGSIb3DQEBAQUABIIB # gJpa9AerPDkROt9hiwccE8RlXXNX69Q+oxcjvYGa88RJ0OJwxcxMot8NLp2obDdv # +ODkwzW3s4ZehwU+gE2ziQNOppQNILDbLMeAgUy7FsralciDeKNBgFPpRNmR9cQ+ # 4yOMNyNA1nyYX38atjSlnXiFRA7ChlQP1RNgpFssYLzhZfKzDfd7mln5UWcoyBqF # KIiBkhur3/9L9qZTJ1EFSoz13O42MjNZKRn9fp6rXnJlB1DTMNoVlKgRXHHQAzrG # wr2eqBuJfO3yoVPWnw8W7ZeyE8ZJhNlZxbekFK/Vapx6Km55+3olDSDD1wcCESeZ # cYJpl2TcBjKQ3HroJJ14Y9+Z4dGqwa78fLT0MbtFnIveBrGb8OOG4aR4GDkON/XS # 0cGkQz5I/e8gtMwK5udOZWNk1iDZS6S9AVK+NGIQx9gfw/A67iDYyyK17sfATwzV # qJwhd27nxAyq882JqBrrcfV2sadsZMnuYpprYU94mAT9VYFRZ9ezpqJ0e/xA3cr+ # t6GCAyAwggMcBgkqhkiG9w0BCQYxggMNMIIDCQIBATB3MGMxCzAJBgNVBAYTAlVT # MRcwFQYDVQQKEw5EaWdpQ2VydCwgSW5jLjE7MDkGA1UEAxMyRGlnaUNlcnQgVHJ1 # c3RlZCBHNCBSU0E0MDk2IFNIQTI1NiBUaW1lU3RhbXBpbmcgQ0ECEAuuZrxaun+V # h8b56QTjMwQwDQYJYIZIAWUDBAIBBQCgaTAYBgkqhkiG9w0BCQMxCwYJKoZIhvcN # AQcBMBwGCSqGSIb3DQEJBTEPFw0yNTAyMjcyMzI3NTZaMC8GCSqGSIb3DQEJBDEi # BCB2chSV4u2Wwd+qs8v55iR0nbxmlrS3Fxv4TmeOBQCqiDANBgkqhkiG9w0BAQEF # AASCAgC0D7/DcVjKQ3Th6Vm28M2I/2FuSSDMViHtZ72vfwHwcU8Hnv/118chGS13 # lcKlbTujnTbIelmiiSgIcl5JM5xooq+lV8cy2bqznhrJcj4FNh7flG00l8zejsTJ # /Ussbl6pXxz+/kjbi+HorEoXBj3BVqxg2HGz8J2Lhw6DhDif9HTUZ8iZCsAbcfff # 0mgAX8yCUvLitIjcNeXp3L/YLIiwXwb1ePwuA4fGXHz8zCSWXJoYn3Csy40H6txp # Bm+o9vklIZxzaSwpPtcD8EjrPuPJ9UZOik6MRVMmCtlNQf6VdKKMwXLSnjWWSa35 # c2JD1/aS6lpnjNKxxMaE4I0dMthIcnrZpzG7ocjmgbQR9GhFtbpaSQVbZUCtkHc4 # Mww4UfCJsWGjjrGvqSa/0M+xN/pfC7YI+tDMgMefVjKvqBq99Ht8Qa9wCNA1TW7Z # HgnMLP6chuGFsiqPaXb27Xc255nq+Tml8kAa1k+U3qQfnW20ng1f1+0JwKfLpRBf # E/eieemqKmOA2uiXb2x+QtTqTB2QRG0pGKMJAXsqrdKwrDiAwYdk5SDiEqwWNoiv # VengG+ZsOr4h8+om/TdbNC6a9QHrPm0OswL17mUvruxQoGRHKLpyrndQ8k7/XjHD # oq8o2UIHNOfU0jBbzY4TD6NHwjFOrCzVAHH4729YvEWhrRrUqQ== # SIG # End signature block |