FileHandlers.psm1

# FileHandlers.psm1

#region FonctionDetect

function Get-OthersContent {
    param (
        [string]$filepath,
        [string[]]$patterns
    )  
    $results = @()    
    try {
        $findmatch = Select-String -Path $filepath -Pattern $patterns -AllMatches
        foreach ($match in $findmatch) {

            $patternMatch = $match.pattern -replace '\\b',''
            [string]$word= $match.line.Replace($patternMatch,'')

            if (-not([string]::IsNullOrEmpty(($word -replace '^[\s=:"]+').Trim()))){

            switch ($patternMatch) {
            '(?:[0-9]{1,3}\.){3}[0-9]{1,3}' { $patternMatch = 'IPv4' }
            '[a-fA-F0-9]{32}' { $patternMatch = 'MD5' }
            '[a-fA-F0-9]{40}' { $patternMatch = 'SHA-1' }
            '[a-fA-F0-9]{64}' { $patternMatch = 'SHA-256' }
            default {
            switch -Wildcard ($patternMatch) {
            'net *' { $patternMatch = 'Commande Net User' }
            default { $patternMatch = $patternMatch }
             }
             }             
             }

            $result = [PSCustomObject]@{
                FilePath = $filepath
                pattern  = $patternMatch
                Word     = $match.Line
            }
            $results += $result
            }            
     }
    } catch {
        $result = [PSCustomObject]@{
            FilePath = $filepath
            pattern  = 'error'
            Word     = $_.Exception.Message
        }
        $results = $result
    }    
    return $results
}

function Get-DocxContent {
    param (
        [string]$filepath,
        [string[]]$patterns
    )
    $results = @()
    Import-Module PSWriteOffice
    try {
    $document = Get-OfficeWord -FilePath $filepath -ReadOnly
       $n = 0
    foreach ($pattern in $patterns) {
        $pattern = $pattern -replace '\\b', ''
        [string]$findtext = $document.Find($pattern) | Select-Object text -First 2
        if ($findtext) {
        if ($n -le '1') {
        foreach ($text in $findtext) {
        $n++
            $result = [PSCustomObject]@{
                FilePath = $filepath
                pattern  = $pattern
                Word     = $text
            }
            $results += $result
            }
        } elseif ($n -gt '1') { 
            $result = [PSCustomObject]@{
                FilePath = $filepath
                pattern  = $pattern
                Word    = "at least " + $n + " characters found"
            }
            $results += $result
         break }
    }
    }
    Close-OfficeWord -Document $document
    } 
    catch {   $result = [PSCustomObject]@{
                FilePath = $filepath
                pattern  = 'error'
                Word     = $_
            }
            $results = $result
    }
    $results
    }

function Get-XlsxContent {
    param (
        [string]$filepath,
        [string[]]$patterns
    )
    $results = @()
    Import-Module PSWriteOffice
    try {
    $excel = Get-OfficeExcel -FilePath $filepath 
    $n = 0
    foreach ($pattern in $patterns) {
        $pattern = $pattern -replace '\\b', ''
        [string]$findtext = $excel.Search($pattern, [System.Globalization.CompareOptions]::IgnoreCase, $false) | Select-Object value -First 2
        if ($findtext) {                    
            if ($n -le '2') {
            foreach ($text in $findtext) {
            $n++
            $result = [PSCustomObject]@{
                FilePath = $filepath
                pattern  = $pattern
                Word    = $text
            }
            $results += $result
        }
        } else { 
            $result = [PSCustomObject]@{
                FilePath = $filepath
                pattern  = $pattern
                Word    = "at least " + $n + " characters found"
            }
            $results += $result
         break }
            }
    }
    $excel.Dispose()
    } 
    catch {
    $result = [PSCustomObject]@{
                FilePath = $filepath
                pattern  = 'error'
                Word     = $_
            }
            $results = $result
    }
    $results
}

function Get-DocContent {
    param (
        [string]$filepath,
        [string[]]$patterns,
        [string]$wordinstalled
    )
    $results = @()
    if ($wordinstalled -eq $false) {
        $result = [PSCustomObject]@{
            FilePath = $filepath
            pattern  = 'requires_check'
            Word     = 'Word is not installed'
        }
        $results += $result
    } else {
        $wordApp = New-Object -ComObject Word.Application
        $wordApp.Visible = $false
        try {
            $document = $wordApp.Documents.Open($filepath, [ref]$null, [ref]$true)
            $n = 0
            foreach ($pattern in $patterns) {
                $pattern = $pattern -replace '\\b', ''
                $find = $document.Content.Find | Select-Object -First 1
                $find.Text = $pattern
                $find.Forward = $true
                $find.Wrap = 1  # wdFindContinue
                $find.Execute() | Out-Null
                if ($find.Found) {
                    if ($n -le '2') {
                        $n++
                        $result = [PSCustomObject]@{
                            FilePath = $filepath
                            pattern  = $pattern
                            Word     = "at least " + $n + " characters found"
                        }
                        $results = $result
                    } else { break }
                }
            }
            $document.Close([ref]$false)
            [System.Runtime.Interopservices.Marshal]::ReleaseComObject($document) | Out-Null
        } catch {
            $result = [PSCustomObject]@{
                FilePath = $filepath
                pattern  = 'error'
                Word     = $_.Exception.Message
            }
            $results = $result
        } finally {
            $wordApp.Quit()
            [System.Runtime.Interopservices.Marshal]::ReleaseComObject($wordApp) | Out-Null
            [System.GC]::Collect()
            [System.GC]::WaitForPendingFinalizers()
        }
    }
    $results
}

function Get-XlsContent {
    param (
        [string]$filePath,
        [string[]]$patterns,
        [string]$excelInstalled
    )
    $results = @()
    
    if ($excelInstalled -eq $false) {
        $result = [PSCustomObject]@{
            FilePath = $filePath
            pattern  = 'requires_check'
            Word     = 'Excel is not installed'
        }
        $results += $result
    } else {
        $excelApp = New-Object -ComObject Excel.Application
        $excelApp.Visible = $false
        try {
            $workbook = $excelApp.Workbooks.Open($filePath, [ref]$null, [ref]$true)
            $n = 0
            foreach ($pattern in $patterns) {
                $pattern = $pattern -replace '\\b', ''
                foreach ($sheet in $workbook.Sheets) {
                    $cells = $sheet.Cells.Find($pattern)
                    if ($null -ne $cells) {
                        if ($n -le '2') {
                            $n++
                            $result = [PSCustomObject]@{
                                FilePath = $filePath
                                pattern  = $pattern
                                Word     = "at least " + $n + " characters found"
                            }
                            $results = $result
                        } else { break }
                    }
                }
            }
            $workbook.Close([ref]$false)
            [System.Runtime.Interopservices.Marshal]::ReleaseComObject($workbook) | Out-Null
        } catch {
            $result = [PSCustomObject]@{
                FilePath = $filePath
                pattern  = 'error'
                Word     = $_.Exception.Message
            }
            $results = $result
        } finally {
            $excelApp.Quit()
            [System.Runtime.Interopservices.Marshal]::ReleaseComObject($excelApp) | Out-Null
            [System.GC]::Collect()
            [System.GC]::WaitForPendingFinalizers()
        }
    }
    $results
}

