Private/SM_PrivateFunctions.ps1

<#
.SYNOPSIS
Retrieves inbound connector settings from a JSON configuration file.
 
.DESCRIPTION
The Get-SM365InboundConnectorSettings function reads inbound connector settings from a JSON file located
in the specified path and returns the settings based on the specified connector type (e.g., Default or Partner).
 
.PARAMETER Type
Specifies the type of connector settings to retrieve. The valid values are 'Default' and 'Partner'.
The parameter defaults to 'Default' if not specified.
 
.EXAMPLE
Get-SM365InboundConnectorSettings -Type 'Partner'
 
This example retrieves the inbound connector settings for the 'Partner' type from the JSON configuration file.
 
.EXAMPLE
Get-SM365InboundConnectorSettings
 
This example retrieves the default inbound connector settings, as the Type parameter defaults to 'Default'.
 
.NOTES
The function assumes that the JSON configuration file SM_Inbound.json is located in the ..\ExOConfig\SM_Connectors directory relative to the script's location. The JSON structure should have keys corresponding to the valid types ('Default', 'Partner') for correct data retrieval.
#>

function Get-SM365InboundConnectorSettings
{
    [CmdletBinding()]
    Param
    (
        [ValidateSet('Default','Partner')]
        [String]$Type = 'Default'
    )
    begin {
        $ret = $null
        $raw = $null
    }
    process {
        Write-Verbose "Loading inbound connector settings for type $Type"
        $raw = (Get-Content "$PSScriptRoot\..\ExOConfig\SM_Connectors\SM_Inbound.json" -Raw|Convertfrom-Json -AsHashtable)
        $ret = $raw.$Type    
    }
    end {
        return $ret
    }
}

<#
.SYNOPSIS
Retrieves outbound connector settings from a JSON configuration file.
 
.DESCRIPTION
The Get-SM365OutboundConnectorSettings function reads outbound connector settings from a JSON file
located in the specified path and returns the settings based on the specified connector type
(e.g., Default or Partner).
 
.PARAMETER Type
Specifies the type of connector settings to retrieve. The valid values are 'Default' and 'Partner'.
The parameter defaults to 'Default' if not specified.
 
.EXAMPLE
Get-SM365OutboundConnectorSettings -Type 'Partner'
 
This example retrieves the outbound connector settings for the 'Partner' type from the JSON configuration file.
 
.EXAMPLE
Get-SM365OutboundConnectorSettings
 
This example retrieves the default outbound connector settings, as the Type parameter defaults to 'Default'.
 
.NOTES
The function assumes that the JSON configuration file SM_OutBound.json is located in the ..\ExOConfig\SM_Connectors directory relative to the script's location. The JSON structure should have keys corresponding to the
valid types ('Default', 'Partner') for correct data retrieval.
#>

function Get-SM365OutboundConnectorSettings
{
    [CmdletBinding()]
    Param
    (
        [ValidateSet('Default','Partner')]
        [String]$Type = 'Default'
    )

    begin {
        $ret = $null
        $raw = $null
    }
    process {
        Write-Verbose "Loading outbound connector settings for type $Type"
        $raw = (Get-Content "$PSScriptRoot\..\ExOConfig\SM_Connectors\SM_OutBound.json" -Raw|Convertfrom-Json -AsHashtable)
        $ret= $Raw.$Type
    }
    end
    {
        return $ret
    }
}

<#
.SYNOPSIS
Retrieves transport rule settings from a JSON file and returns them as a hashtable.
 
.DESCRIPTION
The Get-SM365TransportRuleSettings function reads a JSON file specified by the user,
parses its contents, and returns the transport rule settings found within the "default" section of the JSON as a hashtable.
 
.PARAMETER File
Specifies the path to the JSON file that contains the transport rule settings. This parameter
is mandatory.
 
.EXAMPLE
Get-SM365TransportRuleSettings -File "C:\path\to\settings.json"
 
This example reads the specified JSON file, extracts the `default` section, and returns it as
a hashtable.
 
.NOTES
The function assumes that the JSON file is properly formatted and that it includes a "default" key at the top level. The function reads the entire file as a raw string and converts it into a hashtable for easy access and processing.
#>

function Get-SM365TransportRuleSettings
{
    [CmdLetBinding()]
    Param
    (
        [Parameter(Mandatory = $true)]
        $File
    )
    begin {
        $ret = $null
        $raw = $null
    }
    process {
            $raw = (Get-Content $file -Raw|Convertfrom-Json -AsHashtable)
            $ret = $raw.default
            return $ret    
    }
    end {
    }
}

