Public/SEPPmailAPI-Statistics.ps1

<#
.SYNOPSIS
    Gets usage statistics of your SEPPmail appliance.
 
.DESCRIPTION
    Queries the SEPPmail API endpoint /statistics and returns what the
    appliance has processed over time. The result can be limited to user or
    domain statistics and - in MSP / multi-customer environments - calculated
    on a per-customer basis.
 
.PARAMETER customerStatistics
    If $true, statistics are calculated on a per-customer basis. Can be
    narrowed down with the -customer parameter.
 
    Default: $false
 
.PARAMETER customer
    Filters the statistics type that is returned.
    Valid values: 'user', 'domain', 'all'.
 
    Default: '[default]'
 
.PARAMETER SMAHost
    SEPPmail API hostname. Defaults to the configured value.
 
.PARAMETER SMAPort
    SEPPmail API port. Defaults to the configured value.
 
.PARAMETER SMAVersion
    SEPPmail API version. Defaults to the configured value.
 
.PARAMETER SMACred
    API credentials (PSCredential). Defaults to the configured value.
 
.PARAMETER SMASkipCertCheck
    Skip SSL certificate validation. Use only in test environments.
 
.OUTPUTS
    System.Management.Automation.PSCustomObject
        Returns the user statistics or domain statistics object.
 
.NOTES
    - Requires an active SEPPmail API session (New-SMAConfiguration).
    - Alias: Get-SMAStat
 
.LINK
    Get-SMALicense
 
.LINK
    Get-SMALftStatistics
 
.EXAMPLE
    PS C:\> Get-SMAStatistics
    Returns all available statistics (default).
 
.EXAMPLE
    PS C:\> Get-SMAStatistics -customer user
    Filters the statistics to user data.
 
.EXAMPLE
    PS C:\> Get-SMAStatistics -customer domain
    Filters the statistics to domain data.
 
.EXAMPLE
    PS C:\> Get-SMAStatistics -customerStatistics $true
    Calculates the statistics on a per-customer basis.
#>

function Get-SMAStatistics
{
    [CmdletBinding()]
    param (
        #region API parameters
        [Parameter(
                   Mandatory = $false,
                 HelpMessage = 'Calculate statistics on a per-customer basis. Can be limited via the customer parameter'
        )]
        [bool]$customerStatistics = $false,
    
        [Parameter(
                   Mandatory = $false,
                 HelpMessage = 'Filter to a specific customer type'
        )]
        [ValidateSet('user', 'domain', 'all')]
        [String]$customer = '[default]',
        #endregion API parameters

        #region Config parameters block
        [Parameter(Mandatory = $false)]
        [String]$SMAHost = $Script:activeCfg.SMAHost,

        [Parameter(Mandatory = $false)]
        [int]$SMAPort = $Script:activeCfg.SMAPort,

        [Parameter(Mandatory = $false)]
        [String]$SMAVersion = $Script:activeCfg.SMAPIVersion,

        [Parameter(
            Mandatory=$false
            )]
        [System.Management.Automation.PSCredential]$SMACred=$Script:activeCfg.SMACred,

        [Parameter(
            Mandatory=$false
            )]
        [switch]$SMASkipCertCheck=$Script:activeCfg.SMAskipCertCheck
        #endregion
    )

    begin {
        if (! (verifyVars -VarList $Script:requiredVarList))
        {
            Throw($missingVarsMessage);
        }; # end if
        $smaParams = @{
            Host    = $SMAHost
            Port    = $SMAPort
            Version = $SMAVersion
        } # end smaParams

        Write-Verbose "Creating URL path"
        $uriPath = "{0}" -f 'statistics'

    }
    process {
        Write-Verbose "Building full request uri"
        $uri = $null
        $boundParam = @{
            customerStatistics = $customerstatistics
            customer = $customer
        }
        $uri = New-SMAQueryString -uriPath $uriPath -qParam $boundParam @smaParams
        
        Write-verbose "Crafting InvokeParam for Invoke-SMARestMethod"
        $invokeParam = @{
            Uri         = $uri 
            Method      = 'GET'
            Cred        =  $SMACred
            SkipCertCheck = $SMASkipCertCheck
        }

        Write-Verbose "Replace wrong '%40' value with '@'"
        $invokeparam.Uri = ($invokeParam.Uri).Replace('%40','@')
        Write-Verbose "Call Invoke-SMARestMethod $uri" 
        $statsInfo = Invoke-SMARestMethod @invokeParam
        #Write-Verbose 'Converting Umlauts from ISO-8859-1'
        #Encinfo = ConvertFrom-SMAPIFormat -inputObject $encInfoRaw

        # Userobject
        if ($statsInfo) {
            if ($StatsInfo.User) {
                return $statsInfo.User
            }
            if ($StatsInfo.Domain) {
                return $statsInfo.Domain
            }
        }
    }
    end {
    }
}