function Get-PPTContent {
    param (
        [string]$filepath,
        [string[]]$patterns,
        [string]$wordinstalled
    )
    $results = @()
    if ($wordinstalled -eq $false) {
        $result = [PSCustomObject]@{
            FilePath = $filepath
            pattern  = 'requires_check'
            Word     = 'Office is not installed'
        }
        $results += $result
    } 
    else {      
        try {
            $MSPPT = New-Object -ComObject powerpoint.application
            $PRES = $MSPPT.Presentations.Open($filepath, $true, $true, $false)

            $n = 0

            foreach($Slide in $PRES.Slides) {
            foreach ($Shape in $Slide.Shapes) {

            if ($Shape.HasTextFrame -eq "-1") {
             $text = $Shape.TextFrame.TextRange.Text            

             foreach ($pattern in $patterns) {
             
             $pattern = $pattern -replace '\\b', ''

             if ($text -match $pattern) {
             
             if ($n -le '2') {        
                 
             $n++
             
             $result = [PSCustomObject]@{
                            FilePath = $filepath
                            pattern  = $pattern
                            Word     = "at least " + $n + " characters found"
                        }
                        $results = $result
                        }  else { break }
            }                       

            }
            }
            }
            }

            $MSPPT.PresentationClose
            [System.Runtime.Interopservices.Marshal]::ReleaseComObject($MSPPT) | Out-Null
        } catch {
            $result = [PSCustomObject]@{
                FilePath = $filepath
                pattern  = 'error'
                Word     = $_.Exception.Message
            }
            $results = $result
        } finally {
            $MSPPT.Quit()
            [System.Runtime.Interopservices.Marshal]::ReleaseComObject($MSPPT) | Out-Null
            [System.GC]::Collect()
            [System.GC]::WaitForPendingFinalizers()
        }
    }
    $results
}

function Get-OdsContent {
    param (
        [string]$filePath,
        [string[]]$patterns
    )
    $results = @()


    # Ouvrir le fichier ODS comme un fichier ZIP
    Add-Type -AssemblyName System.IO.Compression.FileSystem
    $zip = [System.IO.Compression.ZipFile]::OpenRead($filePath)

    # Extraire le contenu du fichier content.xml (contenu principal des fichiers ODS)
    $contentXmlEntry = $zip.Entries | Where-Object { $_.FullName -eq "content.xml" }
    $reader = [System.IO.StreamReader]::new($contentXmlEntry.Open())
    $contentXml = $reader.ReadToEnd()
    $reader.Close()
    $zip.Dispose()

    # Analyser le contenu XML
    [xml]$xmlContent = $contentXml

    # Lire le texte du contenu XML
    $textContent = $xmlContent.'document-content'.InnerText

    $n = 0
    foreach ($pattern in $patterns) {
        $pattern = $pattern -replace '\\b', ''
        if ($textContent -match $pattern) {
            if ($n -le '2') {
                $n++
                $result = [PSCustomObject]@{
                    FilePath = $filePath
                    Pattern  = $pattern
                    Word     = "at least " + $n + " characters found"
                }
                $results = $result
            }
        }
    }
    $results
}

function Get-Pdfcontent {
        param (
        [string]$filepath,
        [string[]]$patterns
        )

            $results = @()
            Import-Module PSWritePDF
            try {
            $text = (Convert-PDFToText -FilePath $filepath) -join "`n"
            $n = 0
            foreach ($pattern in $passwordPatterns) {
                if ($text -match $pattern) {
                if ($n -le '2') {
                    $n++
                    $result = [PSCustomObject]@{
                        FilePath = $filepath
                        pattern  = $pattern
                        Word     = "at least " + $n + " characters found"
                    }
                    $results = $result
            }   else { break }
                }
            }
            } 
            catch {
            $result = [PSCustomObject]@{
                        FilePath = $filepath
                        pattern  = 'error'
                        Word     = $_.Exception.Message
                    }
                    $results = $result
            }
            $results
}

function Get-Xmlcontent {
        param (
        [string]$filepath,
        [string[]]$patterns
        )
            $results = @()
            $xmlContent = Get-Content -Path $filepath -Raw
            if ($xmlContent -match 'cpassword') {
                $xml = [xml]$xmlContent
                # Traitement pour cpassword
                $cpasswords = @()
                if ($xml.Groups.User) {
                    $cpasswords = $xml | Select-Xml "/Groups/User/Properties/@cpassword" | Select-Object -Expand Node | ForEach-Object {$_.Value}
                } elseif ($xml.NTServices.NTService) {
                    $cpasswords = $xml | Select-Xml "/NTServices/NTService/Properties/@cpassword" | Select-Object -Expand Node | ForEach-Object {$_.Value}
                } elseif ($xml.ScheduledTasks.Task) {
                    $cpasswords = $xml | Select-Xml "/ScheduledTasks/Task/Properties/@cpassword" | Select-Object -Expand Node | ForEach-Object {$_.Value}
                } elseif ($xml.DataSources.DataSource) {
                    $cpasswords = $xml | Select-Xml "/DataSources/DataSource/Properties/@cpassword" | Select-Object -Expand Node | ForEach-Object {$_.Value}
                } elseif ($xml.Printers.SharedPrinter) {
                    $cpasswords = $xml | Select-Xml "/Printers/SharedPrinter/Properties/@cpassword" | Select-Object -Expand Node | ForEach-Object {$_.Value}
                } elseif ($xml.Drives.Drive) {
                    $cpasswords = $xml | Select-Xml "/Drives/Drive/Properties/@cpassword" | Select-Object -Expand Node | ForEach-Object {$_.Value}
                }
                if ($cpasswords) {
                $result = [PSCustomObject]@{
                    FilePath  = $filepath
                    Pattern  = 'cpassword'
                    Word     = $cpasswords
                }
                $results += $result
                }
            } 
            elseif ($xmlContent -match 'DefaultUserName') {
               
                $xml = [xml]$xmlContent
                # Traitement pour AutoLogon
                $userName = ""
                $password = ""
                foreach ($registry in $xml.RegistrySettings.Registry) {
                    if ($registry.Properties.name -eq "DefaultUserName") {
                        $userName = $registry.Properties.value
                    }
                    if ($registry.Properties.name -eq "DefaultPassword") {
                        $password = $registry.Properties.value
                    }
                }
                $result  = [PSCustomObject]@{
                    FilePath = $filepath
                    Pattern  = 'AutoLogon'
                    Word     = "$userName : $password"
                }
                $results += $result
            } 
            else {
                $xml = [xml]$xmlContent
                foreach ($pattern in $patterns) {
                    $findmatches = $xmlContent | Select-String -Pattern $pattern
                    foreach ($match in $findmatches) {
             
             switch ($pattern) {
            '\b(?:[0-9]{1,3}\.){3}[0-9]{1,3}\b' { $pattern = 'IPv4' }
            '\b[a-fA-F0-9]{32}\b' { $pattern = 'MD5' }
            '\b[a-fA-F0-9]{40}\b' { $pattern = 'SHA-1' }
            '\b[a-fA-F0-9]{64}\b' { $pattern = 'SHA-256' }
            default {
            switch -Wildcard ($pattern) {
            'net *' { $pattern = 'Commande Net User' }
            default { $pattern = $pattern }
             }
             }             
             }
             
             $result = [PSCustomObject]@{
                            FilePath = $filepath
                            Pattern  = $pattern
                            Word     = $match.Matches.Value
                        }
                        $results += $result
                    }
                }
            }
            $results
}

