Start-PsFCIV.ps1
function Start-PsFCIV { <# .ExternalHelp PsFCIV.Help.xml #> [CmdletBinding(DefaultParameterSetName = '__xml')] param ( [Parameter(Mandatory = $true, Position = 0)] [IO.DirectoryInfo]$Path, [Parameter(Mandatory = $true, Position = 1, ParameterSetName = '__xml')] [string]$XML, [string]$Include = "*", [string[]]$Exclude, [Parameter(ParameterSetName = '__xml')] [ValidateSet("Rename", "Delete")] [string]$Action, [Parameter(ParameterSetName = '__xml')] [ValidateSet("Bad", "Locked", "Missed", "New", "Ok", "Unknown", "All")] [String[]]$Show, [ValidateSet("MD5", "SHA1", "SHA256", "SHA384", "SHA512")] [AllowEmptyCollection()] [String[]]$HashAlgorithm = "SHA1", [switch]$Recurse, [Parameter(ParameterSetName = '__xml')] [switch]$Strict, [Parameter(ParameterSetName = '__xml')] [switch]$Rebuild, [Parameter(ParameterSetName = '__online')] [switch]$Online ) #region Prepare environment # configure preferences if ($PSBoundParameters.Verbose) {$VerbosePreference = "continue"; $v = $true} if ($PSBoundParameters.Debug) {$DebugPreference = "continue"; $d = $true} # add DB file to exclusion list if (!$Online) { $XML = $ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($XML) $Exclude += $XML } # preserving current path if (!(Test-Path -LiteralPath (Resolve-Path $Path))) { throw "Specified directory path not found." } # creating statistics variable with properties. Each property will contain file names (and paths) with corresponding status. $script:stats = New-Object PsFCIV.Support.StatTable $script:statcount = New-Object PsFCIV.Support.IntStatTable # mode: New, Check, Rebuild, FCIV $mode = "Check" #endregion # internal function to calculate resulting statistics and show if if necessary. function __showStats { # if -Show parameter is presented we display selected groups (Total, New, Ok, Bad, Missed, Unknown) if ($show) { if ($Show.Contains("All")) { $stats | __showGridView "Bad", "Locked", "Missed", "New", "Ok", "Unknown" $statcount.Total } else { $stats | Select-Object $show | __showGridView $show $statcount.Total } } # script work in numbers if ($v -or $d) { Write-Host ----------------------------------- -ForegroundColor Green if ($Rebuild) { Write-Host "Total entries processed :" $statcount.Total -ForegroundColor Cyan Write-Host "Total removed unused entries :" $statcount.Deleted -ForegroundColor Yellow Write-Host "Total new added files :" $statcount.New -ForegroundColor Green Write-Host "Total locked files :" $statcount.Locked -ForegroundColor Yellow } else { Write-Host "Total files processed :" $statcount.Total -ForegroundColor Cyan if (("New", "Rebuild") -contains $mode) { Write-Host "Total new added files :" $statcount.New -ForegroundColor Green } Write-Host "Total good files :" $statcount.Ok -ForegroundColor Green Write-Host "Total bad files :" $statcount.Bad -ForegroundColor Red Write-Host "Total unknown status files :" $statcount.Unknown -ForegroundColor Yellow Write-Host "Total missing files :" $statcount.Missed -ForegroundColor Yellow Write-Host "Total locked files :" $statcount.Locked -ForegroundColor Yellow } Write-Host ----------------------------------- -ForegroundColor Green } __finalize $statcount } # internal function to update statistic counters. function __addStatCounter ($filename, $status) { $script:statcount.$status++ $script:statcount.Total++ if ($Show -and ($Show.Contains($status) -or $Show.Contains("All"))) { $stats.$status.Add($filename) } } if ($Online) { Write-Debug "Online mode ON" dirx -Path (Join-Path $Path *) -Filter $Include -Exclude $Exclude $Recurse -Force | ForEach-Object { Write-Verbose "Perform file '$($_.fullName)' checking." $file = Get-Item -LiteralPath $_.FullName -Force -ErrorAction SilentlyContinue if (__testFileLock $file) {return} __newFileEntry $file -hex } return } <# in this part we perform XML file update by removing entries for non-exist files and adding new entries for files that are not in the database. #> if ($Rebuild) { Write-Debug "Rebuild mode ON" $mode = "Rebuild" if (Test-Path -LiteralPath $xml) { $old = __readXml $xml } else { __finalize $oldpath throw "Unable to find XML file. Please, run the command without '-Rebuild' switch." } $new = New-Object PsFCIV.Support.FcivRootNode $interm = New-Object PsFCIV.Support.FcivRootNode # use foreach-object instead of where-object to keep original types. Write-Verbose "Perform DB file cleanup from non-existent items." $old.Entries | ForEach-Object { if ((Test-Path -LiteralPath $_.Name)) { if ($_.Name -eq $xml) { Write-Debug "File '$($_.Name)' is DB file. Removed." $statcount.Deleted++ } else { [void]$interm.Entries.Add($_) } } else { Write-Debug "File '$($_.Name)' does not exist. Removed." $statcount.Deleted++ } } $statcount.Total = $old.Entries.Count - $interm.Entries.Count dirx -Path (Join-Path $Path *) -Filter $Include -Exclude $Exclude $Recurse -Force | ForEach-Object { if ($_.FullName -eq $XML) { return } Write-Verbose "Perform file '$($_.FullName)' checking." $file = Get-Item -LiteralPath $_.FullName -Force if (__testFileLock $file) {return} $filename = $file.FullName -replace [regex]::Escape($($pwd.providerpath + "\")) if ($interm.Entries.Contains((New-Object PsFCIV.Support.FcivFileEntry $filename))) { Write-Verbose "File '$filename' already exist in XML database. Skipping." return } else { [void]$new.Entries.Add((__newFileEntry $file)) Write-Verbose "File '$filename' is added." __addStatCounter $filename New } } $new.Entries | ForEach-Object {[void]$interm.Entries.Add($_)} __writeXml $interm __showStats return } # this part contains main routine $db = __readXml $xml # create flat list with indexer access. $db.Entries is of type of ISet<T> and doesn't have indexer $entries = $db.Entries | ForEach-Object {$_} <# check XML file format. If Timestamp property of the first element is null, then the file was generated by original FCIV.exe tool. In this case we transform existing XML to a new PsFCIV format by adding new properties. Each record is checked against hashes stored in the source XML file. If hash check fails, an item is removed from final XML. #> if ($entries.Count -gt 0 -and $entries[0].Size -eq 0) { if ($PSBoundParameters.ContainsKey("HashAlgorithm")) { $HashAlgorithm = $HashAlgorithm[0].ToUpper() } else { $HashAlgorithm = @() } Write-Debug "FCIV (compatibility) mode ON" $mode = "FCIV" if ($HashAlgorithm -and $HashAlgorithm -notcontains "sha1" -and $HashAlgorithm -notcontains "md5") { throw "Specified hash algorithm (or algorithms) is not supported. For native FCIV source, use MD5 and/or SHA1." } for ($index = 0; $index -lt $entries.Length; $index++) { # must be FOR loop, because indexer is used below $entry = $entries[$index] Write-Verbose "Perform file '$($entry.Name)' checking." $filename = $entry.Name # check if the path is absolute and matches current path. If the path is absolute and does not belong to # current path -- skip this entry. if ($filename.Contains(":") -and $filename -notmatch [regex]::Escape($pwd.ProviderPath)) {continue} # if source file name record contains absolute path, and belongs to the current pathe, # just strip base path. New XML format uses relative paths only. if ($filename.Contains(":")) {$filename = $filename -replace ([regex]::Escape($($pwd.ProviderPath + "\")))} # Test if the file exist. If the file does not exist, skip the current entry and process another record. if (!(Test-Path -LiteralPath $filename)) { Write-Verbose "File '$filename' not found. Skipping." __addStatCounter $filename Missed continue } # get file item and test if it is not locked by another application $file = Get-Item -LiteralPath $filename -Force -ErrorAction SilentlyContinue if (__testFileLock $file) {continue} # create new-style entry record that stores additional data: file length and last modification timestamp. $newentry = __newFileEntry $file -NoHash $newentry.Name = $filename # process current hash entries and copy required hash values to a new entry object. "SHA1", "MD5" | ForEach-Object {$newentry.$_ = $entry.$_} $entries[$index] = $newentry __checkfiles $newentry $file $Action $Strict } # clear DB entries and insert from backing collection. This step is necessary since ISet<T> doesn't have an # indexer and you cannot replace the item in set by index. Only remove->add is supported. $db.Entries.Clear() $entries | ForEach-Object {[void]$db.Entries.Add($_)} # we are done. Overwrite XML, display stats and exit. __writeXml $db # display statistics and exit right now. __showStats } # if XML file exist, proccess and check all records. XML file will not be modified. if ($entries.Count -gt 0) { Write-Debug "Native PsFCIV mode ON" $mode = "Check" # this part is executed only when we want to process certain file. Wildcards are not allowed. if ($Include -ne "*") { $db.Entries | Where-Object {$_.Name -like $Include} | ForEach-Object { Write-Verbose "Perform file '$($_.Name)' checking." $entry = $_ # calculate the hash if the file exist. if (Test-Path -LiteralPath $entry.Name) { # and check file integrity $file = Get-Item -LiteralPath $entry.Name -Force -ErrorAction SilentlyContinue __checkfiles $entry $file $Action $Strict } else { # if there is no record for the file, skip it and display appropriate message Write-Verbose "File '$filename' not found. Skipping." __addStatCounter $entry.Name Missed } } } else { $db.Entries | ForEach-Object { <# to process files only in the current directory (without subfolders), we remove items that contain slashes from the process list and continue regular file checking. #> if (!$Recurse -and $_.Name -match "\\") {return} Write-Verbose "Perform file '$($_.Name)' checking." $entry = $_ if (Test-Path -LiteralPath $entry.Name) { $file = Get-Item -LiteralPath $entry.Name -Force -ErrorAction SilentlyContinue __checkfiles $entry $file $Action $Strict } else { Write-Verbose "File '$($entry.Name)' not found. Skipping." __addStatCounter $entry.Name Missed } } } } else { # if there is no existing XML DB file, start from scratch and create a new one. Write-Debug "New XML mode ON" $mode = "New" dirx -Path (Join-Path $Path *) -Filter $Include -Exclude $Exclude $Recurse -Force | ForEach-Object { Write-Verbose "Perform file '$($_.fullName)' checking." $file = Get-Item -LiteralPath $_.FullName -Force -ErrorAction SilentlyContinue if (__testFileLock $file) {return} $entry = __newFileEntry $file [void]$db.Entries.Add($entry) __addStatCounter $entry.Name New } __writeXml $db } __showStats } # SIG # Begin signature block # MIIcnwYJKoZIhvcNAQcCoIIckDCCHIwCAQExDzANBglghkgBZQMEAgEFADB5Bgor # BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG # KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCCQ6+jgqwrOnxe9 # 2KFTlTo6jkwQbQvImsMFZDvUuadSgKCCFn4wggT+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 # hkiG9w0BCQQxIgQgkEl32sCS5s6RZ1UEuft+NiD4VoEmEZS1WDHRBNruEb4wDQYJ # KoZIhvcNAQEBBQAEggIAdaPWF+Pq8UqJUq1r4fgV1x9YCZg1H7TPkZnubSzi+S7+ # 306rF0MDf3tBXUgaX3MJCHycULa5Bh07RgAerpbxUYj7sAwBWKWUrIqojAFOWTsu # BPYHGjTa6r7PDbIoBm470S/Fk3AhjQy6+qqPXT2EbDO/yafZ48mCDPweQp1xFBBr # cKvLogH9Cq/P6C8HZ1pwKv5YT9qf946saKdEydjWe2lCnBxQnN2I/3OXzt+RDBSY # jqLXoZsiktG7WjfkufXCl9ScPh9p/TJ6ZVHhSuOY7sFp4qOg/dSE/iGxWukqwEP4 # gpfR59+2oz4AK0lJ38qr7a8TWenrzZYBblnXyUfNAKY8SWdeGBLxQsJwyuFI1eMw # vuTcmUH4GCvKJOwqpWOKNyGBV1jEH6Q9nC0C1fZvkclVWSbUKxPUvIviZqjk4gjb # cd33gOdP29boQ4hIXFuq9UG9rbhBxvj0QVak6/iaT6GtYCMUJPWJizZx/EYdi2Id # VJjbdxIXXsjSsXDWw7UPLecbMYNsqB2nssRjM8CsRwaDq0GIXfW9FlePmivB93yl # RwO2phuIrWC0EIssnG4b7LjrbJNWjMT9/wR3nDdOAdOeyPfehiuyITg1Wig4sNuW # 7D0sJW/2bReytbQlg1YAuxdVLpzy7vhL7SGxojL7BCAMgWIFquaT6VDHbQcB6MSh # ggIwMIICLAYJKoZIhvcNAQkGMYICHTCCAhkCAQEwgYYwcjELMAkGA1UEBhMCVVMx # FTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZMBcGA1UECxMQd3d3LmRpZ2ljZXJ0LmNv # bTExMC8GA1UEAxMoRGlnaUNlcnQgU0hBMiBBc3N1cmVkIElEIFRpbWVzdGFtcGlu # ZyBDQQIQDUJK4L46iP9gQCHOFADw3TANBglghkgBZQMEAgEFAKBpMBgGCSqGSIb3 # DQEJAzELBgkqhkiG9w0BBwEwHAYJKoZIhvcNAQkFMQ8XDTIyMDExMjA5MjkwMlow # LwYJKoZIhvcNAQkEMSIEIKvC9781DYGs4jl83KrXSIydOi84inYqV6Yw9QD2jkll # MA0GCSqGSIb3DQEBAQUABIIBABo0KKqwny/ujS7FVqZGCa9g6jBMUAPUeKvY/qAb # RzaSbOEPfMn5XqAMbEK2gbnu3bcMjbYcaCHtjL5PTnf+JchKYHc2MksjgmG1Bamm # LBz+I9Gvhc1WCMtin7mBOtAlf1pe+Q1RZniWVf0LxnoVYe79FlKk4F6ZNCaAhkDf # fZSrsMB7dMn8h9jwl9kTrp52HoNG7PXeh/K5fieoWIZP1u1a8yUCVQ3Z6gDAHu1t # IJ1i/QWhxsvaZ6ecJgzLy2tfk7gc0k6YobXz0R2EpmIGE45NWUObzsUS0P6IooXk # kkb1vVieRdqAMZSEdZ/Re9LcSIhaXMgdaAedRZTyjSlxJfU= # SIG # End signature block |