<#
.SYNOPSIS
    Retrieves license statistics from your SEPPmail appliance.
 
.DESCRIPTION
    The Get-SMALicense CmdLet queries the SEPPmail API endpoint /statistics/license for license information.
    It has 2 outputs. Either License information or customer license statistics information (for MSPs or multi-customer environments)
     
.PARAMETER Customer
    Returns license information for a specific customer (tenant).
 
.PARAMETER CustomerStatistics
    Returns usage statistics per appliance-customer instead of the plain
    license object. Alias: stats
 
.PARAMETER showGlobal
    Returns the global license usage statistics of the appliance.
    Alias: global
 
.PARAMETER SMAVersion
    SEPPmail API version. Defaults to the configured value.
 
.PARAMETER SMAHost
    SEPPmail API hostname. Defaults to the configured value.
 
.PARAMETER SMAPort
    SEPPmail API port. Defaults to the configured value.
 
.PARAMETER SMACred
    API credentials (PSCredential). Defaults to the configured value.
 
.PARAMETER SMASkipCertCheck
    Skip SSL certificate validation. Use only in test environments.
 
.OUTPUTS
    System.Management.Automation.PSCustomObject
        Without parameters: the license object.
        With -showGlobal: the global usage statistics.
        With -CustomerStatistics: an array of per-customer usage statistics.
        With -Customer: the usage statistics of the specified customer.
 
.EXAMPLE
    PS C:\> Get-SMALicense
    Emits the appliance license information.
 
.EXAMPLE
    PS C:\> Get-SMALicense -CustomerStatistics
    Emits the usage statistics per appliance-customer.
 
.EXAMPLE
    PS C:\> Get-SMALicense -showGlobal
    Emits the usage statistics of the appliance globally.
 
.EXAMPLE
    PS C:\> Get-SMALicense -Customer "Contoso"
    Emits the usage statistics for a particular customer.
 
.LINK
    Get-SMAStatistics
 
.LINK
    Get-SMALftStatistics
 
.NOTES
    Available parameters may vary depending on appliance and API version.
    Alias: Get-SMALic
#>