function Get-Executablescontent {
        
        param (
        [string]$filepath
        )

        $results = @()
            if ($filepath -notlike '*.jar') { 
            $signature = Get-AuthenticodeSignature -FilePath $filepath
            if ($signature.Status -ne 'Valid') {
                $result = [PSCustomObject]@{
                    FilePath = $filepath
                    pattern  = "NotSigned"
                    Word     = 'File is Not Signed'
                }
                $results = $result
            }
            } else {
        $isSigned = $false
        try {
        $zip = [System.IO.Compression.ZipFile]::OpenRead($filepath)
        foreach ($entry in $zip.Entries) {
            if ($entry.FullName -like "META-INF/*.SF") {
                $isSigned = $true
                break
            }
        }
        $zip.Dispose()
    } catch {
        $result = [PSCustomObject]@{
                        FilePath = $filepath
                        pattern  = 'error'
                        Word     = $_.Exception.Message
                    }
                    $results = $result
    }

    if (!$isSigned) {
         $result = [PSCustomObject]@{
                    FilePath = $filepath
                    pattern  = "NotSigned"
                    Word     = "Jar file not signed"
                }
                $results = $result
    }
            }
        $results
}

function Get-Requiredcheckcontent {

         param (
        [string]$filepath
        )

             $results = @()
             $result = [PSCustomObject]@{
                    FilePath =  $filepath
                    pattern  = 'check required'
                    Word     = 'Binary does not match'
                }
                $results = $result
        $results
        }

function Get-CertifsContent {
    param (
        [string]$filePath
    )

    $results = @()
    try {
        # Tenter de charger le certificat avec la classe .NET X509Certificate2
        $cert = New-Object System.Security.Cryptography.X509Certificates.X509Certificate2
        $cert.Import($filePath)        
        if (!$cert.Thumbprint) { $result = [PSCustomObject]@{
                    FilePath =  $filepath
                    pattern  = 'Certificate Empty'
                    Word     = 'Certificate without Thumbprint'
                }
      $results = $result }
        if ($cert.PrivateKey) { $result = [PSCustomObject]@{
                    FilePath =  $filepath
                    pattern  = 'Certificate private key'
                    Word     = 'certificate with exportable private key'
                }
      $results += $result }
        $cert.Dispose()
    }
    catch {
      $result = [PSCustomObject]@{
                    FilePath =  $filepath
                    pattern  = 'Protected Certificate'
                    Word     = ($_.Exception.Message).split(":").split("`n")[1].Trim()
                }
      $results = $result       
    }
    return $results
}

function Get-P7bCertContent {
    param (
        [string]$filePath
    )
    $results = @()
    try {
        # Cr�er une collection de certificats pour le fichier P7B ou P7C
        $certCollection = New-Object System.Security.Cryptography.X509Certificates.X509Certificate2Collection
        $certCollection.Import($filePath)

        # Parcourir tous les certificats dans le fichier P7B/P7C
    foreach ($cert in $certCollection) {         
       if (!$cert.Thumbprint) { 
                    $result = [PSCustomObject]@{
                    FilePath =  $filepath
                    pattern  = 'Certificate Empty'
                    Word     = 'Certificate without Thumbprint'
                }
       $results += $result
       }
       if ($cert.PrivateKey) { 
       $result = [PSCustomObject]@{
                    FilePath =  $filepath
                    pattern  = 'Certificate private key'
                    Word     = 'certificate with exportable private key'
                }
       $results += $result
      }
      $cert.Dispose()
      }
      }
    catch {
      $result = [PSCustomObject]@{
                    FilePath =  $filepath
                    pattern  = 'Protected Certificate'
                    Word     = $_.Exception.Message
      }
      $results = $result
    }
    return $results
}

function Get-HiddenFilesInImage {
    param (
        [string]$filePath
    )
        
        $fileInfo = Get-Item $filePath
        $fileSizeMB = [math]::Round($fileInfo.Length / 1MB, 2)  

        # Skip file more than 4 Mo
        if ($fileSizeMB -gt 4) {
        $result = [PSCustomObject]@{
                    FilePath =  $filepath
                    pattern  = 'Large size'
                    Word     = "File ignored: (size: $fileSizeMB MB)"
                }
      return $result
        }

    try {
        # read binary and convert file to Hexa
        $fileBytes = [System.IO.File]::ReadAllBytes($filePath)
        $fileHex   = [BitConverter]::ToString($fileBytes) -replace '-'

        # Liste des Magic Numbers to search (EXE, ZIP, RAR, 7z)
        $magicNumbers = [ordered]@{
            "MSI" = "D0CF11E0A1B11AE1" # MSI or office
            "RAR" = "526172211A0700";  # RAR file
            "ZIP" = "504B0304";   # ZIP file
            "7z"  = "377ABCAF271C"; # 7z file
            "EXE" = "4D5A";       # EXE file (MZ header)
        }

        # Parcourir les Magic Numbers
        foreach ($key in $magicNumbers.Keys) {
            $magicNumber = $magicNumbers[$key]
            $currentIndex = 0

            if ($fileHex -match $magicNumber) {

            if ($key -eq "EXE") {
                while ($fileHex.IndexOf($magicNumber, $currentIndex) -ne -1) {
                    $startIndex = $fileHex.IndexOf($magicNumber, $currentIndex)
                    # Extract the part after the Magic Number (limited to 400 bytes after)
                    $remainingHex = $fileHex.Substring($startIndex + $magicNumber.Length, [Math]::Min(200 * 2, $fileHex.Length - ($startIndex + $magicNumber.Length)))

                    # Check for the presence of the special string "0000004000" in this range
                    if ($remainingHex -match "0000004000") {
                    $result = [PSCustomObject]@{
                    FilePath =  $filepath
                    pattern  = 'Suspicious Image'
                    Word     = "EXE file with '0000004000' string detected"
                }
                return $result
                    }

                    # Continue the search from the next index
                    $currentIndex = $startIndex + $magicNumber.Length
                }
            }
            elseif ($key -eq "ZIP") {

    # Find the index where the ZIP file starts
    $startIndex = $fileHex.IndexOf($magicNumber, $currentIndex) / 2  # Divide by 2 because each hexadecimal represents 2 characters

    # Extract the ZIP bytes starting from this index
    $zipBytes = $fileBytes[$startIndex..($fileBytes.Length - 1)]

    $tempDir = [System.IO.Path]::GetTempPath()
    $fileNameWithoutExtension = [System.IO.Path]::GetFileNameWithoutExtension($filePath)
    $tempZipPath = [System.IO.Path]::Combine($tempDir, "$fileNameWithoutExtension.zip")
    [System.IO.File]::WriteAllBytes($tempZipPath, $zipBytes)

    # Read the ZIP file and list the files
    Add-Type -AssemblyName System.IO.Compression.FileSystem
    $zip = [System.IO.Compression.ZipFile]::OpenRead($tempZipPath)

    # List the files inside the ZIP
    $fileNames = $zip.Entries | Select-Object -ExpandProperty FullName
    $fileNames | ForEach-Object { Write-Host "File in ZIP: $_" }

    $zip.Dispose()

    # Delete the temporary file
    Remove-Item $tempZipPath

    # Return the result
    $result = [PSCustomObject]@{
        FilePath =  $filePath
        Pattern  = 'Suspicious Image'
        Word     = "ZIP detected in pictures. Containing: $($fileNames -join ', ')"
    }
    return $result
}
            else {
                $result = [PSCustomObject]@{
                    FilePath =  $filepath
                    pattern  = 'Suspicious Image'
                    Word     = "File $key detected in the image"
                }
                return $result
                }
            }
        }

        #Write-Host "Not file found" -ForegroundColor Cyan
    }
    catch {
        # Gestion des erreurs
        $result = [PSCustomObject]@{
                 FilePath =  $filepath
                 pattern  = 'Error'
                 Word     = "Details : $_"
                }
       return $result
    }
}