<#
.SYNOPSIS
Adds unique strings to an existing string array only if they do not already exist in the array.
 
.DESCRIPTION
The Add-UniqueStringsToArray function takes an existing string array and an array of new strings.
It checks each new string to see if it already exists in the existing array. If a string is not
already present, it adds the string to the array. The function returns the updated array.
 
.PARAMETER ExistingArray
The existing string array to which new unique strings will be added. If null or empty, the array
is initialized as an empty array.
 
.PARAMETER NewStrings
The array of new strings to be added to the existing array if they do not already exist in it.
 
.EXAMPLE
$existingArray = @('example.com', 'test.com')
$newStrings = @('test.com', 'newdomain.com', 'sample.org')
$updatedArray = Add-UniqueStringsToArray -ExistingArray $existingArray -NewStrings $newStrings
Write-Host "Updated Array: $updatedArray"
 
This example takes an array containing 'example.com' and 'test.com', and adds 'newdomain.com'
and 'sample.org' while skipping 'test.com' since it already exists in the existing array.
The output will be:
Updated Array: example.com test.com newdomain.com sample.org
#>

function Add-UniqueStringsToArray {
    param (
        [string[]]$ExistingArray,
        [string[]]$NewStrings
    )

    # Wenn das vorhandene Array null oder leer ist, initialisieren wir es
    if (-not $ExistingArray) {
        $ExistingArray = @()
    }

    # Durchlaufen der neuen Strings und Hinzufügen, wenn sie nicht bereits existieren
    foreach ($string in $NewStrings) {
        if ($ExistingArray -notcontains $string) {
            $ExistingArray += $string
        }
    }

    # Rückgabe des aktualisierten Arrays
    return $ExistingArray
}


<#
.SYNOPSIS
    Filters IPv6 addresses out of an array of IP addresses.
 
.DESCRIPTION
    Takes an array of IP address strings (mixed IPv4/IPv6) and returns only
    the IPv4 addresses. Useful when downstream Exchange Online cmdlets only
    accept IPv4 entries (e.g. Hosted Connection Filter Policy allow lists).
 
.PARAMETER IPArray
    Array of IP address strings to filter. May contain a mix of IPv4 and IPv6
    addresses. The original array is not modified.
 
.EXAMPLE
    Remove-IPv6Address -IPArray '192.0.2.10','2001:db8::1','198.51.100.5'
 
    Returns: 192.0.2.10, 198.51.100.5
 
.INPUTS
    System.String[]
 
.OUTPUTS
    System.String[]
    Array of IPv4 address strings.
 
.NOTES
    Filtering uses a regular expression for IPv6 detection; any string matching
    the IPv6 pattern is excluded. Non-IP strings that do not match the IPv6
    pattern are returned as-is.
 
.LINK
    https://github.com/seppmail/SEPPmail365cloud/blob/main/README.md#setup-the-integration
#>

function Remove-IPv6Address {
    [CmdLetBinding(
        HelpURI = 'https://github.com/seppmail/SEPPmail365cloud/blob/main/README.md#setup-the-integration'
    )]
    param (
        [Parameter(Mandatory=$true)]
        [string[]]$IPArray
    )
    # Regex for IPv6
    $ipv6Pattern = "(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9])?[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9])?[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9])?[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9])?[0-9]))"

    # Filter of IPv4-Adresses
    $ipv4Array = $IPArray | Where-Object { $_ -notmatch $ipv6Pattern }
    return $ipv4Array
}