function Get-SMALicense {
    [CmdletBinding()]
    param (
        #api Params
        [Parameter(
              Mandatory = $false,
            HelpMessage = 'Returns license information for a specific customer (tenant).'
            )]
        [string]$Customer,

        [Parameter(
              Mandatory = $false,
            HelpMessage = 'Returns license information for a specific customer (tenant).'
            )]
        [Alias("stats")]
        [switch]$CustomerStatistics,

        [Parameter(
              Mandatory = $false,
            HelpMessage = 'Returns global license statistics.'
            )]
        [Alias("global")]
        [switch]$showGlobal,
        #endregion API parameters
    
        #region SMA Config Params
        [Parameter(
              Mandatory = $false,
            HelpMessage = 'API version to use.'
            )]
        [string]$SMAVersion = $Script:activeCfg.SMAPIVersion,

        [Parameter(
              Mandatory = $false,
            HelpMessage = 'Hostname or IP of the SEPPmail appliance.'
            )]
        [string]$SMAHost = $Script:activeCfg.SMAHost,

        [Parameter(
              Mandatory = $false,
            HelpMessage = 'Port for the SEPPmail API.'
            )]
        [int]$SMAPort = $Script:activeCfg.SMAPort,

        [Parameter(Mandatory = $false)]
        [System.Management.Automation.PSCredential]$SMACred = $Script:activeCfg.SMACred,

        [Parameter(Mandatory = $false)]
        [switch]$SMASkipCertCheck = $Script:activeCfg.SMAskipCertCheck
        #endregion
    )

    begin {
        if (! (verifyVars -VarList $Script:requiredVarList)) {
            Throw($missingVarsMessage);
        }
        Write-Verbose "Creating URL path for license statistics"
        $uriPath = "statistics/license"

        $smaParams = @{
            Host    = $SMAHost
            Port    = $SMAPort
            Version = $SMAVersion
        }
    }
    process {
        Write-Verbose "Building full request uri"
        if ($customer) {$CustomerStatistics = $true}
        $boundParam = @{
            customerStatistics = $customerStatistics
        }
        if ($Customer) { $boundParam.customer = $Customer }

        $uri = New-SMAQueryString -uriPath $uriPath -qParam $boundParam @smaParams
        Write-Verbose "Crafting InvokeParam for Invoke-SMARestMethod"
        $invokeParam = @{
            Uri         = $uri
            Method      = 'GET'
            Cred        = $SMACred
            SkipCertCheck = $SMASkipCertCheck
        }
        Write-Verbose "Replace wrong '%40' value with '@'"
        $invokeParam.Uri = ($invokeParam.Uri).Replace('%40','@')
        Write-Verbose "Call Invoke-SMARestMethod $($invokeParam.Uri)"
        $licenseInfo = Invoke-SMARestMethod @invokeParam
        if ($licenseInfo) {
            
            # Keine Parameter gesetzt ==> OK
            if ((!($customer)) -and (!($customerStatistics)) -and (!($showGlobal))) {
                Write-Output $licenseInfo.License
            }
            
            # Global statistics
            if ((!($customer)) -and (!($customerStatistics)) -and ($showGlobal)) {
                Write-Output $licenseInfo.statistics.global
            }
            # Customerusage
            if ((!($customer)) -and ($customerStatistics)) {
                $outPutObj = $licenseInfo.statistics.customers.PSObject.Properties | ForEach-Object {
                    $obj = [PSCustomObject]@{
                        customerName = $_.Name
                    }
                    # Alle Properties aus dem Originalobjekt hinzufügen
                    $_.Value.PSObject.Properties | ForEach-Object {
                        $obj | Add-Member -MemberType NoteProperty -Name $_.Name -Value $_.Value
                    }
                    $obj
                }
                Write-Output $outPutObj
            }
            # specific customer
            if (($customer) -and (!($showGlobal))) {
                Write-Output $licenseInfo.statistics.customers.$customer
            }
        } else {
            Write-Information 'No license information found, nothing to return'
        }

    } 
    end {
        # Cleanup or final actions can be added here if needed
    }
}

<#
.SYNOPSIS
    Gets LFT (Large File Transfer) statistics from your SEPPmail appliance.
 
.DESCRIPTION
    Queries the SEPPmail API endpoints under /statistics/lft and returns Large
    File Transfer usage statistics. You must select exactly one scope via the
    mutually exclusive switches -CustomerStats, -UserStats, -DomainStats or
    -GroupStats. Within each scope you can optionally narrow down to a single
    entity and define a date or date range.
 
.PARAMETER CustomerStats
    Selects the customer scope (parameter set 'Customer'). Returns LFT
    statistics for all customers or, with -customer, a single customer.
 
.PARAMETER UserStats
    Selects the user scope (parameter set 'User'). Returns LFT statistics for
    all users or, with -user, a single user.
 
.PARAMETER DomainStats
    Selects the domain scope (parameter set 'Domain'). Returns LFT statistics
    for all domains or, with -domain, a single domain.
 
.PARAMETER GroupStats
    Selects the group scope (parameter set 'Group'). Returns LFT statistics for
    all groups or, with -group, a single group.
 
.PARAMETER customer
    Specific customer name. Omit to return statistics for all customers.
    ('Customer' parameter set.)
 
.PARAMETER user
    Specific user e-mail. Omit to return statistics for all users.
    ('User' parameter set.)
 
.PARAMETER domain
    Specific domain name. Omit to return statistics for all domains.
    ('Domain' parameter set.)
 
.PARAMETER group
    Specific group name. Omit to return statistics for all groups.
    ('Group' parameter set.)
 
.PARAMETER date
    A specific date for the statistics (format: YYYY-MM-DD).
 
.PARAMETER startdate
    Start date of a statistics range (format: YYYY-MM-DD).
 
.PARAMETER enddate
    End date of a statistics range (format: YYYY-MM-DD).
 