#endregion FonctionDetect

# Add other functions for different file types...

function Get-CompressedFileType {    
    param (
        [string]$filePath,
        [object[]]$detectedType
    )


    # V�rification suppl�mentaire pour DOCX, XLSX, ODT, ODS ou JAR PPTX zip
    if ($detectedType -contains "docx" -or $detectedType -contains "jar") {
    try {
        Add-Type -AssemblyName System.IO.Compression.FileSystem
        $zip = [System.IO.Compression.ZipFile]::OpenRead($filePath)

        $fileNames = $zip.Entries | Select-Object -ExpandProperty FullName

        if ($fileNames -contains "word/document.xml") {
            $detectedType = "docx"
        } 
        elseif ($fileNames -contains "xl/workbook.xml") {
            $detectedType = "xlsx"
        } 
        elseif ($fileNames -contains "ppt/presentation.xml") {
            $detectedType = "pptx"
        }
        elseif ($fileNames -contains "visio/document.xml") {
            $detectedType = "vsdx"
        } 
        elseif ($fileNames -contains "content.xml") {
            $mimetypeEntry = $zip.Entries | Where-Object { $_.FullName -eq "mimetype" }
            if ($mimetypeEntry -ne $null) {
                $reader = [System.IO.StreamReader]::new($mimetypeEntry.Open())
                $mimetype = $reader.ReadToEnd()
                $reader.Close()

                switch ($mimetype) {
                    "application/vnd.oasis.opendocument.spreadsheet" {
                        $detectedType = "ods"
                    }
                    "application/vnd.oasis.opendocument.text" {
                        $detectedType = "odt"
                    }
                    "application/vnd.oasis.opendocument.presentation" {
                        $detectedType = "odp"
                    }
                    "application/vnd.oasis.opendocument.text-template" {
                        $detectedType = "ott"
                    }
                }
            }
        } 
        elseif ($fileNames -contains "META-INF/MANIFEST.MF") {
            $detectedType = "jar"
        } 
        else   { $detectedType = "others" }
        $zip.Dispose()
     } 
    catch {
        $detectedType = "requires_check"
         }
    }
    elseif ($detectedType -contains "doc") {
        
        $a = [System.IO.File]::ReadAllBytes($filePath)
        $content = [System.Text.Encoding]::ASCII.GetString($a)

        if ($content.Contains("Word.Document")) {
            $detectedType = "doc"
        } elseif ($content.Contains("MSI") -or $content.Contains("Installer")) {
            $detectedType = "msi"
        } elseif ($content.Contains("Excel") ) {
            $detectedType = "xls"
        } elseif ($content.Contains("PowerPoint")) {
            $detectedType = "ppt"
        } elseif ($content.Contains("Microsoft Visio")) {
            $detectedType = "vsd"
        } else {
            $detectedType = "others"
        }
     }

    return $detectedType
}
function Get-FileType {
    param (
        [string]$filePath,
        [object]$jsonContent
    )

    $detectedType = "others"
    $extension = [System.IO.Path]::GetExtension($filePath).TrimStart('.')    

    $fileHeaderHex = [System.IO.File]::ReadAllBytes($filePath)[0..16] | ForEach-Object { "{0:X2}" -f $_ }
    $fileHeaderHex = ($fileHeaderHex -join '').Trim()
    
    if ($fileHeaderHex.Length -eq 0) {
        return "empty"
    }
    $matchFound = $false

    foreach ($entry in $jsonContent.magic_numbers) {
        if ($matchFound) { break }

        foreach ($expectedMagic in $entry.magic) {
            if ($matchFound) { break }

            if ($expectedMagic.Length -le $fileHeaderHex.Length) {
                $difference = Compare-Object -ReferenceObject $expectedMagic -DifferenceObject ($fileHeaderHex.Substring(0, $expectedMagic.Length)) -SyncWindow 0

                if ($difference.Count -eq 0) {
                   if ($entry.offset) {
                        $offsetPosition = $entry.offset[0]  
                        $expectedOffsetValue = $entry.offset[1]  
                        $specificBytes = $fileHeaderHex.Substring($offsetPosition * 2, $expectedOffsetValue.Length)
                        if ($specificBytes -eq $expectedOffsetValue) {
                            if ($extension -notin $entry.extensions) {
                                $detectedType = 'requires_check'
                            } else {
                                $detectedType = $extension
                            }
                            $matchFound = $true
                            break
                        }
                    } 
                    else {
                        if ($extension -notin $entry.extensions) {
                            $detectedType = 'requires_check'
                        } 
                        else {
                        if ($entry.extensions -contains "docx" -or $entry.extensions -contains "doc") {
                            $detectedType = Get-CompressedFileType -filePath $filePath -detectedType $entry.extensions
                        }
                        if ($detectedType -eq "others" -and $extension -in $entry.extensions) {
                         $detectedType = $extension
                         } elseif ($detectedType -ne $extension -and $detectedType -ne "others") {
                         $detectedType = 'requires_check'
                         }
                    }
                        $matchFound = $true
                        break
                    }
                }
            }
        }
    }

    if ($detectedType -eq 'others' -and $extension -in $jsonContent.magic_numbers.extensions) {
        $detectedType = 'requires_check'
    }

    if ($detectedType -eq "others") {    
        $firstLine = Get-Content -Path $filePath -TotalCount 1
        if ($firstLine -match '^<\?xml') {
            $detectedType = "xml"
        }
    }

    return $detectedType
}
# SIG # Begin signature block
# MIImaQYJKoZIhvcNAQcCoIImWjCCJlYCAQExDzANBglghkgBZQMEAgEFADB5Bgor
# BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG
# KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCCY9ud8BQRULrwU
# 2VE5oi3Os9iui7NKkujAvzP2hF7PC6CCH/8wggWNMIIEdaADAgECAhAOmxiO+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
# twGpn1eqXijiuZQwggY1MIIEHaADAgECAhB/Kidy8q3kVvTPaSZfaX+zMA0GCSqG
# SIb3DQEBCwUAMFYxCzAJBgNVBAYTAlBMMSEwHwYDVQQKExhBc3NlY28gRGF0YSBT
# eXN0ZW1zIFMuQS4xJDAiBgNVBAMTG0NlcnR1bSBDb2RlIFNpZ25pbmcgMjAyMSBD
# QTAeFw0yMzExMDIwNjUzMjVaFw0yNDExMDEwNjUzMjRaMFsxCzAJBgNVBAYTAkZS
# MQ8wDQYDVQQHDAZUb3Vsb24xCzAJBgNVBAsMAklUMRYwFAYDVQQKDA1NZWhkaSBE
# YWtoYW1hMRYwFAYDVQQDDA1NZWhkaSBEYWtoYW1hMIIBojANBgkqhkiG9w0BAQEF
# AAOCAY8AMIIBigKCAYEAujQ+veep1KIlqcEmDI6UEPFQ/BjmnsnNVZW5XNSLQLhU
# OpsgbTkjGKW8Bso8gS3ul5qunY1ONd2h2sscJXuXmpUZDWSFI1emriL7yXPdQKX9
# dku2ucbVxmdnRHxtgPh0zqLZP//kgkDale3PjCI3kkBQWSJcVC66VdabhhHFafGa
# JSRTd13AB0hoC107AMXLBAuw73znDd4dfZxJMW4FTQzQT/MYr332Rn4YtFGbxi0f
# mNtm+uOR5YDGi//s51FnETxpeDV2NtyQsDPMn0OYjNxul931CVTLQJ87TlyMIxvt
# jswJ+Y5B8E9RqAOSNpSXk1p33y7vJJSKuIgbn2Bce8bugy2Gi6soca1yge1O2PZW
# lWEWz3dNCeWad7u040KTZ5WxHnE2UuBDYNPBhldcLZ21ZWdD8J5vY0iPVPjZfyw4
# eZzsj6PEyBMVI1U5FP9wX6D7qWlxGOFNKOQzg+Fk11ut+2b55aeDLrSwUUNtFCRZ
# /xZffJLyaFanwFfgLV2bAgMBAAGjggF4MIIBdDAMBgNVHRMBAf8EAjAAMD0GA1Ud
# HwQ2MDQwMqAwoC6GLGh0dHA6Ly9jY3NjYTIwMjEuY3JsLmNlcnR1bS5wbC9jY3Nj
# YTIwMjEuY3JsMHMGCCsGAQUFBwEBBGcwZTAsBggrBgEFBQcwAYYgaHR0cDovL2Nj
# c2NhMjAyMS5vY3NwLWNlcnR1bS5jb20wNQYIKwYBBQUHMAKGKWh0dHA6Ly9yZXBv
# c2l0b3J5LmNlcnR1bS5wbC9jY3NjYTIwMjEuY2VyMB8GA1UdIwQYMBaAFN10XUwA
# 23ufoHTKsW73PMAywHDNMB0GA1UdDgQWBBRTARHa/eTNBE999ctVnkA6rW5HwzBL
# BgNVHSAERDBCMAgGBmeBDAEEATA2BgsqhGgBhvZ3AgUBBDAnMCUGCCsGAQUFBwIB
# FhlodHRwczovL3d3dy5jZXJ0dW0ucGwvQ1BTMBMGA1UdJQQMMAoGCCsGAQUFBwMD
# MA4GA1UdDwEB/wQEAwIHgDANBgkqhkiG9w0BAQsFAAOCAgEAYiRKgn05ElQ35lhw
# NYle+DsOwLLCAJV1XN9SM78CxW/G4ASVXMDySbQseVIzJr7HXQxfqWK9mC1dqd6q
# 2cYexoG3sR2nQY0g/ylpseUt3GHUJi6rKVmH3Ii6a+iSv5LBf5L7wZCrCfrNHqj8
# W++l2Yh9s0l2goE+TGAfKSwfiHKE4gN0OKpmREsBS+VJAQhr437Jk2MwaZIpf9IQ
# fKDcdnY48x0qDr7NKqc8F+Iux8P9j4mrVfTw2by8t2JLXBdvsKwe/YTeW9JX5KIv
# hq7g+x8nl80i1xkgeZ1lJw/aZ8QRCt/AqvzrJk+CcdcDWjtaOEKV73w60j6fAbgz
# +DxF/i25caXESj51Lt0Op1pgEB9rhQHP0dxBhgA/3rhwfBfXTjFiYjSsZyrJyJoO
# YZecSNaQ9t5/pnocQg8AUQVYqA1l5R0dD5AEgvd6IlrKyBD9NZDALmcUljyghqDP
# /GdhecuQp7whAptt5MJQ2qq/LYulgDA8OFi+6SLe/TvYHYb6wMexzu/dD7duDAKt
# gLoMNVKCN/5kvai8ulL1bUnG0nAq9IoKyZqxHoQtiAk1IzCoAzYTofai5zZrlVNn
# z36+mDoXLwB7GBt2NZgAXFwSuH7386G3ZZNtOag5f96T4Zss7KFCzxxj2OOWFRLa
# 8mppbeJHflrjP/jI9pQRaB+e0dcwggauMIIElqADAgECAhAHNje3JFR82Ees/Shm
# Kl5bMA0GCSqGSIb3DQEBCwUAMGIxCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdp
# Q2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xITAfBgNVBAMTGERp
# Z2lDZXJ0IFRydXN0ZWQgUm9vdCBHNDAeFw0yMjAzMjMwMDAwMDBaFw0zNzAzMjIy
# MzU5NTlaMGMxCzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5EaWdpQ2VydCwgSW5jLjE7
# MDkGA1UEAxMyRGlnaUNlcnQgVHJ1c3RlZCBHNCBSU0E0MDk2IFNIQTI1NiBUaW1l
# U3RhbXBpbmcgQ0EwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDGhjUG
# SbPBPXJJUVXHJQPE8pE3qZdRodbSg9GeTKJtoLDMg/la9hGhRBVCX6SI82j6ffOc
# iQt/nR+eDzMfUBMLJnOWbfhXqAJ9/UO0hNoR8XOxs+4rgISKIhjf69o9xBd/qxkr
# PkLcZ47qUT3w1lbU5ygt69OxtXXnHwZljZQp09nsad/ZkIdGAHvbREGJ3HxqV3rw
# N3mfXazL6IRktFLydkf3YYMZ3V+0VAshaG43IbtArF+y3kp9zvU5EmfvDqVjbOSm
# xR3NNg1c1eYbqMFkdECnwHLFuk4fsbVYTXn+149zk6wsOeKlSNbwsDETqVcplicu
# 9Yemj052FVUmcJgmf6AaRyBD40NjgHt1biclkJg6OBGz9vae5jtb7IHeIhTZgirH
# kr+g3uM+onP65x9abJTyUpURK1h0QCirc0PO30qhHGs4xSnzyqqWc0Jon7ZGs506
# o9UD4L/wojzKQtwYSH8UNM/STKvvmz3+DrhkKvp1KCRB7UK/BZxmSVJQ9FHzNklN
# iyDSLFc1eSuo80VgvCONWPfcYd6T/jnA+bIwpUzX6ZhKWD7TA4j+s4/TXkt2ElGT
# yYwMO1uKIqjBJgj5FBASA31fI7tk42PgpuE+9sJ0sj8eCXbsq11GdeJgo1gJASgA
# DoRU7s7pXcheMBK9Rp6103a50g5rmQzSM7TNsQIDAQABo4IBXTCCAVkwEgYDVR0T
# AQH/BAgwBgEB/wIBADAdBgNVHQ4EFgQUuhbZbU2FL3MpdpovdYxqII+eyG8wHwYD
# VR0jBBgwFoAU7NfjgtJxXWRM3y5nP+e6mK4cD08wDgYDVR0PAQH/BAQDAgGGMBMG
# A1UdJQQMMAoGCCsGAQUFBwMIMHcGCCsGAQUFBwEBBGswaTAkBggrBgEFBQcwAYYY
# aHR0cDovL29jc3AuZGlnaWNlcnQuY29tMEEGCCsGAQUFBzAChjVodHRwOi8vY2Fj
# ZXJ0cy5kaWdpY2VydC5jb20vRGlnaUNlcnRUcnVzdGVkUm9vdEc0LmNydDBDBgNV
# HR8EPDA6MDigNqA0hjJodHRwOi8vY3JsMy5kaWdpY2VydC5jb20vRGlnaUNlcnRU
# cnVzdGVkUm9vdEc0LmNybDAgBgNVHSAEGTAXMAgGBmeBDAEEAjALBglghkgBhv1s
# BwEwDQYJKoZIhvcNAQELBQADggIBAH1ZjsCTtm+YqUQiAX5m1tghQuGwGC4QTRPP
# MFPOvxj7x1Bd4ksp+3CKDaopafxpwc8dB+k+YMjYC+VcW9dth/qEICU0MWfNthKW
# b8RQTGIdDAiCqBa9qVbPFXONASIlzpVpP0d3+3J0FNf/q0+KLHqrhc1DX+1gtqpP
# kWaeLJ7giqzl/Yy8ZCaHbJK9nXzQcAp876i8dU+6WvepELJd6f8oVInw1YpxdmXa
# zPByoyP6wCeCRK6ZJxurJB4mwbfeKuv2nrF5mYGjVoarCkXJ38SNoOeY+/umnXKv
# xMfBwWpx2cYTgAnEtp/Nh4cku0+jSbl3ZpHxcpzpSwJSpzd+k1OsOx0ISQ+UzTl6
# 3f8lY5knLD0/a6fxZsNBzU+2QJshIUDQtxMkzdwdeDrknq3lNHGS1yZr5Dhzq6YB
# T70/O3itTK37xJV77QpfMzmHQXh6OOmc4d0j/R0o08f56PGYX/sr2H7yRp11LB4n
# LCbbbxV7HhmLNriT1ObyF5lZynDwN7+YAN8gFk8n+2BnFqFmut1VwDophrCYoCvt
# lUG3OtUVmDG0YgkPCr2B2RP+v6TR81fZvAT6gt4y3wSJ8ADNXcL50CN/AAvkdgIm
# 2fBldkKmKYcJRyvmfxqkhQ/8mJb2VVQrH4D6wPIOK+XW+6kvRBVK5xMOHds3OBqh
# K/bt1nz8MIIGuTCCBKGgAwIBAgIRAJmjgAomVTtlq9xuhKaz6jkwDQYJKoZIhvcN
# AQEMBQAwgYAxCzAJBgNVBAYTAlBMMSIwIAYDVQQKExlVbml6ZXRvIFRlY2hub2xv
# Z2llcyBTLkEuMScwJQYDVQQLEx5DZXJ0dW0gQ2VydGlmaWNhdGlvbiBBdXRob3Jp
# dHkxJDAiBgNVBAMTG0NlcnR1bSBUcnVzdGVkIE5ldHdvcmsgQ0EgMjAeFw0yMTA1
# MTkwNTMyMThaFw0zNjA1MTgwNTMyMThaMFYxCzAJBgNVBAYTAlBMMSEwHwYDVQQK
# ExhBc3NlY28gRGF0YSBTeXN0ZW1zIFMuQS4xJDAiBgNVBAMTG0NlcnR1bSBDb2Rl
# IFNpZ25pbmcgMjAyMSBDQTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIB
# AJ0jzwQwIzvBRiznM3M+Y116dbq+XE26vest+L7k5n5TeJkgH4Cyk74IL9uP61ol
# RsxsU/WBAElTMNQI/HsE0uCJ3VPLO1UufnY0qDHG7yCnJOvoSNbIbMpT+Cci75sc
# Cx7UsKK1fcJo4TXetu4du2vEXa09Tx/bndCBfp47zJNsamzUyD7J1rcNxOw5g6FJ
# g0ImIv7nCeNn3B6gZG28WAwe0mDqLrvU49chyKIc7gvCjan3GH+2eP4mYJASflBT
# Q3HOs6JGdriSMVoD1lzBJobtYDF4L/GhlLEXWgrVQ9m0pW37KuwYqpY42grp/kSY
# E4BUQrbLgBMNKRvfhQPskDfZ/5GbTCyvlqPN+0OEDmYGKlVkOMenDO/xtMrMINRJ
# S5SY+jWCi8PRHAVxO0xdx8m2bWL4/ZQ1dp0/JhUpHEpABMc3eKax8GI1F03mSJVV
# 6o/nmmKqDE6TK34eTAgDiBuZJzeEPyR7rq30yOVw2DvetlmWssewAhX+cnSaaBKM
# Ej9O2GgYkPJ16Q5Da1APYO6n/6wpCm1qUOW6Ln1J6tVImDyAB5Xs3+JriasaiJ7P
# 5KpXeiVV/HIsW3ej85A6cGaOEpQA2gotiUqZSkoQUjQ9+hPxDVb/Lqz0tMjp6RuL
# SKARsVQgETwoNQZ8jCeKwSQHDkpwFndfCceZ/OfCUqjxAgMBAAGjggFVMIIBUTAP
# BgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBTddF1MANt7n6B0yrFu9zzAMsBwzTAf
# BgNVHSMEGDAWgBS2oVQ5AsOgP46KvPrU+Bym0ToO/TAOBgNVHQ8BAf8EBAMCAQYw
# EwYDVR0lBAwwCgYIKwYBBQUHAwMwMAYDVR0fBCkwJzAloCOgIYYfaHR0cDovL2Ny
# bC5jZXJ0dW0ucGwvY3RuY2EyLmNybDBsBggrBgEFBQcBAQRgMF4wKAYIKwYBBQUH
# MAGGHGh0dHA6Ly9zdWJjYS5vY3NwLWNlcnR1bS5jb20wMgYIKwYBBQUHMAKGJmh0
# dHA6Ly9yZXBvc2l0b3J5LmNlcnR1bS5wbC9jdG5jYTIuY2VyMDkGA1UdIAQyMDAw
# LgYEVR0gADAmMCQGCCsGAQUFBwIBFhhodHRwOi8vd3d3LmNlcnR1bS5wbC9DUFMw
# DQYJKoZIhvcNAQEMBQADggIBAHWIWA/lj1AomlOfEOxD/PQ7bcmahmJ9l0Q4SZC+
# j/v09CD2csX8Yl7pmJQETIMEcy0VErSZePdC/eAvSxhd7488x/Cat4ke+AUZZDtf
# Cd8yHZgikGuS8mePCHyAiU2VSXgoQ1MrkMuqxg8S1FALDtHqnizYS1bIMOv8znyJ
# jZQESp9RT+6NH024/IqTRsRwSLrYkbFq4VjNn/KV3Xd8dpmyQiirZdrONoPSlCRx
# CIi54vQcqKiFLpeBm5S0IoDtLoIe21kSw5tAnWPazS6sgN2oXvFpcVVpMcq0C4x/
# CLSNe0XckmmGsl9z4UUguAJtf+5gE8GVsEg/ge3jHGTYaZ/MyfujE8hOmKBAUkVa
# 7NMxRSB1EdPFpNIpEn/pSHuSL+kWN/2xQBJaDFPr1AX0qLgkXmcEi6PFnaw5T17U
# dIInA58rTu3mefNuzUtse4AgYmxEmJDodf8NbVcU6VdjWtz0e58WFZT7tST6EWQm
# x/OoHPelE77lojq7lpsjhDCzhhp4kfsfszxf9g2hoCtltXhCX6NqsqwTT7xe8LgM
# kH4hVy8L1h2pqGLT2aNCx7h/F95/QvsTeGGjY7dssMzq/rSshFQKLZ8lPb8hFTmi
# GDJNyHga5hZ59IGynk08mHhBFM/0MLeBzlAQq1utNjQprztZ5vv/NJy8ua9AGbwk
# MWkOMIIGwjCCBKqgAwIBAgIQBUSv85SdCDmmv9s/X+VhFjANBgkqhkiG9w0BAQsF
# ADBjMQswCQYDVQQGEwJVUzEXMBUGA1UEChMORGlnaUNlcnQsIEluYy4xOzA5BgNV
# BAMTMkRpZ2lDZXJ0IFRydXN0ZWQgRzQgUlNBNDA5NiBTSEEyNTYgVGltZVN0YW1w
# aW5nIENBMB4XDTIzMDcxNDAwMDAwMFoXDTM0MTAxMzIzNTk1OVowSDELMAkGA1UE
# BhMCVVMxFzAVBgNVBAoTDkRpZ2lDZXJ0LCBJbmMuMSAwHgYDVQQDExdEaWdpQ2Vy
# dCBUaW1lc3RhbXAgMjAyMzCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIB
# AKNTRYcdg45brD5UsyPgz5/X5dLnXaEOCdwvSKOXejsqnGfcYhVYwamTEafNqrJq
# 3RApih5iY2nTWJw1cb86l+uUUI8cIOrHmjsvlmbjaedp/lvD1isgHMGXlLSlUIHy
# z8sHpjBoyoNC2vx/CSSUpIIa2mq62DvKXd4ZGIX7ReoNYWyd/nFexAaaPPDFLnkP
# G2ZS48jWPl/aQ9OE9dDH9kgtXkV1lnX+3RChG4PBuOZSlbVH13gpOWvgeFmX40Qr
# StWVzu8IF+qCZE3/I+PKhu60pCFkcOvV5aDaY7Mu6QXuqvYk9R28mxyyt1/f8O52
# fTGZZUdVnUokL6wrl76f5P17cz4y7lI0+9S769SgLDSb495uZBkHNwGRDxy1Uc2q
# TGaDiGhiu7xBG3gZbeTZD+BYQfvYsSzhUa+0rRUGFOpiCBPTaR58ZE2dD9/O0V6M
# qqtQFcmzyrzXxDtoRKOlO0L9c33u3Qr/eTQQfqZcClhMAD6FaXXHg2TWdc2PEnZW
# pST618RrIbroHzSYLzrqawGw9/sqhux7UjipmAmhcbJsca8+uG+W1eEQE/5hRwqM
# /vC2x9XH3mwk8L9CgsqgcT2ckpMEtGlwJw1Pt7U20clfCKRwo+wK8REuZODLIivK
# 8SgTIUlRfgZm0zu++uuRONhRB8qUt+JQofM604qDy0B7AgMBAAGjggGLMIIBhzAO
# BgNVHQ8BAf8EBAMCB4AwDAYDVR0TAQH/BAIwADAWBgNVHSUBAf8EDDAKBggrBgEF
# BQcDCDAgBgNVHSAEGTAXMAgGBmeBDAEEAjALBglghkgBhv1sBwEwHwYDVR0jBBgw
# FoAUuhbZbU2FL3MpdpovdYxqII+eyG8wHQYDVR0OBBYEFKW27xPn783QZKHVVqll
# MaPe1eNJMFoGA1UdHwRTMFEwT6BNoEuGSWh0dHA6Ly9jcmwzLmRpZ2ljZXJ0LmNv
# bS9EaWdpQ2VydFRydXN0ZWRHNFJTQTQwOTZTSEEyNTZUaW1lU3RhbXBpbmdDQS5j
# cmwwgZAGCCsGAQUFBwEBBIGDMIGAMCQGCCsGAQUFBzABhhhodHRwOi8vb2NzcC5k
# aWdpY2VydC5jb20wWAYIKwYBBQUHMAKGTGh0dHA6Ly9jYWNlcnRzLmRpZ2ljZXJ0
# LmNvbS9EaWdpQ2VydFRydXN0ZWRHNFJTQTQwOTZTSEEyNTZUaW1lU3RhbXBpbmdD
# QS5jcnQwDQYJKoZIhvcNAQELBQADggIBAIEa1t6gqbWYF7xwjU+KPGic2CX/yyzk
# zepdIpLsjCICqbjPgKjZ5+PF7SaCinEvGN1Ott5s1+FgnCvt7T1IjrhrunxdvcJh
# N2hJd6PrkKoS1yeF844ektrCQDifXcigLiV4JZ0qBXqEKZi2V3mP2yZWK7Dzp703
# DNiYdk9WuVLCtp04qYHnbUFcjGnRuSvExnvPnPp44pMadqJpddNQ5EQSviANnqlE
# 0PjlSXcIWiHFtM+YlRpUurm8wWkZus8W8oM3NG6wQSbd3lqXTzON1I13fXVFoaVY
# JmoDRd7ZULVQjK9WvUzF4UbFKNOt50MAcN7MmJ4ZiQPq1JE3701S88lgIcRWR+3a
# EUuMMsOI5ljitts++V+wQtaP4xeR0arAVeOGv6wnLEHQmjNKqDbUuXKWfpd5OEhf
# ysLcPTLfddY2Z1qJ+Panx+VPNTwAvb6cKmx5AdzaROY63jg7B145WPR8czFVoIAR
# yxQMfq68/qTreWWqaNYiyjvrmoI1VygWy2nyMpqy0tg6uLFGhmu6F/3Ed2wVbK6r
# r3M66ElGt9V/zLY4wNjsHPW2obhDLN9OTH0eaHDAdwrUAuBcYLso/zjlUlrWrBci
# I0707NMX+1Br/wd3H3GXREHJuEbTbDJ8WC9nR2XlG3O2mflrLAZG70Ee8PBf4NvZ
# rZCARK+AEEGKMYIFwDCCBbwCAQEwajBWMQswCQYDVQQGEwJQTDEhMB8GA1UEChMY
# QXNzZWNvIERhdGEgU3lzdGVtcyBTLkEuMSQwIgYDVQQDExtDZXJ0dW0gQ29kZSBT
# aWduaW5nIDIwMjEgQ0ECEH8qJ3LyreRW9M9pJl9pf7MwDQYJYIZIAWUDBAIBBQCg
# gYQwGAYKKwYBBAGCNwIBDDEKMAigAoAAoQKAADAZBgkqhkiG9w0BCQMxDAYKKwYB
# BAGCNwIBBDAcBgorBgEEAYI3AgELMQ4wDAYKKwYBBAGCNwIBFTAvBgkqhkiG9w0B
# CQQxIgQgXok3QAt3lTuSndrJdsCfa6vn+Uax0tdAEqQGVFsKvr4wDQYJKoZIhvcN
# AQEBBQAEggGAS7aJphFmmFGAESsifcM6/XEj1dy4GwQZA4A1iIZA150PGhAFGvB/
# Ul5OYsq02IBk/44b24n7fzlfTLKTUujjEAZalX2PC8IlpzCZftTpUjVW0JatY+jX
# +z1r4biNzJMtmzGwEC4obRofNJBdU8BnJWzB+fDiibylNGfneghNRMR1RS7GYJAZ
# rLJmROrA8iPQGTVsrs6g2Y+9yvS9zSEc/fFD+UPsKbxSPIv5S/8+gFAQdnzXlfhz
# 1cVzqAIljTNLI4SEGs3IPoZ0KWQqsCOCC/rns4r+1Z/87/Rysh3Sbi7z2oxxd6zZ
# mMUdNcconT0XWOMchgRBb6gk3V6CJC9HpqYa4wd9ogRJx16kxs8xR55OcN29UkUx
# Q9izarolFwPUSzoLpb+msdA6ajmbi7kDBFgCnTclgk7IWRGowYYFjy5rd9FNKvOi
# lGOHC6Gkfnlj3w0YRrOib/yUSJuAZcVvE0F0DssjWXDvRqL23XiiaATU7AdqnKMQ
# 6iffE7FjB6MQoYIDIDCCAxwGCSqGSIb3DQEJBjGCAw0wggMJAgEBMHcwYzELMAkG
# A1UEBhMCVVMxFzAVBgNVBAoTDkRpZ2lDZXJ0LCBJbmMuMTswOQYDVQQDEzJEaWdp
# Q2VydCBUcnVzdGVkIEc0IFJTQTQwOTYgU0hBMjU2IFRpbWVTdGFtcGluZyBDQQIQ
# BUSv85SdCDmmv9s/X+VhFjANBglghkgBZQMEAgEFAKBpMBgGCSqGSIb3DQEJAzEL
# BgkqhkiG9w0BBwEwHAYJKoZIhvcNAQkFMQ8XDTI0MTAwMjIxNDkxNVowLwYJKoZI
# hvcNAQkEMSIEIGED0BpRM1+Ueb2K1KIwkH+4Ky3I/saa7SML/suK+8yfMA0GCSqG
# SIb3DQEBAQUABIICAEd6Mqg8gsvIJM2YdJFIWvSpPxPKIXTcjNdKJB1Q9FfuNrPF
# EVKkSJp86ULpfnLe/ObQbBDN4GIqecwp0t3EL6GweukAkul89jDhXCXo+a9jN8+F
# QVRaqZ+LE+VVjNZYD4qkuJ03YVmp+4w3PzHZDx9v/QZ+I28TAqZ6jN4Wft2NvXiS
# BYG1qO7nuXACN0JsvB7vEZZl741ItGPDyNvM6LdIn90fm6iFdNuTdLRX0HfCv1id
# TeUDhfl8jjXGmaUru+dJPSXaO6a/JvVaihEoxMpZJ3PQcAQZ5xdiLRCqQ9TBc60i
# KbewPtkkd6KT06eZmjoBnh9Xrtc5KrxU7e589WY+oYqQ7UOfIwRdqIGmoHMJNB+H
# uaaXVpnGtSf/ownl+elPK+5KJnHBxQCGRSD36Qsv71OaOXsEZ+nUQcqeRO+WigoO
# rDG7Ha5oTItOWZrxEyNFiDV60j008e4m3JKuBmGBkdDcIh5eQW90Kl6Cl1A41H12
# L6AP9oqnHEN6zLcYnTVlsEWSR7FzkeAILak8P4DnxLNrh8HAaQhRuLVZO7/oTKNO
# OIA19Mf6qNpAGaqPl6AUrxzYNHgETit7b0VAUwLQm9tLLhmfbRL12k8yyHOFL7Yr
# l2rDLQnWOpDijxG76pOq7ZddO30YBNonGBSiBk62OiFANFOAF7pvF0C8wK3j
# SIG # End signature block