# SIG # Begin signature block
# MIIVyAYJKoZIhvcNAQcCoIIVuTCCFbUCAQExDzANBglghkgBZQMEAgEFADB5Bgor
# BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG
# KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCDb9A6DSi/ADnnj
# QboMYkTH4XL5ixHgNgrEWGaKoMBWMKCCEgQwggVvMIIEV6ADAgECAhBI/JO0YFWU
# jTanyYqJ1pQWMA0GCSqGSIb3DQEBDAUAMHsxCzAJBgNVBAYTAkdCMRswGQYDVQQI
# DBJHcmVhdGVyIE1hbmNoZXN0ZXIxEDAOBgNVBAcMB1NhbGZvcmQxGjAYBgNVBAoM
# EUNvbW9kbyBDQSBMaW1pdGVkMSEwHwYDVQQDDBhBQUEgQ2VydGlmaWNhdGUgU2Vy
# dmljZXMwHhcNMjEwNTI1MDAwMDAwWhcNMjgxMjMxMjM1OTU5WjBWMQswCQYDVQQG
# EwJHQjEYMBYGA1UEChMPU2VjdGlnbyBMaW1pdGVkMS0wKwYDVQQDEyRTZWN0aWdv
# IFB1YmxpYyBDb2RlIFNpZ25pbmcgUm9vdCBSNDYwggIiMA0GCSqGSIb3DQEBAQUA
# A4ICDwAwggIKAoICAQCN55QSIgQkdC7/FiMCkoq2rjaFrEfUI5ErPtx94jGgUW+s
# hJHjUoq14pbe0IdjJImK/+8Skzt9u7aKvb0Ffyeba2XTpQxpsbxJOZrxbW6q5KCD
# J9qaDStQ6Utbs7hkNqR+Sj2pcaths3OzPAsM79szV+W+NDfjlxtd/R8SPYIDdub7
# P2bSlDFp+m2zNKzBenjcklDyZMeqLQSrw2rq4C+np9xu1+j/2iGrQL+57g2extme
# me/G3h+pDHazJyCh1rr9gOcB0u/rgimVcI3/uxXP/tEPNqIuTzKQdEZrRzUTdwUz
# T2MuuC3hv2WnBGsY2HH6zAjybYmZELGt2z4s5KoYsMYHAXVn3m3pY2MeNn9pib6q
# RT5uWl+PoVvLnTCGMOgDs0DGDQ84zWeoU4j6uDBl+m/H5x2xg3RpPqzEaDux5mcz
# mrYI4IAFSEDu9oJkRqj1c7AGlfJsZZ+/VVscnFcax3hGfHCqlBuCF6yH6bbJDoEc
# QNYWFyn8XJwYK+pF9e+91WdPKF4F7pBMeufG9ND8+s0+MkYTIDaKBOq3qgdGnA2T
# OglmmVhcKaO5DKYwODzQRjY1fJy67sPV+Qp2+n4FG0DKkjXp1XrRtX8ArqmQqsV/
# AZwQsRb8zG4Y3G9i/qZQp7h7uJ0VP/4gDHXIIloTlRmQAOka1cKG8eOO7F/05QID
# AQABo4IBEjCCAQ4wHwYDVR0jBBgwFoAUoBEKIz6W8Qfs4q8p74Klf9AwpLQwHQYD
# VR0OBBYEFDLrkpr/NZZILyhAQnAgNpFcF4XmMA4GA1UdDwEB/wQEAwIBhjAPBgNV
# HRMBAf8EBTADAQH/MBMGA1UdJQQMMAoGCCsGAQUFBwMDMBsGA1UdIAQUMBIwBgYE
# VR0gADAIBgZngQwBBAEwQwYDVR0fBDwwOjA4oDagNIYyaHR0cDovL2NybC5jb21v
# ZG9jYS5jb20vQUFBQ2VydGlmaWNhdGVTZXJ2aWNlcy5jcmwwNAYIKwYBBQUHAQEE
# KDAmMCQGCCsGAQUFBzABhhhodHRwOi8vb2NzcC5jb21vZG9jYS5jb20wDQYJKoZI
# hvcNAQEMBQADggEBABK/oe+LdJqYRLhpRrWrJAoMpIpnuDqBv0WKfVIHqI0fTiGF
# OaNrXi0ghr8QuK55O1PNtPvYRL4G2VxjZ9RAFodEhnIq1jIV9RKDwvnhXRFAZ/ZC
# J3LFI+ICOBpMIOLbAffNRk8monxmwFE2tokCVMf8WPtsAO7+mKYulaEMUykfb9gZ
# pk+e96wJ6l2CxouvgKe9gUhShDHaMuwV5KZMPWw5c9QLhTkg4IUaaOGnSDip0TYl
# d8GNGRbFiExmfS9jzpjoad+sPKhdnckcW67Y8y90z7h+9teDnRGWYpquRRPaf9xH
# +9/DUp/mBlXpnYzyOmJRvOwkDynUWICE5EV7WtgwggYaMIIEAqADAgECAhBiHW0M
# UgGeO5B5FSCJIRwKMA0GCSqGSIb3DQEBDAUAMFYxCzAJBgNVBAYTAkdCMRgwFgYD
# VQQKEw9TZWN0aWdvIExpbWl0ZWQxLTArBgNVBAMTJFNlY3RpZ28gUHVibGljIENv
# ZGUgU2lnbmluZyBSb290IFI0NjAeFw0yMTAzMjIwMDAwMDBaFw0zNjAzMjEyMzU5
# NTlaMFQxCzAJBgNVBAYTAkdCMRgwFgYDVQQKEw9TZWN0aWdvIExpbWl0ZWQxKzAp
# BgNVBAMTIlNlY3RpZ28gUHVibGljIENvZGUgU2lnbmluZyBDQSBSMzYwggGiMA0G
# CSqGSIb3DQEBAQUAA4IBjwAwggGKAoIBgQCbK51T+jU/jmAGQ2rAz/V/9shTUxjI
# ztNsfvxYB5UXeWUzCxEeAEZGbEN4QMgCsJLZUKhWThj/yPqy0iSZhXkZ6Pg2A2NV
# DgFigOMYzB2OKhdqfWGVoYW3haT29PSTahYkwmMv0b/83nbeECbiMXhSOtbam+/3
# 6F09fy1tsB8je/RV0mIk8XL/tfCK6cPuYHE215wzrK0h1SWHTxPbPuYkRdkP05Zw
# mRmTnAO5/arnY83jeNzhP06ShdnRqtZlV59+8yv+KIhE5ILMqgOZYAENHNX9SJDm
# +qxp4VqpB3MV/h53yl41aHU5pledi9lCBbH9JeIkNFICiVHNkRmq4TpxtwfvjsUe
# dyz8rNyfQJy/aOs5b4s+ac7IH60B+Ja7TVM+EKv1WuTGwcLmoU3FpOFMbmPj8pz4
# 4MPZ1f9+YEQIQty/NQd/2yGgW+ufflcZ/ZE9o1M7a5Jnqf2i2/uMSWymR8r2oQBM
# dlyh2n5HirY4jKnFH/9gRvd+QOfdRrJZb1sCAwEAAaOCAWQwggFgMB8GA1UdIwQY
# MBaAFDLrkpr/NZZILyhAQnAgNpFcF4XmMB0GA1UdDgQWBBQPKssghyi47G9IritU
# pimqF6TNDDAOBgNVHQ8BAf8EBAMCAYYwEgYDVR0TAQH/BAgwBgEB/wIBADATBgNV
# HSUEDDAKBggrBgEFBQcDAzAbBgNVHSAEFDASMAYGBFUdIAAwCAYGZ4EMAQQBMEsG
# A1UdHwREMEIwQKA+oDyGOmh0dHA6Ly9jcmwuc2VjdGlnby5jb20vU2VjdGlnb1B1
# YmxpY0NvZGVTaWduaW5nUm9vdFI0Ni5jcmwwewYIKwYBBQUHAQEEbzBtMEYGCCsG
# AQUFBzAChjpodHRwOi8vY3J0LnNlY3RpZ28uY29tL1NlY3RpZ29QdWJsaWNDb2Rl
# U2lnbmluZ1Jvb3RSNDYucDdjMCMGCCsGAQUFBzABhhdodHRwOi8vb2NzcC5zZWN0
# aWdvLmNvbTANBgkqhkiG9w0BAQwFAAOCAgEABv+C4XdjNm57oRUgmxP/BP6YdURh
# w1aVcdGRP4Wh60BAscjW4HL9hcpkOTz5jUug2oeunbYAowbFC2AKK+cMcXIBD0Zd
# OaWTsyNyBBsMLHqafvIhrCymlaS98+QpoBCyKppP0OcxYEdU0hpsaqBBIZOtBajj
# cw5+w/KeFvPYfLF/ldYpmlG+vd0xqlqd099iChnyIMvY5HexjO2AmtsbpVn0OhNc
# WbWDRF/3sBp6fWXhz7DcML4iTAWS+MVXeNLj1lJziVKEoroGs9Mlizg0bUMbOalO
# hOfCipnx8CaLZeVme5yELg09Jlo8BMe80jO37PU8ejfkP9/uPak7VLwELKxAMcJs
# zkyeiaerlphwoKx1uHRzNyE6bxuSKcutisqmKL5OTunAvtONEoteSiabkPVSZ2z7
# 6mKnzAfZxCl/3dq3dUNw4rg3sTCggkHSRqTqlLMS7gjrhTqBmzu1L90Y1KWN/Y5J
# KdGvspbOrTfOXyXvmPL6E52z1NZJ6ctuMFBQZH3pwWvqURR8AgQdULUvrxjUYbHH
# j95Ejza63zdrEcxWLDX6xWls/GDnVNueKjWUH3fTv1Y8Wdho698YADR7TNx8X8z2
# Bev6SivBBOHY+uqiirZtg0y9ShQoPzmCcn63Syatatvx157YK9hlcPmVoa1oDE5/
# L9Uo2bC5a4CH2RwwggZvMIIE16ADAgECAhBIqMP3CCLHOHtOKuaWNyeFMA0GCSqG
# SIb3DQEBDAUAMFQxCzAJBgNVBAYTAkdCMRgwFgYDVQQKEw9TZWN0aWdvIExpbWl0
# ZWQxKzApBgNVBAMTIlNlY3RpZ28gUHVibGljIENvZGUgU2lnbmluZyBDQSBSMzYw
# HhcNMjYwNDE1MDAwMDAwWhcNMjcwNzE0MjM1OTU5WjBmMQswCQYDVQQGEwJERTEP
# MA0GA1UECAwGQmF5ZXJuMSIwIAYDVQQKDBlTRVBQbWFpbCBEZXV0c2NobGFuZCBH
# bWJIMSIwIAYDVQQDDBlTRVBQbWFpbCBEZXV0c2NobGFuZCBHbWJIMIICIjANBgkq
# hkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAvAFzE8MbJpvQt+IdIh1M+bKYsJBFDk4b
# 9ySe25IrCi00B9o5XmQtIw42MqyIKbUq1tDARtp9KTQedEP9W+rflAF2l+0Z046J
# kiqumU9/enbqWLDyln1aS/p7HOgwZFMhnsR9zH0MfFckiklUmkzJO+vmzYAK7ZmD
# xajNLJs0gkGRU2/BecAx/TSvLXMaKONsKZCyMKQCnwo1mCY/tFl5EgUz7YQFrPOR
# BQGfQke/hkdBfQDqNRsi/J6+KhJWc6LvgQihdRg/INQbQsTxlow18NWvyFsjjueH
# 7kG6HR4YKfbv07xgrsIh8xvq9ZJ1SBhLXmkg4SdoQGASjqR6o3keAX+bDRFf+hml
# WWJp/FqVHR5QomF3vbK2/bbz4jAclYSPx/sPasNJ0YnKFkgmowZ7Ysa0KA0/egBg
# tI4gJ+8V7zrqIVEG3rMQh9KCdMnJqP2aM9o4gUzQvE1M4x606liX9EWwdLLS+fe7
# 9o+Fzo5oH4wBE/En6hQQkzseHHu+TXCDd6zUUZ/PlTK0gTaDIRXt6UzPNqJ4RiRC
# W2pNFcPt078qqVTuwKUXoE4ufxGgXKFrZlCYST/9eG1TnW2oq19nz8A333GCsL3g
# poNIKvfmDyGMMNzvx2aeqn2v6e75z8kH19iGSNZ51xT+WgS9F1aIvjz08/T7XAv7
# iDPF1/gPIp8CAwEAAaOCAakwggGlMB8GA1UdIwQYMBaAFA8qyyCHKLjsb0iuK1Sm
# KaoXpM0MMB0GA1UdDgQWBBS30/Tq+alF3j2BY5up8n5zpAU23DAOBgNVHQ8BAf8E
# BAMCB4AwDAYDVR0TAQH/BAIwADATBgNVHSUEDDAKBggrBgEFBQcDAzBKBgNVHSAE
# QzBBMDUGDCsGAQQBsjEBAgEDAjAlMCMGCCsGAQUFBwIBFhdodHRwczovL3NlY3Rp
# Z28uY29tL0NQUzAIBgZngQwBBAEwSQYDVR0fBEIwQDA+oDygOoY4aHR0cDovL2Ny
# bC5zZWN0aWdvLmNvbS9TZWN0aWdvUHVibGljQ29kZVNpZ25pbmdDQVIzNi5jcmww
# eQYIKwYBBQUHAQEEbTBrMEQGCCsGAQUFBzAChjhodHRwOi8vY3J0LnNlY3RpZ28u
# Y29tL1NlY3RpZ29QdWJsaWNDb2RlU2lnbmluZ0NBUjM2LmNydDAjBggrBgEFBQcw
# AYYXaHR0cDovL29jc3Auc2VjdGlnby5jb20wHgYDVR0RBBcwFYETc3VwcG9ydEBz
# ZXBwbWFpbC5jaDANBgkqhkiG9w0BAQwFAAOCAYEAi7fmb5UYoemWG3CC4K2UZWVr
# R6GOfi8gbJKgjPbKO4zrCrU/x6cOdyp6scKZfUEGFDf8KH6pP4pAQv1Hsbi49gU2
# kxoUWLlCiipn05qJY663DHx9hlStej/ZdEatou0wyCDiG5xD7kmG+1t6iLyyCBgE
# B88tJpzTjI61qXmBTS/FGEOAsB4SDEW1ngA7bc5FOv4IUKA43hp8M+N3GeYFzDqw
# JELYEfVVYheBW3o7q4VrCdfFEuaQihOtvfDfYpP6ANgekNn8HdsMT8rx9D1I50Rl
# i/qQFo2BOuPyb2SIQPzJvCs5wgi5qgp1nHiN6igumu2Cz7BmGjOazGUgCSUY5Qwy
# E8+F+R2tVM+2O15rfX01+e56ZfojBEiEjMwfPHs3fa3V3gokWWNwUMkton/v0R/n
# l2zjmOr2okohOINZEDh9frg21zUCN5ZD8Y4zQWuiJLCvvvBZs0JR4c9xl2k2wtw/
# QLPhGU69zM3smGpRoLE8M6zvUvSU7jXjvefazUniMYIDGjCCAxYCAQEwaDBUMQsw
# CQYDVQQGEwJHQjEYMBYGA1UEChMPU2VjdGlnbyBMaW1pdGVkMSswKQYDVQQDEyJT
# ZWN0aWdvIFB1YmxpYyBDb2RlIFNpZ25pbmcgQ0EgUjM2AhBIqMP3CCLHOHtOKuaW
# NyeFMA0GCWCGSAFlAwQCAQUAoIGEMBgGCisGAQQBgjcCAQwxCjAIoAKAAKECgAAw
# GQYJKoZIhvcNAQkDMQwGCisGAQQBgjcCAQQwHAYKKwYBBAGCNwIBCzEOMAwGCisG
# AQQBgjcCARUwLwYJKoZIhvcNAQkEMSIEIFHvABrICOev8tXSM0Lg7FYqnhxdixc1
# KTAWhwB8ZP1qMA0GCSqGSIb3DQEBAQUABIICAIC+gyqgmZObUf0cZHux/oNQdQWc
# rfP1P1T1b8qotDe/7gDSIH5nnaiHLp0BZ72wWar0vTIl5aRaeaEIrOKPlLnHNrcx
# PScUsybjNA/g2yps+U0w+gqq39piUu01QTy6YcENgdj4zDm5Is/9I+Encp+bAuaZ
# Y+Mg1sGHP48CJTPDRgRYHSWS23jWARD7dO8IKzTw7NxdkTLmdgjOIpe9uqoIO6Rn
# c5fHlugrZEZr/pJkpmYBl4+CpROXz/v/Rrtzi+SX4VfJovc5NLqz4BJYjiSrSZf1
# aHucqOhaoRzNyYHkKHZF220EwrOBxE6TCaW+MXBT6CPchtnn5KQfbZF76+zsUvHI
# Ey633IbJyyMoanpwDwcDCmMayIbYMZY4X88MmSzv58ncBryjHryERpUq3MAvjycc
# HCpcVmRXS8qbk85cvFTrEw9lCKXwNdwtUgODOim3enFa2UlG6j17vckdknTpj5oS
# P4sxNDtw/RZKUKT6hl2W7keO5FYtH46zhpJ7Bo4eUtmzBetwuu/ZuPN1H1QsurwN
# FXthZ6PKUD7O/VsaxFpb1AZcbnqmEmTjQXvDZWOYbisj1S6fpuNFLmiQBCe3OBg+
# Xt2pCTJwsmmNM91hQ4tRfptRi54eLYw5EXhrUZ/O0y+UzA97M1XalXiq4S1CSGhU
# yzM7+OTjG6KZ/TGQ
# SIG # End signature block