.PARAMETER split
    Splits the statistics by a sub-dimension. Valid values depend on the scope:
    - Customer: 'domain', 'group', 'user'
    - Domain: 'group', 'user'
    - Group: 'user'
    - User: not allowed
 
.PARAMETER SMAHost
    SEPPmail API hostname. Defaults to the configured value.
 
.PARAMETER SMAPort
    SEPPmail API port. Defaults to the configured value.
 
.PARAMETER SMAVersion
    SEPPmail API version. Defaults to the configured value.
 
.PARAMETER SMACred
    API credentials (PSCredential). Defaults to the configured value.
 
.PARAMETER SMASkipCertCheck
    Skip SSL certificate validation. Use only in test environments.
 
.OUTPUTS
    System.Management.Automation.PSCustomObject
        Returns the LFT statistics (with umlaut/unicode conversion applied).
 
.NOTES
    - Requires an active SEPPmail API session (New-SMAConfiguration).
    - Exactly one of -CustomerStats/-UserStats/-DomainStats/-GroupStats must be
      specified.
    - Alias: Get-SMALftStat
 
.LINK
    Get-SMAStatistics
 
.LINK
    Get-SMALicense
 
.EXAMPLE
    PS C:\> Get-SMALftStatistics -CustomerStats
    Returns LFT statistics for all customers.
 
.EXAMPLE
    PS C:\> Get-SMALftStatistics -DomainStats -domain 'contoso.com' -split user
    Returns LFT statistics for 'contoso.com', split by user.
 
.EXAMPLE
    PS C:\> Get-SMALftStatistics -UserStats -user 'alice@contoso.com' -startdate 2026-01-01 -enddate 2026-01-31
    Returns LFT statistics for a single user over a date range.
#>

