PsFCIV.psm1

#region helper functions
# internal function which reads the XML file (if exist).
function __readXml ($xml) {
    # reading existing XML file and selecting required properties
    if (!(Test-Path -LiteralPath $XML)) {
        New-Object PsFCIV.Support.FcivRootNode
        return
    }
    try {
        [PsFCIV.Support.FcivRootNode]::ReadFromFile($XML)
    } catch {
        __finalize
        Write-Error -Category InvalidData -Message "Input XML file is not valid FCIV XML file." -ErrorAction Stop
    }
}
# internal xml writer
function __writeXml ($root) {
    if ($root.Entries.Count -eq 0) {
        Write-Verbose "There is no data to write to XML database."
        Write-Debug "There is no data to write to XML database."
    } else {
        Write-Debug "Preparing to DataBase file creation..."
        $root.SaveToFile($XML)
    }
}
# lightweight proxy function for Get-ChildItem cmdlet
function dirx ([string]$Path, [string]$Filter, [string[]]$Exclude, $Recurse, [switch]$Force) {
    Get-ChildItem @PSBoundParameters -File -ErrorAction SilentlyContinue
}
# internal function that will check whether the file is locked. All locked files are added to a group with 'Unknown' status.
function __testFileLock ($file) {
    $locked = $false
    trap {Set-Variable -name locked -value $true -scope 1; continue}
    $inputStream = New-Object IO.StreamReader $file.FullName
    if ($inputStream) {$inputStream.Close()}
    if ($locked) {
        Write-Verbose "File $($file.Name) is locked. Skipping this file.."
        Write-Debug "File $($file.Name) is locked. Skipping this file.."
        __addStatCounter $filename Locked
    }
    $locked
}
# internal function to generate UI window with results by using Out-GridView cmdlet.
function __showGridView ($props, $max) {
    $total = @($input)
    foreach ($property in $props) {
        $(for ($n = 0; $n -lt $max; $n++) {
            $total[0] | Select-Object @{n=$property; e={$_.$property[$n]}}
        }) | Out-GridView -Title "File list by category: $property"
    }
}
# internal function to create XML entry object for a file.
function __newFileEntry ($file, [switch]$NoHash, [switch]$hex) {
    Write-Debug "Starting object creation for '$($file.FullName)'..."
    $object = New-Object PsFCIV.Support.FcivFileEntry $file
    $object.Name = $file.FullName -replace [regex]::Escape($($pwd.ProviderPath + "\"))
    if (!$NoHash) {
    # calculating appropriate hash and convert resulting byte array to a Base64 string
        foreach ($hash in "MD5", "SHA1", "SHA256", "SHA384", "SHA512") {
            if ($HashAlgorithm -contains $hash) {
                Write-Debug "Calculating '$hash' hash..."
                $hashBytes = [PsFCIV.Support.CryptUtils]::HashFile($file, $hash)
                if ($hex) {
                    $object.$hash = [PsFCIV.Support.CryptUtils]::FormatBytes($hashBytes, "Hex")
                } else {
                    Write-Debug ("Calculated hash value: " + (-join ($hashBytes | Foreach-Object {"{0:X2}" -f $_})))
                    $object.$hash = [PsFCIV.Support.CryptUtils]::FormatBytes($hashBytes, "Base64")
                }
            }
        }
    }
    Write-Debug "Object created!"
    $object
}
# internal function that calculates current file hash and formats it to an octet string (for example, B926D7416E8235E6F94F756E9F3AE2F33A92B2C4).
function __selectHAlg ($entry, $file, $HashAlgorithm) {
    if ($HashAlgorithm.Length -gt 0) {
        $SelectedHash = $HashAlgorithm
    } else {
        :outer foreach ($hash in "SHA512", "SHA384", "SHA256", "SHA1", "MD5") {
            if ($entry.$hash) {$SelectedHash = $hash; break outer}
        }
    }
    $hex = [PsFCIV.Support.CryptUtils]::FormatBytes([PsFCIV.Support.CryptUtils]::HashFile($file, $SelectedHash), "Hex")
    Write-Debug "Selected hash name : $SelectedHash"
    Write-Debug "Selected hash value: $hex"
    New-Object psobject -Property @{
        HashName = $SelectedHash
        HashValue = $hex
    }
}
# process -Action parameter to perform an action against bad file (if actual file properties do not match the record in XML).
function __takeAction ($file, $Action) {
    switch ($Action) {
        "Rename" {Rename-Item $file $($file.FullName + ".bad")}
        "Delete" {Remove-Item $file -Force}
    }
}
# core file verification function.
function __checkfiles ($entry, $file, $Action, $Strict) {
    if (__testFileLock $file) {return}
    if ($Strict -and (($file.Length -ne $entry.Size) -or ("$($file.LastWriteTime.ToUniversalTime())" -ne $entry.TimeStamp))) {
        Write-Verbose "File '$($file.FullName)' size or Modified Date/Time mismatch."
        Write-Debug "Expected file size is: $($entry.Size) byte(s), actual size is: $($file.Length) byte(s)."
        Write-Debug "Expected file modification time is: $($entry.TimeStamp), actual file modification time is: $($file.LastWriteTime.ToUniversalTime())"
        __addStatCounter $entry.Name Bad
        __takeAction $file $Action
    } else {
        $hexhash = __selectHAlg $entry $file $HashAlgorithm
        $ActualHash = [PsFCIV.Support.CryptUtils]::FormatBytes([Convert]::FromBase64String($entry.($hexhash.HashName)), "Hex")
        if (!$ActualHash) {
            Write-Verbose "XML database entry does not contain '$($hexhash.HashName)' hash value for the entry '$($entry.Name)'."
            __addStatCounter $entry.Name Unknown
            return
        } elseif ($ActualHash -eq $hexhash.HashValue) {
            Write-Debug "File hash: $ActualHash"
            Write-Verbose "File '$($file.Name)' is ok."
            __addStatCounter $entry.Name Ok
            return
        } else {
            Write-Debug "File '$($file.Name)' failed hash verification.
                Expected hash: $hexhash.HashValue
                Actual hash: $ActualHash"

            __addStatCounter $entry.Name Bad
            __takeAction $file $Action
        }
    }
}
function __finalize {
    # do nothing at this moment
}
#endregion

#region global variables
$oldpath = ""
$stats = New-Object PsFCIV.Support.StatTable
$statcount = New-Object PsFCIV.Support.IntStatTable
#endregion

# dot-source all function files
Get-ChildItem -Path $PSScriptRoot -Include *.ps1 -Recurse -File | Foreach-Object { . $_.FullName }
# SIG # Begin signature block
# MIIcnwYJKoZIhvcNAQcCoIIckDCCHIwCAQExDzANBglghkgBZQMEAgEFADB5Bgor
# BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG
# KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCA2A/JW3zeasPK4
# Tssyx0/4JfFWo90t4aQ7+fFqbOMEYqCCFn4wggT+MIID5qADAgECAhANQkrgvjqI
# /2BAIc4UAPDdMA0GCSqGSIb3DQEBCwUAMHIxCzAJBgNVBAYTAlVTMRUwEwYDVQQK
# EwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xMTAvBgNV
# BAMTKERpZ2lDZXJ0IFNIQTIgQXNzdXJlZCBJRCBUaW1lc3RhbXBpbmcgQ0EwHhcN
# MjEwMTAxMDAwMDAwWhcNMzEwMTA2MDAwMDAwWjBIMQswCQYDVQQGEwJVUzEXMBUG
# A1UEChMORGlnaUNlcnQsIEluYy4xIDAeBgNVBAMTF0RpZ2lDZXJ0IFRpbWVzdGFt
# cCAyMDIxMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAwuZhhGfFivUN
# CKRFymNrUdc6EUK9CnV1TZS0DFC1JhD+HchvkWsMlucaXEjvROW/m2HNFZFiWrj/
# ZwucY/02aoH6KfjdK3CF3gIY83htvH35x20JPb5qdofpir34hF0edsnkxnZ2OlPR
# 0dNaNo/Go+EvGzq3YdZz7E5tM4p8XUUtS7FQ5kE6N1aG3JMjjfdQJehk5t3Tjy9X
# tYcg6w6OLNUj2vRNeEbjA4MxKUpcDDGKSoyIxfcwWvkUrxVfbENJCf0mI1P2jWPo
# GqtbsR0wwptpgrTb/FZUvB+hh6u+elsKIC9LCcmVp42y+tZji06lchzun3oBc/gZ
# 1v4NSYS9AQIDAQABo4IBuDCCAbQwDgYDVR0PAQH/BAQDAgeAMAwGA1UdEwEB/wQC
# MAAwFgYDVR0lAQH/BAwwCgYIKwYBBQUHAwgwQQYDVR0gBDowODA2BglghkgBhv1s
# BwEwKTAnBggrBgEFBQcCARYbaHR0cDovL3d3dy5kaWdpY2VydC5jb20vQ1BTMB8G
# A1UdIwQYMBaAFPS24SAd/imu0uRhpbKiJbLIFzVuMB0GA1UdDgQWBBQ2RIaOpLqw
# Zr68KC0dRDbd42p6vDBxBgNVHR8EajBoMDKgMKAuhixodHRwOi8vY3JsMy5kaWdp
# Y2VydC5jb20vc2hhMi1hc3N1cmVkLXRzLmNybDAyoDCgLoYsaHR0cDovL2NybDQu
# ZGlnaWNlcnQuY29tL3NoYTItYXNzdXJlZC10cy5jcmwwgYUGCCsGAQUFBwEBBHkw
# dzAkBggrBgEFBQcwAYYYaHR0cDovL29jc3AuZGlnaWNlcnQuY29tME8GCCsGAQUF
# BzAChkNodHRwOi8vY2FjZXJ0cy5kaWdpY2VydC5jb20vRGlnaUNlcnRTSEEyQXNz
# dXJlZElEVGltZXN0YW1waW5nQ0EuY3J0MA0GCSqGSIb3DQEBCwUAA4IBAQBIHNy1
# 6ZojvOca5yAOjmdG/UJyUXQKI0ejq5LSJcRwWb4UoOUngaVNFBUZB3nw0QTDhtk7
# vf5EAmZN7WmkD/a4cM9i6PVRSnh5Nnont/PnUp+Tp+1DnnvntN1BIon7h6JGA078
# 9P63ZHdjXyNSaYOC+hpT7ZDMjaEXcw3082U5cEvznNZ6e9oMvD0y0BvL9WH8dQgA
# dryBDvjA4VzPxBFy5xtkSdgimnUVQvUtMjiB2vRgorq0Uvtc4GEkJU+y38kpqHND
# Udq9Y9YfW5v3LhtPEx33Sg1xfpe39D+E68Hjo0mh+s6nv1bPull2YYlffqe0jmd4
# +TaY4cso2luHpoovMIIFMTCCBBmgAwIBAgIQCqEl1tYyG35B5AXaNpfCFTANBgkq
# hkiG9w0BAQsFADBlMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5j
# MRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29tMSQwIgYDVQQDExtEaWdpQ2VydCBB
# c3N1cmVkIElEIFJvb3QgQ0EwHhcNMTYwMTA3MTIwMDAwWhcNMzEwMTA3MTIwMDAw
# WjByMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQL
# ExB3d3cuZGlnaWNlcnQuY29tMTEwLwYDVQQDEyhEaWdpQ2VydCBTSEEyIEFzc3Vy
# ZWQgSUQgVGltZXN0YW1waW5nIENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB
# CgKCAQEAvdAy7kvNj3/dqbqCmcU5VChXtiNKxA4HRTNREH3Q+X1NaH7ntqD0jbOI
# 5Je/YyGQmL8TvFfTw+F+CNZqFAA49y4eO+7MpvYyWf5fZT/gm+vjRkcGGlV+Cyd+
# wKL1oODeIj8O/36V+/OjuiI+GKwR5PCZA207hXwJ0+5dyJoLVOOoCXFr4M8iEA91
# z3FyTgqt30A6XLdR4aF5FMZNJCMwXbzsPGBqrC8HzP3w6kfZiFBe/WZuVmEnKYmE
# UeaC50ZQ/ZQqLKfkdT66mA+Ef58xFNat1fJky3seBdCEGXIX8RcG7z3N1k3vBkL9
# olMqT4UdxB08r8/arBD13ays6Vb/kwIDAQABo4IBzjCCAcowHQYDVR0OBBYEFPS2
# 4SAd/imu0uRhpbKiJbLIFzVuMB8GA1UdIwQYMBaAFEXroq/0ksuCMS1Ri6enIZ3z
# bcgPMBIGA1UdEwEB/wQIMAYBAf8CAQAwDgYDVR0PAQH/BAQDAgGGMBMGA1UdJQQM
# MAoGCCsGAQUFBwMIMHkGCCsGAQUFBwEBBG0wazAkBggrBgEFBQcwAYYYaHR0cDov
# L29jc3AuZGlnaWNlcnQuY29tMEMGCCsGAQUFBzAChjdodHRwOi8vY2FjZXJ0cy5k
# aWdpY2VydC5jb20vRGlnaUNlcnRBc3N1cmVkSURSb290Q0EuY3J0MIGBBgNVHR8E
# ejB4MDqgOKA2hjRodHRwOi8vY3JsNC5kaWdpY2VydC5jb20vRGlnaUNlcnRBc3N1
# cmVkSURSb290Q0EuY3JsMDqgOKA2hjRodHRwOi8vY3JsMy5kaWdpY2VydC5jb20v
# RGlnaUNlcnRBc3N1cmVkSURSb290Q0EuY3JsMFAGA1UdIARJMEcwOAYKYIZIAYb9
# bAACBDAqMCgGCCsGAQUFBwIBFhxodHRwczovL3d3dy5kaWdpY2VydC5jb20vQ1BT
# MAsGCWCGSAGG/WwHATANBgkqhkiG9w0BAQsFAAOCAQEAcZUS6VGHVmnN793afKpj
# erN4zwY3QITvS4S/ys8DAv3Fp8MOIEIsr3fzKx8MIVoqtwU0HWqumfgnoma/Capg
# 33akOpMP+LLR2HwZYuhegiUexLoceywh4tZbLBQ1QwRostt1AuByx5jWPGTlH0gQ
# GF+JOGFNYkYkh2OMkVIsrymJ5Xgf1gsUpYDXEkdws3XVk4WTfraSZ/tTYYmo9WuW
# wPRYaQ18yAGxuSh1t5ljhSKMYcp5lH5Z/IwP42+1ASa2bKXuh1Eh5Fhgm7oMLStt
# osR+u8QlK0cCCHxJrhO24XxCQijGGFbPQTS2Zl22dHv1VjMiLyI2skuiSpXY9aaO
# UjCCBfUwggPdoAMCAQICEB2iSDBvmyYY0ILgln0z02owDQYJKoZIhvcNAQEMBQAw
# gYgxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpOZXcgSmVyc2V5MRQwEgYDVQQHEwtK
# ZXJzZXkgQ2l0eTEeMBwGA1UEChMVVGhlIFVTRVJUUlVTVCBOZXR3b3JrMS4wLAYD
# VQQDEyVVU0VSVHJ1c3QgUlNBIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MB4XDTE4
# MTEwMjAwMDAwMFoXDTMwMTIzMTIzNTk1OVowfDELMAkGA1UEBhMCR0IxGzAZBgNV
# BAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UEBxMHU2FsZm9yZDEYMBYGA1UE
# ChMPU2VjdGlnbyBMaW1pdGVkMSQwIgYDVQQDExtTZWN0aWdvIFJTQSBDb2RlIFNp
# Z25pbmcgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCGIo0yhXoY
# n0nwli9jCB4t3HyfFM/jJrYlZilAhlRGdDFixRDtsocnppnLlTDAVvWkdcapDlBi
# pVGREGrgS2Ku/fD4GKyn/+4uMyD6DBmJqGx7rQDDYaHcaWVtH24nlteXUYam9Cfl
# fGqLlR5bYNV+1xaSnAAvaPeX7Wpyvjg7Y96Pv25MQV0SIAhZ6DnNj9LWzwa0VwW2
# TqE+V2sfmLzEYtYbC43HZhtKn52BxHJAteJf7wtF/6POF6YtVbC3sLxUap28jVZT
# xvC6eVBJLPcDuf4vZTXyIuosB69G2flGHNyMfHEo8/6nxhTdVZFuihEN3wYklX0P
# p6F8OtqGNWHTAgMBAAGjggFkMIIBYDAfBgNVHSMEGDAWgBRTeb9aqitKz1SA4dib
# wJ3ysgNmyzAdBgNVHQ4EFgQUDuE6qFM6MdWKvsG7rWcaA4WtNA4wDgYDVR0PAQH/
# BAQDAgGGMBIGA1UdEwEB/wQIMAYBAf8CAQAwHQYDVR0lBBYwFAYIKwYBBQUHAwMG
# CCsGAQUFBwMIMBEGA1UdIAQKMAgwBgYEVR0gADBQBgNVHR8ESTBHMEWgQ6BBhj9o
# dHRwOi8vY3JsLnVzZXJ0cnVzdC5jb20vVVNFUlRydXN0UlNBQ2VydGlmaWNhdGlv
# bkF1dGhvcml0eS5jcmwwdgYIKwYBBQUHAQEEajBoMD8GCCsGAQUFBzAChjNodHRw
# Oi8vY3J0LnVzZXJ0cnVzdC5jb20vVVNFUlRydXN0UlNBQWRkVHJ1c3RDQS5jcnQw
# JQYIKwYBBQUHMAGGGWh0dHA6Ly9vY3NwLnVzZXJ0cnVzdC5jb20wDQYJKoZIhvcN
# AQEMBQADggIBAE1jUO1HNEphpNveaiqMm/EAAB4dYns61zLC9rPgY7P7YQCImhtt
# EAcET7646ol4IusPRuzzRl5ARokS9At3WpwqQTr81vTr5/cVlTPDoYMot94v5JT3
# hTODLUpASL+awk9KsY8k9LOBN9O3ZLCmI2pZaFJCX/8E6+F0ZXkI9amT3mtxQJmW
# unjxucjiwwgWsatjWsgVgG10Xkp1fqW4w2y1z99KeYdcx0BNYzX2MNPPtQoOCwR/
# oEuuu6Ol0IQAkz5TXTSlADVpbL6fICUQDRn7UJBhvjmPeo5N9p8OHv4HURJmgyYZ
# SJXOSsnBf/M6BZv5b9+If8AjntIeQ3pFMcGcTanwWbJZGehqjSkEAnd8S0vNcL46
# slVaeD68u28DECV3FTSK+TbMQ5Lkuk/xYpMoJVcp+1EZx6ElQGqEV8aynbG8HAra
# fGd+fS7pKEwYfsR7MUFxmksp7As9V1DSyt39ngVR5UR43QHesXWYDVQk/fBO4+L4
# g71yuss9Ou7wXheSaG3IYfmm8SoKC6W59J7umDIFhZ7r+YMp08Ysfb06dy6LN0Kg
# aoLtO0qqlBCk4Q34F8W2WnkzGJLjtXX4oemOCiUe5B7xn1qHI/+fpFGe+zmAEc3b
# tcSnqIBv5VPU4OOiwtJbGvoyJi1qV3AcPKRYLqPzW0sH3DJZ84enGm1YMIIGSjCC
# BTKgAwIBAgIQF0FLo4fb8T/ESzcF/lyStzANBgkqhkiG9w0BAQsFADB8MQswCQYD
# VQQGEwJHQjEbMBkGA1UECBMSR3JlYXRlciBNYW5jaGVzdGVyMRAwDgYDVQQHEwdT
# YWxmb3JkMRgwFgYDVQQKEw9TZWN0aWdvIExpbWl0ZWQxJDAiBgNVBAMTG1NlY3Rp
# Z28gUlNBIENvZGUgU2lnbmluZyBDQTAeFw0xOTA4MTMwMDAwMDBaFw0yMjA4MTIy
# MzU5NTlaMIGZMQswCQYDVQQGEwJVUzEOMAwGA1UEEQwFOTcyMTkxDzANBgNVBAgM
# Bk9yZWdvbjERMA8GA1UEBwwIUG9ydGxhbmQxHDAaBgNVBAkMEzE3MTAgU1cgTWls
# aXRhcnkgUmQxGzAZBgNVBAoMElBLSSBTb2x1dGlvbnMgSW5jLjEbMBkGA1UEAwwS
# UEtJIFNvbHV0aW9ucyBJbmMuMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKC
# AgEA0L1qj5TDs7BrGL6/kUXX54oYim9CcoEW7rVWygcXKi0nzKh4Ly2JGOYeCBFu
# ZCbSxMB0BfDbdGPllbqd0xaADbnxxqtrr6hYHTvV7dy2wehq9zs2QOgKQpLa6Hm1
# OapyU0yDrFpTUgin0hYWrTQrWOR5d7EcgUuNMXYADZIS14k7pVjTwSI3qS0A5rU/
# g1sHR9NFSZxrSPdbnnaG9TkiDwbQMm1vggMwye7paWU27F+rI0mJQ5iQMRPWnnZf
# O+EwUwWeFuwf0k9xHggDs+njFzWZGF4P24T4pXHVqy0D8a9a2Sl5nL959tow7Fjh
# W5Nb2R2Bzy0HhU9qZBCmhWYPdQuxo/OK/xw66bQIytO3AoD+Z5qzbQDr27fGDwp6
# 4PDETSvKxPhrryVeMVlWLIdBBDN5mLjACJ+TdREgAG4rLoNB1DgUlGMi8BeHk8PZ
# Zq1jtoknD2dzRuIQHtsSMM1i58ng4v0z2JtWpDGkvPRWb0P5oIPUIkXJIJwg6DtV
# FYI3JOq0PEOVZ3ojsfWzZDCyIRYg4IQy06W976tmL1GAmG2t2gg2CaBI80f6UhN7
# EIys0O+kTNjGCCxMtwSziOrpfOgP9tEb2C9K/93kOLwmzSCNqqnANlsuw+uh1EVf
# QCUBE5qtNVbSb2epUdw/ijQtz+MoInrzTveSE75ow4kpb/0CAwEAAaOCAagwggGk
# MB8GA1UdIwQYMBaAFA7hOqhTOjHVir7Bu61nGgOFrTQOMB0GA1UdDgQWBBR31gKB
# zWLrup1S1jqN5GdPRjOxUDAOBgNVHQ8BAf8EBAMCB4AwDAYDVR0TAQH/BAIwADAT
# BgNVHSUEDDAKBggrBgEFBQcDAzARBglghkgBhvhCAQEEBAMCBBAwQAYDVR0gBDkw
# NzA1BgwrBgEEAbIxAQIBAwIwJTAjBggrBgEFBQcCARYXaHR0cHM6Ly9zZWN0aWdv
# LmNvbS9DUFMwQwYDVR0fBDwwOjA4oDagNIYyaHR0cDovL2NybC5zZWN0aWdvLmNv
# bS9TZWN0aWdvUlNBQ29kZVNpZ25pbmdDQS5jcmwwcwYIKwYBBQUHAQEEZzBlMD4G
# CCsGAQUFBzAChjJodHRwOi8vY3J0LnNlY3RpZ28uY29tL1NlY3RpZ29SU0FDb2Rl
# U2lnbmluZ0NBLmNydDAjBggrBgEFBQcwAYYXaHR0cDovL29jc3Auc2VjdGlnby5j
# b20wIAYDVR0RBBkwF4EVaW5mb0Bwa2lzb2x1dGlvbnMuY29tMA0GCSqGSIb3DQEB
# CwUAA4IBAQBrghkGUdTVXoPL5q2OvBJi6Av6vK/NHV4Yfn6fNvDECLiHchqSdAEi
# 5bOhqZH6FFSBr39F3iOsFtcZOCSozsC2fNc/s/k1k6bE50U6XVR+A0i/kx8k0/Oy
# +fnhufH2uApYmWmYo8KvXG1+PYRGWEB/oAM59TIJxOdLCUGLUFqLrTCqoM+6PXNw
# NoPZcZ11WapWumXHXM2hfu+HISsDtX2mxFZBgh+VhjQvnyCmiwRRUwozpMlFGOd2
# JtGc7YIj2mVcMHPiPdxOeLd9cYzdS4FUicpJ4L6ZOy8lVhMeMGjBaiGHEwF2oPTE
# VVvKyhEoa1ZInASt0CiaMwKtjarqhhzHMYIFdzCCBXMCAQEwgZAwfDELMAkGA1UE
# BhMCR0IxGzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UEBxMHU2Fs
# Zm9yZDEYMBYGA1UEChMPU2VjdGlnbyBMaW1pdGVkMSQwIgYDVQQDExtTZWN0aWdv
# IFJTQSBDb2RlIFNpZ25pbmcgQ0ECEBdBS6OH2/E/xEs3Bf5ckrcwDQYJYIZIAWUD
# BAIBBQCggYQwGAYKKwYBBAGCNwIBDDEKMAigAoAAoQKAADAZBgkqhkiG9w0BCQMx
# DAYKKwYBBAGCNwIBBDAcBgorBgEEAYI3AgELMQ4wDAYKKwYBBAGCNwIBFTAvBgkq
# hkiG9w0BCQQxIgQghf6TqhFOFJQYha7H0Qd0P1jR4oF/4aEHG2jmu8nmBlQwDQYJ
# KoZIhvcNAQEBBQAEggIAGg8l8dO6Rcs2y8N82muP1puw3sXRlSvxCkrHZGn3YbgN
# JbGbKoeCaTgbK9o8UtBzzH/8yLNnwj5uZkpFDQB42iSeUbVMSvenxlc6K+DaP1NX
# /k7sxyaXydO3ZwaEXTdZiwRcna/wx40W+f9xFJL0SRZJpuWd11z+1Llu6bLilifp
# RSmqJ7FvYrxMXJfVVby0UR1bUYrO+R+kb4FTkGXllP8G/yYMFlZ+r8pM/Z468o0f
# 9pAm9LGCE56p6NwfKI7e/OW/pE+E3tVZ5UBM0EKhl6MTi2a/MeMx5FtG6xfO1uNx
# PD01Lv7lmSZVdnW142TnL4dGqDFsl02tjJ6fTn6qXw6DA0dQznEU3MRcK637okbx
# txhD1Xau/j06EIjArOMN6nq834ugelvp6mM1UQJUA4WIPTAN4EWj58IvNsBr6Hx1
# tx5xWRvyrAPgh4V5mHL0W4hHfVXkOhKSH/uJkEpLbBPZ/EgTgzm28hnUYW+f+tth
# fNCaAKaxPyQrELgzswd5PxYlbcSxwtrCu9gn/iScSPFOc6beqja/RoaNxbKcsCer
# yPcgrXEbhcbYz++xTC+9wmZ5C6ZLMGlHMKVj731QbCU7ANlwlmiFk1mzcy0UM6lj
# v+JsLZPy7thKMl4RUCySkI8bgZZE1NyFeJmYZAC2KZUBRXXGwcopTa+lHAh/bmWh
# ggIwMIICLAYJKoZIhvcNAQkGMYICHTCCAhkCAQEwgYYwcjELMAkGA1UEBhMCVVMx
# FTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZMBcGA1UECxMQd3d3LmRpZ2ljZXJ0LmNv
# bTExMC8GA1UEAxMoRGlnaUNlcnQgU0hBMiBBc3N1cmVkIElEIFRpbWVzdGFtcGlu
# ZyBDQQIQDUJK4L46iP9gQCHOFADw3TANBglghkgBZQMEAgEFAKBpMBgGCSqGSIb3
# DQEJAzELBgkqhkiG9w0BBwEwHAYJKoZIhvcNAQkFMQ8XDTIyMDExMjA5MjkwMlow
# LwYJKoZIhvcNAQkEMSIEILE24DrWdGPvNoKti9UJClC1U+tbwmG5drPxj/Gh/vq3
# MA0GCSqGSIb3DQEBAQUABIIBAKywznm1T3wGYCQQOUUYvqzrQ6lc2lR5he3Ui9iM
# fmytD8VPgobllpTPO25Oisyx1i5ZLeSBdSoM7vgtzH8V31J1LKv12OHwyHup5uOs
# v7PlVwubSiakWUsEQWNOdSeOL80J1cp+spPbm4BPQ08HdtOMZMN3AYsn9O164g4A
# 470mVd6lsCj8joPLfZVaiFr01jjLf9X4v1gNA4Y+Ww4SkFfLwsOwIK+WKUz886Kt
# NEu5nCaZnLlHSmHaZGt4EVcTJknF8PZG35pmaJQX1MwUA/AxWEXkC//hIw2uCTrF
# SG37u/0urf41p3YZneUIO4ZOzBNTB9mmjYtzE+jP0DDa+ZQ=
# SIG # End signature block