function Get-SMALftStatistics
{
    [CmdletBinding()]
    param (
        #region API parameters

        # Parameter set switches - user MUST choose one
        [Parameter(
            Mandatory = $true,
            ParameterSetName = 'Customer',
            HelpMessage = 'Get customer statistics (all or specific customer)'
        )]
        [switch]$CustomerStats,

        [Parameter(
            Mandatory = $true,
            ParameterSetName = 'User',
            HelpMessage = 'Get user statistics (all or specific user)'
        )]
        [switch]$UserStats,

        [Parameter(
            Mandatory = $true,
            ParameterSetName = 'Domain',
            HelpMessage = 'Get domain statistics (all or specific domain)'
        )]
        [switch]$DomainStats,

        [Parameter(
            Mandatory = $true,
            ParameterSetName = 'Group',
            HelpMessage = 'Get group statistics (all or specific group)'
        )]
        [switch]$GroupStats,

        # Optional detail parameters for each set
        [Parameter(
            Mandatory = $false,
            ParameterSetName = 'Customer',
            ValueFromPipelineByPropertyName = $true,
            HelpMessage = 'Specific customer name (optional - omit for all customers)'
        )]
        [string]$customer,

        [Parameter(
            Mandatory = $false,
            ParameterSetName = 'User',
            ValueFromPipelineByPropertyName = $true,
            HelpMessage = 'Specific user email (optional - omit for all users)'
        )]
        [string]$user,

        [Parameter(
            Mandatory = $false,
            ParameterSetName = 'Domain',
            ValueFromPipelineByPropertyName = $true,
            HelpMessage = 'Specific domain name (optional - omit for all domains)'
        )]
        [string]$domain,

        [Parameter(
            Mandatory = $false,
            ParameterSetName = 'Group',
            ValueFromPipelineByPropertyName = $true,
            HelpMessage = 'Specific group name (optional - omit for all groups)'
        )]
        [string]$group,

        # Common parameters for all parameter sets
        [Parameter(
            Mandatory = $false,
            ValueFromPipelineByPropertyName = $true,
            HelpMessage = 'Specific date for statistics (format: YYYY-MM-DD)'
        )]
        [datetime]$date,

        [Parameter(
            Mandatory = $false,
            ValueFromPipelineByPropertyName = $true,
            HelpMessage = 'Start date for statistics range (format: YYYY-MM-DD)'
        )]
        [datetime]$startdate,

        [Parameter(
            Mandatory = $false,
            ValueFromPipelineByPropertyName = $true,
            HelpMessage = 'End date for statistics range (format: YYYY-MM-DD)'
        )]
        [datetime]$enddate,

        # Split parameter - validation depends on parameter set (validated in begin block)
        [Parameter(
            Mandatory = $false,
            ParameterSetName = 'Customer',
            ValueFromPipelineByPropertyName = $true,
            HelpMessage = 'Split statistics by domain, group or user (depending on context)'
        )]
        [Parameter(
            Mandatory = $false,
            ParameterSetName = 'Domain',
            ValueFromPipelineByPropertyName = $true,
            HelpMessage = 'Split statistics by group or user'
        )]
        [Parameter(
            Mandatory = $false,
            ParameterSetName = 'Group',
            ValueFromPipelineByPropertyName = $true,
            HelpMessage = 'Split statistics by user'
        )]
        [string]$split,

        #endregion API parameters

        #region Config parameters block
        [Parameter(Mandatory = $false)]
        [String]$SMAHost = $Script:activeCfg.SMAHost,

        [Parameter(Mandatory = $false)]
        [int]$SMAPort = $Script:activeCfg.SMAPort,

        [Parameter(Mandatory = $false)]
        [String]$SMAVersion = $Script:activeCfg.SMAPIVersion,

        [Parameter(
            Mandatory=$false
            )]
        [System.Management.Automation.PSCredential]$SMACred=$Script:activeCfg.SMACred,

        [Parameter(
            Mandatory=$false
            )]
        [switch]$SMASkipCertCheck=$Script:activeCfg.SMAskipCertCheck
        #endregion
    )

    begin {
        if (! (verifyVars -VarList $Script:requiredVarList))
        {
            Throw($missingVarsMessage);
        }
        
        $smaParams = @{
            Host    = $SMAHost
            Port    = $SMAPort
            Version = $SMAVersion
        } # end smaParams

        Write-Verbose "Creating URL path"
        $basicUriPath = "{0}/{1}" -f 'statistics','lft'
    
    }
    process {
        Write-Verbose "Convert Dates to correct formats"
        if ($date) {
            [string]$dateString = "{0:s}" -f (Get-Date $date)
        }
        if ($startDate) {
            [string]$startDateString = "{0:s}" -f (Get-Date $startDate)
        }
        if ($endDate) {
            [string]$endDateString = "{0:s}" -f (Get-Date $endDate)
        }
        
        Write-verbose "Validate $split parameter based on parameter set"
        if ($split) {
            $validSplitValues = switch ($PSCmdlet.ParameterSetName) {
                'Customer' { @('domain', 'group', 'user') }
                'Domain'   { @('group', 'user') }
                'Group'    { @('user') }
                'User'     { @() }  # No split allowed for User
            }
            
            if ($validSplitValues.Count -eq 0) {
                throw "The -split parameter is not valid for parameter set '$($PSCmdlet.ParameterSetName)'"
            }
            
            if ($split -notin $validSplitValues) {
                throw "Invalid value '$split' for -split parameter in parameter set '$($PSCmdlet.ParameterSetName)'. Valid values are: $($validSplitValues -join ', ')"
            }
        }
                
        Write-Verbose "Building URI path based on parameter set"
        $boundParam = @{}
        if ($dateString) {$boundParam.date = $dateString}
        if ($startdateString) {$boundParam.startdate = $startdateString}
        if ($enddateString) {$boundParam.enddate = $enddateString}
        if ($split) {$boundParam.split = $split}

        # Switch statement to build the correct URI path for each parameter set
        switch ($PSCmdlet.ParameterSetName) {
            'Customer' {
                if ($customer) {
                    $uriPath = "{0}/{1}" -f $basicUriPath, "customer/$customer"
                    Write-Verbose "Using customer-specific path: $uriPath"
                } else {
                    $uriPath = "{0}/{1}" -f $basicUriPath, 'customer'
                    Write-Verbose "Using general customer path: $uriPath"
                }
            }
            'User' {
                if ($user) {
                    $uriPath = "{0}/{1}" -f $basicUriPath, "user/$user"
                    Write-Verbose "Using user-specific path: $uriPath"
                } else {
                    $uriPath = "{0}/{1}" -f $basicUriPath, 'user'
                    Write-Verbose "Using general user path: $uriPath"
                }
            }
            'Domain' {
                if ($domain) {
                    $uriPath = "{0}/{1}" -f $basicUriPath, "domain/$domain"
                    Write-Verbose "Using domain-specific path: $uriPath"
                } else {
                    $uriPath = "{0}/{1}" -f $basicUriPath, 'domain'
                    Write-Verbose "Using general domain path: $uriPath"
                }
            }
            'Group' {
                if ($group) {
                    $uriPath = "{0}/{1}" -f $basicUriPath, "group/$group"
                    Write-Verbose "Using group-specific path: $uriPath"
                } else {
                    $uriPath = "{0}/{1}" -f $basicUriPath, 'group'
                    Write-Verbose "Using general group path: $uriPath"
                }
            }
        }
        
        Write-Verbose "Building full request uri"
        $uri = $null
        $uri = New-SMAQueryString -uriPath $uriPath -qParam $boundParam @smaParams
        
        Write-verbose "Crafting InvokeParam for Invoke-SMARestMethod"
        $invokeParam = @{
            Uri         = $uri 
            Method      = 'GET'
            Cred        =  $SMACred
            SkipCertCheck = $SMASkipCertCheck
        }

        Write-Verbose "Replace wrong '%40' value with '@'"
        $invokeparam.Uri = ($invokeParam.Uri).Replace('%40','@')
        Write-Verbose "Call Invoke-SMARestMethod $uri" 
        $lftStatsInfoRaw = Invoke-SMARestMethod @invokeParam
        Write-Verbose 'Converting Umlauts from ISO-8859-1'
        
        
        # Userobject
        if ($lftStatsInfoRaw) {
            $lftStatsinfo = ConvertFrom-SMAPIFormat -inputObject $lftStatsInfoRaw
            return $lftStatsinfo
        }
        else {
            Write-Information 'No LFT statistics information found, nothing to return'
        }
    }
    end {
    }
}

New-Alias -Name Get-SMAStat -Value Get-SMAStatistics
New-Alias -Name Get-SMALic -Value Get-SMALicense
New-Alias -Name Get-SMALftStat -Value Get-SMALftStatistics

# SIG # Begin signature block
# MIIVyAYJKoZIhvcNAQcCoIIVuTCCFbUCAQExDzANBglghkgBZQMEAgEFADB5Bgor
# BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG
# KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCA3bpJwWY8GKwYN
# 3kEHS6EL+t9oyefr5jYDKAB7hrdZ8KCCEgQwggVvMIIEV6ADAgECAhBI/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
# AQQBgjcCARUwLwYJKoZIhvcNAQkEMSIEIM6ZNQRGdrQa9/tCPorVYdLRGm/jW6HR
# xq2hNyoAJcFjMA0GCSqGSIb3DQEBAQUABIICAE9xddS0lTUxzKS/eX+QJ00h0A7K
# nVVFoocuJMxKBx/es7JPEEVRg1w7NHiuUPt5rkemDcGApopzQuAU+jRj8zvamMsj
# aNTGY+kSdB01/20bjeauSSiMo8HbvyQJYBHm5kYuBRPEK11/bmGmY+3K1UJ4fsXV
# tyi4l9+zPZFm+mvxtPCn49NSSdUUigUo/sYXncCsb5sh/fEu8+QHBNtd7rDdNkuC
# +EpZQcT0JJPoLvz6K0zZElhpvVDBqj86HBRCDlpQHL9/FBf8PxCohNph3YBmyABW
# 3U2+Nj7TZxo9T/EAGRVWy7iwh4Ntr4WvCSyBEWIQDD5A73iqvpReXLZFowTvS9kf
# ZxKinL79bXCWl2a5g5ypv+S3MavJsAv6RyPfYvtx+9xE7rvBkfTj8KHTZmSrjnVE
# xSj4G7CYIRtkmErMQNhE64OYqiTJjLHeabR3dALhVIo5UPX7DGKIx1h2VzSQGiE0
# FBtyEjGG6cvWC57brY3u3LlcHu37uV0ou9mEiZUc8IblwFk5jo78gyi/YqtyTsw7
# wdhk0+uIyFGwmgL6HZZj+XxTO/qNdDMlhWPe9EldT+tgarT/1+N3HLLN1WoH8zK/
# tK+rgncalI4ffKcEv369NSHnIfw29LJIHKxp28HqxmcZSedkjaduBXlIuSV5aclM
# BksUw3kF3AcHh5II
# SIG # End signature block