Transferetto.psm1

function Remove-EmptyValue {
    [alias('Remove-EmptyValues')]
    [CmdletBinding()]
    param([alias('Splat', 'IDictionary')][Parameter(Mandatory)][System.Collections.IDictionary] $Hashtable,
        [string[]] $ExcludeParameter,
        [switch] $Recursive,
        [int] $Rerun)
    foreach ($Key in [string[]] $Hashtable.Keys) { if ($Key -notin $ExcludeParameter) { if ($Recursive) { if ($Hashtable[$Key] -is [System.Collections.IDictionary]) { if ($Hashtable[$Key].Count -eq 0) { $Hashtable.Remove($Key) } else { Remove-EmptyValue -Hashtable $Hashtable[$Key] -Recursive:$Recursive } } else { if ($null -eq $Hashtable[$Key] -or ($Hashtable[$Key] -is [string] -and $Hashtable[$Key] -eq '') -or ($Hashtable[$Key] -is [System.Collections.IList] -and $Hashtable[$Key].Count -eq 0)) { $Hashtable.Remove($Key) } } } else { if ($null -eq $Hashtable[$Key] -or ($Hashtable[$Key] -is [string] -and $Hashtable[$Key] -eq '') -or ($Hashtable[$Key] -is [System.Collections.IList] -and $Hashtable[$Key].Count -eq 0)) { $Hashtable.Remove($Key) } } } }
    if ($Rerun) { for ($i = 0; $i -lt $Rerun; $i++) { Remove-EmptyValue -Hashtable $Hashtable -Recursive:$Recursive } }
}
function Add-PrivateFTPFile {
    [cmdletBinding()]
    param([Parameter(Mandatory)][FluentFTP.FtpClient] $Client,
        [string] $RemotePath,
        [string] $LocalPath,
        [FluentFTP.FtpRemoteExists] $RemoteExists = [FluentFTP.FtpRemoteExists]::Skip,
        [FluentFTP.FtpVerify] $VerifyOptions = [FluentFTP.FtpVerify]::None,
        [switch] $CreateRemoteDirectory)
    try {
        $Message = $Client.UploadFile($LocalPath, $RemotePath, $RemoteExists, $CreateRemoteDirectory.IsPresent, $VerifyOptions)
        if ($Message -eq 'success') { $State = $true } else { $State = $false }
        $Status = [PSCustomObject] @{Action = 'UploadFile'
            LocalPath                       = $LocalPath
            RemotePath                      = $RemotePath
            Status                          = $State
            Message                         = $Message
        }
    } catch {
        $Status = [PSCustomObject] @{Action = 'UploadFile'
            LocalPath                       = $LocalPath
            RemotePath                      = $RemotePath
            Status                          = $false
            Message                         = "Error: $($_.Exception.Message)"
        }
        if ($PSBoundParameters.ErrorAction -eq 'Stop') {
            Write-Error $_
            return
        } else { Write-Warning "Add-PrivateFTPFile - Error: $($_.Exception.Message)" }
    }
    $Status
}
function Add-PrivateFTPFiles {
    [cmdletBinding()]
    param([Parameter(Mandatory)][FluentFTP.FtpClient] $Client,
        [string] $RemotePath,
        [string[]] $LocalPath,
        [System.IO.FileInfo[]] $LocalFile,
        [FluentFTP.FtpRemoteExists] $RemoteExists = [FluentFTP.FtpRemoteExists]::Skip,
        [FluentFTP.FtpVerify] $VerifyOptions = [FluentFTP.FtpVerify]::None,
        [FluentFTP.FtpError] $ErrorHandling = [FluentFTP.FtpError]::None,
        [switch] $CreateRemoteDirectory)
    try {
        if ($LocalFile) { $Message = $Client.UploadFiles([System.IO.FileInfo[]] $LocalFile, $RemotePath, $RemoteExists, $CreateRemoteDirectory.IsPresent, $VerifyOptions, $ErrorHandling) } else { $Message = $Client.UploadFiles([string[]] $LocalPath, $RemotePath, $RemoteExists, $CreateRemoteDirectory.IsPresent, $VerifyOptions, $ErrorHandling) }
        if ($Message -gt 1) { $State = $true } else { $State = $false }
        $Status = [PSCustomObject] @{Action = 'UploadFile'
            LocalPath                       = $LocalPath
            RemotePath                      = $RemotePath
            Status                          = $State
            Message                         = $Message
        }
    } catch {
        $Status = [PSCustomObject] @{Action = 'UploadFile'
            LocalPath                       = $LocalPath
            RemotePath                      = $RemotePath
            Status                          = $false
            Message                         = "Error: $($_.Exception.Message)"
        }
        if ($PSBoundParameters.ErrorAction -eq 'Stop') {
            Write-Error $_
            return
        } else { Write-Warning "Add-PrivateFTPFiles - Error: $($_.Exception.Message)" }
    }
    $Status
}
function Get-PrivateFTPFile {
    [cmdletBinding()]
    param([Parameter(Mandatory)][FluentFTP.FtpClient] $Client,
        [string] $LocalPath,
        [FluentFTP.FtpListItem] $RemoteFile,
        [string] $RemotePath,
        [FluentFTP.FtpLocalExists] $LocalExists,
        [FluentFTP.FtpVerify[]] $VerifyOptions,
        [FluentFTP.FtpError] $FtpError)
    if ($RemoteFile) {
        if ($RemoteFile.Type -eq 'File') { $FileToDownload = $RemoteFile.FullName } else {
            if (-not $Suppress) {
                return [PSCustomObject] @{Action = 'DownloadFile'
                    Status                       = $false
                    LocalPath                    = $LocalPath
                    RemotePath                   = $RemoteFile.FullName
                    Message                      = "Get-FTPFile - Given path $($RemoteFile.FullName) is $($RemoteFile.Type). Skipping."
                }
            } else {
                Write-Warning "Get-FTPFile - Given path $($RemoteFile.FullName) is a directory. Skipping."
                return
            }
        }
    } else { $FileToDownload = $RemotePath }
    try {
        $Message = $Client.DownloadFile($LocalPath, $FileToDownload, $LocalExists, $VerifyOptions)
        if ($Message -eq 'success') { $State = $true } else { $State = $false }
        $Status = [PSCustomObject] @{Action = 'DownloadFile'
            Status                          = $State
            LocalPath                       = $LocalPath
            RemotePath                      = $FileToDownload
            Message                         = $Message
        }
    } catch {
        $Status = [PSCustomObject] @{Action = 'DownloadFile'
            Status                          = $false
            LocalPath                       = $LocalPath
            RemotePath                      = $FileToDownload
            Message                         = "Error: $($_.Exception.Message)"
        }
        if ($PSBoundParameters.ErrorAction -eq 'Stop') {
            Write-Error $_
            return
        } else { Write-Warning "Get-FTPFile - Error: $($_.Exception.Message)" }
    }
    $Status
}
function Get-PrivateFTPFiles {
    [cmdletBinding()]
    param([Parameter(Mandatory)][FluentFTP.FtpClient] $Client,
        [string] $LocalPath,
        [FluentFTP.FtpListItem[]] $RemoteFile,
        [string[]] $RemotePath,
        [FluentFTP.FtpLocalExists] $LocalExists,
        [FluentFTP.FtpVerify[]] $VerifyOptions,
        [FluentFTP.FtpError] $FtpError)
    if ($RemoteFile) {
        $FileToDownload = foreach ($File in $RemoteFile) {
            if ($File.Type -eq 'File') { $File.FullName } else {
                if (-not $Suppress) {
                    [PSCustomObject] @{Action = 'DownloadFile'
                        Status                = $false
                        LocalPath             = $LocalPath
                        RemotePath            = $File.FullName
                        Message               = "Get-FTPFile - Given path $($RemoteFile.FullName) is $($RemoteFile.Type). Skipping."
                    }
                } else { Write-Warning "Get-FTPFile - Given path $($RemoteFile.FullName) is a directory. Skipping." }
            }
        }
    } else { $FileToDownload = $RemotePath }
    try {
        $Message = $Client.DownloadFiles($LocalPath, ([string[]] $FileToDownload), $LocalExists, $VerifyOptions, $FtpError)
        if ($Message -gt 0) { $State = $true } else { $State = $false }
        $Status = [PSCustomObject] @{Action = 'DownloadFile'
            Status                          = $State
            LocalPath                       = $LocalPath
            RemotePath                      = $FileToDownload
            Message                         = $Message
        }
    } catch {
        $Status = [PSCustomObject] @{Action = 'DownloadFile'
            Status                          = $false
            LocalPath                       = $LocalPath
            RemotePath                      = $FileToDownload
            Message                         = "Error: $($_.Exception.Message)"
        }
        if ($PSBoundParameters.ErrorAction -eq 'Stop') {
            Write-Error $_
            return
        } else { Write-Warning "Get-FTPFile - Error: $($_.Exception.Message)" }
    }
    $Status
}
function Add-FTPFile {
    [cmdletBinding()]
    param([Parameter(Mandatory)][FluentFTP.FtpClient] $Client,
        [string] $RemotePath,
        [System.IO.FileInfo[]] $LocalFile,
        [string[]] $LocalPath,
        [FluentFTP.FtpRemoteExists] $RemoteExists = [FluentFTP.FtpRemoteExists]::Skip,
        [FluentFTP.FtpVerify] $VerifyOptions = [FluentFTP.FtpVerify]::None,
        [FluentFTP.FtpError] $ErrorHandling = [FluentFTP.FtpError]::None,
        [switch] $CreateRemoteDirectory)
    if ($Client -and $Client.IsConnected) {
        if ($LocalPath.Count -gt 1 -or $LocalFile.Count -gt 1) {
            $Splat = @{Client         = $Client
                RemoteExists          = $RemoteExists
                VerifyOptions         = $VerifyOptions
                LocalPath             = $LocalPath
                LocalFile             = $LocalFile
                RemotePath            = $RemotePath
                CreateRemoteDirectory = $CreateRemoteDirectory.IsPresent
                ErrorHandling         = $ErrorHandling
            }
            Remove-EmptyValue -Hashtable $Splat
            $Status = Add-PrivateFTPFiles @Splat
            $Status
        } else {
            foreach ($Path in $LocalPath) {
                $Splat = @{Client         = $Client
                    RemoteExists          = $RemoteExists
                    VerifyOptions         = $VerifyOptions
                    LocalPath             = $Path
                    RemotePath            = $RemotePath
                    CreateRemoteDirectory = $CreateRemoteDirectory.IsPresent
                }
                $Status = Add-PrivateFTPFile @Splat
                $Status
            }
        }
    }
}
function Add-SFTPFile {
    [cmdletBinding()]
    param([Parameter(Mandatory)][Renci.SshNet.SftpClient] $SftpClient,
        [string] $RemotePath,
        [string] $LocalPath,
        [switch] $AllowOverride)
    if ($SftpClient -and $SftpClient.IsConnected) {
        if (Test-Path -LiteralPath $LocalPath) {
            try {
                $FileStream = [System.IO.FileStream]::new($LocalPath, [System.IO.FileMode]::OpenOrCreate)
                $SftpClient.UploadFile($FileStream, $RemotePath, $AllowOverride)
                $Status = [PSCustomObject] @{Action = 'UploadFile'
                    Status                          = $true
                    LocalPath                       = $LocalPath
                    RemotePath                      = $RemotePath
                    Message                         = ""
                }
            } catch {
                $Status = [PSCustomObject] @{Action = 'UploadFile'
                    Status                          = $false
                    LocalPath                       = $LocalPath
                    RemotePath                      = $RemotePath
                    Message                         = "Error: $($_.Exception.Message)"
                }
                if ($PSBoundParameters.ErrorAction -eq 'Stop') {
                    Write-Error $_
                    return
                } else { Write-Warning "Add-SFTPFile - Error: $($_.Exception.Message)" }
            } finally { $FileStream.Close() }
        } else {
            Write-Warning "Add-SFTPFile - File $LocalPath doesn't exists."
            $Status = [PSCustomObject] @{Action = 'UploadFile'
                Status                          = $false
                LocalPath                       = $LocalPath
                RemotePath                      = $RemotePath
                Message                         = "LocalPath doesn't exists $LocalPath"
            }
        }
        $Status
    }
}
function Connect-FTP {
    [cmdletBinding(DefaultParameterSetName = 'Password')]
    param([Parameter(ParameterSetName = 'FtpProfile')]
        [FluentFTP.FtpProfile] $FtpProfile,
        [Parameter(ParameterSetName = 'ClearText')]
        [Parameter(ParameterSetName = 'Password')]
        [string] $Server,
        [Parameter(ParameterSetName = 'ClearText')]
        [string] $Username,
        [Parameter(ParameterSetName = 'ClearText')]
        [string] $Password,
        [Parameter(ParameterSetName = 'Password')]
        [pscredential] $Credential,
        [Parameter(ParameterSetName = 'ClearText')]
        [Parameter(ParameterSetName = 'Password')]
        [FluentFTP.FtpEncryptionMode[]] $EncryptionMode,
        [Parameter(ParameterSetName = 'ClearText')]
        [Parameter(ParameterSetName = 'Password')]
        [FluentFTP.FtpDataConnectionType] $DataConnectionType,
        [Parameter(ParameterSetName = 'ClearText')]
        [Parameter(ParameterSetName = 'Password')]
        [FluentFTP.FtpsBuffering] $SslBuffering,
        [Parameter(ParameterSetName = 'ClearText')]
        [Parameter(ParameterSetName = 'Password')]
        [switch] $DisableDataConnectionEncryption,
        [Parameter(ParameterSetName = 'ClearText')]
        [Parameter(ParameterSetName = 'Password')]
        [switch] $DisableValidateCertificateRevocation,
        [Parameter(ParameterSetName = 'ClearText')]
        [Parameter(ParameterSetName = 'Password')]
        [switch] $ValidateAnyCertificate,
        [Parameter(ParameterSetName = 'ClearText')]
        [Parameter(ParameterSetName = 'Password')]
        [int] $Port,
        [Parameter(ParameterSetName = 'ClearText')]
        [Parameter(ParameterSetName = 'Password')]
        [switch] $SendHost,
        [Parameter(ParameterSetName = 'ClearText')]
        [Parameter(ParameterSetName = 'Password')]
        [switch] $SocketKeepAlive,
        [Parameter(ParameterSetName = 'ClearText')]
        [Parameter(ParameterSetName = 'Password')]
        [switch] $AutoConnect)
    if ($FtpProfile) {
        $Client = [FluentFTP.FtpClient]::new()
        $Client.LoadProfile($FtpProfile)
    } else {
        $Client = [FluentFTP.FtpClient]::new($Server)
        if ($Username -and $Password) { $Client.Credentials = [System.Net.NetworkCredential]::new($Username, $Password) } elseif ($Credential) { $Client.Credentials = [System.Net.NetworkCredential]::new($Credential.Username, $Credential.Password) } else {}
    }
    if ($Port) { $Client.Port = $Port }
    if ($DataConnectionType) { $Client.DataConnectionType = $DataConnectionType }
    if ($DisableDataConnectionEncryption) { $Client.DataConnectionEncryption = $false }
    if ($EncryptionMode) { $Client.EncryptionMode = $EncryptionMode }
    if ($ValidateAnyCertificate) { $Client.ValidateAnyCertificate = $true }
    if ($DisableValidateCertificateRevocation) { $Client.ValidateCertificateRevocation = $false }
    if ($SendHost) { $Client.SendHost = $true }
    if ($SocketKeepAlive) { $Client.SocketKeepAlive = $true }
    if ($FtpsBuffering) { $Client.SslBuffering = $SslBuffering }
    try {
        if ($AutoConnect) {
            $TempFtpProfile = $Client.AutoConnect()
            if ($TempFtpProfile -and $Client.IsConnected) {
                Write-Verbose "Following options where used to autoconnect: "
                foreach ($Name in $TempFtpProfile.PSObject.Properties.Name) { Write-Verbose "[x] $Name -> $($TempFtpProfile.$Name)" }
            }
        } else { $Client.Connect() }
        $Client | Add-Member -Name 'Error' -Value $null -Force -MemberType NoteProperty
    } catch {
        $Client | Add-Member -Name 'Error' -Value $($_.Exception.Message) -Force -MemberType NoteProperty
        if ($PSBoundParameters.ErrorAction -eq 'Stop') {
            Write-Error $_
            return
        } else { Write-Warning "Connect-FTP - Error: $($_.Exception.Message)" }
    }
    $Client
}
function Connect-SFTP {
    [cmdletBinding(DefaultParameterSetName = 'Password')]
    param([Parameter(ParameterSetName = 'ClearText')]
        [Parameter(ParameterSetName = 'Password')]
        [string] $Server,
        [Parameter(ParameterSetName = 'ClearText')]
        [string] $Username,
        [Parameter(ParameterSetName = 'ClearText')]
        [string] $Password,
        [Parameter(ParameterSetName = 'Password')]
        [pscredential] $Credential,
        [Parameter(ParameterSetName = 'ClearText')]
        [Parameter(ParameterSetName = 'Password')]
        [int] $Port)
    if ($Username -and $Password) { $SftpClient = [Renci.SshNet.SftpClient]::new($Server, $Username, $Password) } elseif ($Credential) { $SftpClient = [Renci.SshNet.SftpClient]::new($Server, $Credential.Username, $Credential.GetNetworkCredential().Password) } else { throw 'Not implemented. Add certificate' }
    if ($Port) { $SftpClient.Port = $Port }
    try {
        $SftpClient.Connect()
        $SftpClient | Add-Member -Name 'Error' -Value $null -Force -MemberType NoteProperty
    } catch {
        $SftpClient | Add-Member -Name 'Error' -Value $($_.Exception.Message) -Force -MemberType NoteProperty
        if ($PSBoundParameters.ErrorAction -eq 'Stop') {
            Write-Error $_
            return
        } else { Write-Warning "Connect-SFTP - Error: $($_.Exception.Message)" }
    }
    $SftpClient
}
function Disconnect-FTP {
    [cmdletBinding()]
    param([FluentFTP.FtpClient] $Client)
    if ($Client -and $Client.IsConnected) {
        try { $Client.Disconnect() } catch {
            if ($PSBoundParameters.ErrorAction -eq 'Stop') {
                Write-Error $_
                return
            } else { Write-Warning "Disconnect-FTP - Error: $($_.Exception.Message)" }
        }
    }
}
function Disconnect-SFTP {
    [cmdletBinding()]
    param([Parameter(Mandatory)][Renci.SshNet.SftpClient] $SftpClient)
    if ($SftpClient -and $SftpClient.IsConnected) {
        try { $SftpClient.Disconnect() } catch {
            if ($PSBoundParameters.ErrorAction -eq 'Stop') {
                Write-Error $_
                return
            } else { Write-Warning "Disconnect-SFTP - Error: $($_.Exception.Message)" }
        }
    }
}
function Get-FTPFile {
    [cmdletBinding(DefaultParameterSetName = 'Text')]
    param([Parameter(ParameterSetName = 'Text')]
        [Parameter(ParameterSetName = 'Native')]
        [Parameter(Mandatory)][FluentFTP.FtpClient] $Client,
        [Parameter(ParameterSetName = 'Native')]
        [FluentFTP.FtpListItem[]] $RemoteFile,
        [Parameter(ParameterSetName = 'Text')]
        [Parameter(ParameterSetName = 'Native')]
        [string[]] $RemotePath,
        [Parameter(ParameterSetName = 'Text')]
        [Parameter(ParameterSetName = 'Native')]
        [string] $LocalPath,
        [Parameter(ParameterSetName = 'Text')]
        [Parameter(ParameterSetName = 'Native')]
        [FluentFTP.FtpLocalExists] $LocalExists = [FluentFTP.FtpLocalExists]::Skip,
        [Parameter(ParameterSetName = 'Text')]
        [Parameter(ParameterSetName = 'Native')]
        [FluentFTP.FtpVerify[]] $VerifyOptions = [FluentFTP.FtpVerify]::None,
        [Parameter(ParameterSetName = 'Text')]
        [Parameter(ParameterSetName = 'Native')]
        [FluentFTP.FtpError] $FtpError = [FluentFTP.FtpError]::Stop,
        [Parameter(ParameterSetName = 'Text')]
        [Parameter(ParameterSetName = 'Native')]
        [switch] $Suppress)
    if ($Client -and $Client.IsConnected) {
        $Path = Get-Item -LiteralPath $LocalPath -ErrorAction SilentlyContinue
        if ($Path -is [System.IO.DirectoryInfo]) { Get-PrivateFTPFiles -Client $Client -LocalPath $LocalPath -RemoteFile $RemoteFile -RemotePath $RemotePath -LocalExists $LocalExists -VerifyOptions $VerifyOptions -FtpError $FtpError } else {
            if ($RemoteFile.Count -gt 1 -or $RemotePath.Count -gt 1) {
                Write-Warning "Get-FTPFile - Multiple files detected, but $LocalPath is not a directory or directory doesn't exists. "
                if ($RemoteFile) { $FileToDownload = $RemoteFile.FullName } else { $FileToDownload = $RemotePath }
                $Status = [PSCustomObject] @{Action = 'DownloadFile'
                    Status                          = $false
                    LocalPath                       = $LocalPath
                    RemotePath                      = $FileToDownload
                    Message                         = "Not connected."
                }
            } else {
                $Splat = @{Client = $Client
                    LocalExists   = $LocalExists
                    VerifyOptions = $VerifyOptions
                    FtpError      = $FtpError
                    LocalPath     = $LocalPath
                }
                if ($RemoteFile) { $Splat.RemoteFile = $RemoteFile[0] } else { $Splat.RemotePath = $RemotePath[0] }
                Get-PrivateFTPFile @Splat
            }
        }
    } else {
        $Status = [PSCustomObject] @{Action = 'DownloadFile'
            Status                          = $false
            LocalPath                       = $LocalPath
            RemotePath                      = $FileToDownload
            Message                         = "Not connected."
        }
    }
    if (-not $Suppress) { $Status }
}
function Get-FTPList {
    [cmdletBinding()]
    param([alias('FtpPath')][string] $Path,
        [FluentFTP.FtpListOption] $Options,
        [Parameter(Mandatory)][FluentFTP.FtpClient] $Client)
    if ($Client -and $Client.IsConnected -and -not $Client.Error) {
        try { if ($Path -and $Options) { $Client.GetListing($Path, $Options) } elseif ($Path) { $Client.GetListing($Path) } else { $Client.GetListing() } } catch {
            if ($PSBoundParameters.ErrorAction -eq 'Stop') {
                Write-Error $_
                return
            } else { Write-Warning "Get-FTPList - Error: $($_.Exception.Message)" }
        }
    } else { Write-Warning "Get-FTPList - Skipped (IsConnected $($Client.IsConnected) / Error: $($Client.Error))" }
}
function Get-SFTPFile {
    [cmdletBinding()]
    param([Parameter(Mandatory)][Renci.SshNet.SftpClient] $SftpClient,
        [string] $RemotePath,
        [string] $LocalPath)
    if ($SftpClient -and $SftpClient.IsConnected) {
        try {
            $FileStream = [System.IO.FileStream]::new($LocalPath, [System.IO.FileMode]::OpenOrCreate)
            $SftpClient.DownloadFile($RemotePath, $FileStream)
            $Status = [PSCustomObject] @{Action = 'DownloadFile'
                Status                          = $true
                LocalPath                       = $LocalPath
                RemotePath                      = $RemotePath
                Message                         = ""
            }
        } catch {
            $Status = [PSCustomObject] @{Action = 'DownloadFile'
                Status                          = $false
                LocalPath                       = $LocalPath
                RemotePath                      = $RemotePath
                Message                         = "Error: $($_.Exception.Message)"
            }
            if ($PSBoundParameters.ErrorAction -eq 'Stop') {
                Write-Error $_
                return
            } else { Write-Warning "Get-SFTPFile - Error: $($_.Exception.Message)" }
        } finally {
            $FileStream.Close()
            if ($Status.Status -eq $false) { Remove-Item -LiteralPath $LocalPath }
        }
        $Status
    }
}
function Get-SFTPList {
    [cmdletBinding()]
    param([alias('FtpPath')][string] $Path,
        [Parameter(Mandatory)][Renci.SshNet.SftpClient] $SftpClient)
    if ($SftpClient -and $SftpClient.IsConnected -and -not $SftpClient.Error) {
        try { if ($Path) { $SftpClient.ListDirectory($Path) } else { $SftpClient.ListDirectory('') } } catch {
            if ($PSBoundParameters.ErrorAction -eq 'Stop') {
                Write-Error $_
                return
            } else { Write-Warning "Get-SFTPList - Error: $($_.Exception.Message)" }
        }
    } else { Write-Warning "Get-SFTPList - Skipped (IsConnected $($SftpClient.IsConnected) / Error: $($SftpClient.Error))" }
}
function Remove-FTPFile {
    [cmdletBinding()]
    param()
}
function Remove-SFTPFile {
    [cmdletBinding()]
    param([Parameter(Mandatory)][Renci.SshNet.SftpClient] $SftpClient,
        [string] $RemotePath,
        [switch] $Suppress)
    if ($SftpClient -and $SftpClient.IsConnected) {
        try {
            $SftpClient.DeleteFile($RemotePath)
            $Status = [PSCustomObject] @{Action = 'RemoveFile'
                Status                          = $true
                Message                         = ""
            }
        } catch {
            $Status = [PSCustomObject] @{Action = 'RemoveFile'
                Status                          = $false
                Message                         = "Error $($_.Exception.Message)"
            }
            if ($PSBoundParameters.ErrorAction -eq 'Stop') {
                Write-Error $_
                return
            } else { Write-Warning "Remove-SFTPFile - Error: $($_.Exception.Message)" }
        }
        if (-not $Suppress) { $Status }
    }
}
function Rename-FTPFile {
    [cmdletBinding()]
    param()
}
function Rename-SFTPFile {
    [cmdletBinding()]
    param([Parameter(Mandatory)][Renci.SshNet.SftpClient] $SftpClient,
        [string] $RemotePath,
        [switch] $Suppress)
    if ($SftpClient -and $SftpClient.IsConnected) {
        try {
            $SftpClient.RenameFile($RemotePath)
            $Status = [PSCustomObject] @{Action = 'RenameFile'
                LocalPath                       = ''
                RemotePath                      = $RemotePath
                Status                          = $true
                Message                         = ""
            }
        } catch {
            $Status = [PSCustomObject] @{Action = 'RenameFile'
                Status                          = $false
                LocalPath                       = ''
                RemotePath                      = $RemotePath
                Message                         = "Error $($_.Exception.Message)"
            }
            if ($PSBoundParameters.ErrorAction -eq 'Stop') {
                Write-Error $_
                return
            } else { Write-Warning "Rename-SFTPFile - Error: $($_.Exception.Message)" }
        }
        if (-not $Suppress) { $Status }
    }
}
function Request-FTPConfiguration {
    <#
    .SYNOPSIS
    Short description
 
    .DESCRIPTION
    Automatically discover working FTP connection settings and return those connection profiles. This method will try every possible connection type combination in a loop until it finds a working combination, and it will return the first found combination or all found combinations. The connection types are tried in this order of preference.
 
    Auto connection attempts to find working connection settings in this order of preference:
 
    Protocol Preference:
    1. None - Let the OS decide which TLS/SSL version to use
    2. Tls12 - TLS 1.2 (TLS 1.3 is not yet stable in .NET Framework)
    3. Tls11 - TLS 1.1
    4. Tls - TLS 1.0
    5. Ssl3 - SSL 3.0 (obsolete, need to use TLS instead)
    6. Ssl2 - SSL 2.0 (obsolete, need to use TLS instead)
    7. Default - Undefined/weird behaviour
 
    Data Connection Type Preference:
 
    1. PASV - We prefer passive as its the most reliable
    2. EPSV - Enhanced passive is not as well supported on servers
    3. PORT - PORT is an older connection type
    4. EPRT - Enhanced PORT is not as well supported on servers
    5. PASVEX
 
    Encoding Type Preference:
 
    1. UTF8 - We prefer Unicode encoding as there will be no issues with file and folder names
    2. ASCII - ASCII/ANSI is a fallback used for older servers
 
    .PARAMETER Server
    Server Name or IP Address to Connect
 
    .PARAMETER Username
    UserName for FTP Connection
 
    .PARAMETER Password
    Password for FTP Connection (cleartext)
 
    .PARAMETER Credential
    UserName and Password in form of Credentials
 
    .PARAMETER FirstOnly
    Returns first working profile
 
    .EXAMPLE
    # Login via UserName/Password
    $ProfileFtp1 = Request-FTPConfiguration -Server 'test.rebex.net' -Verbose -Username 'demo' -Password 'password'
    $ProfileFtp1 | Format-Table
 
    .EXAMPLE
    # Anonymous login
    $ProfileFtp2 = Request-FTPConfiguration -Server 'speedtest.tele2.net' -Verbose
    $ProfileFtp2 | Format-Table
 
    .NOTES
    General notes
    #>

    [cmdletBinding(DefaultParameterSetName = 'Password')]
    param([Parameter(ParameterSetName = 'ClearText')]
        [Parameter(ParameterSetName = 'Password')]
        [string] $Server,
        [Parameter(ParameterSetName = 'ClearText')]
        [string] $Username,
        [Parameter(ParameterSetName = 'ClearText')]
        [string] $Password,
        [Parameter(ParameterSetName = 'Password')]
        [pscredential] $Credential,
        [Parameter(ParameterSetName = 'ClearText')]
        [Parameter(ParameterSetName = 'Password')]
        [switch] $FirstOnly)
    $Client = [FluentFTP.FtpClient]::new($Server)
    if ($Username -and $Password) { $Client.Credentials = [System.Net.NetworkCredential]::new($Username, $Password) } elseif ($Credential) { $Client.Credentials = [System.Net.NetworkCredential]::new($Credential.Username, $Credential.Password) } else {}
    try { $Client.AutoDetect($FirstOnly.IsPresent) } catch {
        $Client | Add-Member -Name 'Error' -Value $($_.Exception.Message) -Force -MemberType NoteProperty
        if ($PSBoundParameters.ErrorAction -eq 'Stop') {
            Write-Error $_
            return
        } else { Write-Warning "Request-FTPConfiguration - Error: $($_.Exception.Message)" }
    }
}
function Set-FTPTracing {
    [cmdletBinding()]
    param([string] $LogPath,
        [switch] $Enable,
        [switch] $Disable,
        [switch] $ShowPassword,
        [switch] $ShowUsername,
        [switch] $HideIP,
        [switch] $HideFunctions,
        [switch] $DisplayConsole)
    if ($Enable) { [FluentFTP.FtpTrace]::EnableTracing = $true }
    if ($Disable) { [FluentFTP.FtpTrace]::EnableTracing = $false }
    if ($LogPath) { [FluentFTP.FtpTrace]::LogToFile = "log_file.txt" }
    [FluentFTP.FtpTrace]::LogFunctions = $true
    [FluentFTP.FtpTrace]::LogToConsole = $true
    [FluentFTP.FtpTrace]::LogUserName = $true
    [FluentFTP.FtpTrace]::LogPassword = $false
    [FluentFTP.FtpTrace]::LogIP = $true
}
function Test-FTPFile {
    [cmdletBinding()]
    param()
}
if ($PSEdition -eq 'Core') {
    Add-Type -Path $PSScriptRoot\Lib\Standard\FluentFTP.dll
    Add-Type -Path $PSScriptRoot\Lib\Standard\Renci.SshNet.dll
    Add-Type -Path $PSScriptRoot\Lib\Standard\SshNet.Security.Cryptography.dll
} else {
    Add-Type -Path $PSScriptRoot\Lib\Standard\FluentFTP.dll
    Add-Type -Path $PSScriptRoot\Lib\Standard\Renci.SshNet.dll
    Add-Type -Path $PSScriptRoot\Lib\Standard\SshNet.Security.Cryptography.dll
}
Export-ModuleMember -Function @('Add-FTPFile', 'Add-SFTPFile', 'Connect-FTP', 'Connect-SFTP', 'Disconnect-FTP', 'Disconnect-SFTP', 'Get-FTPFile', 'Get-FTPList', 'Get-SFTPFile', 'Get-SFTPList', 'Remove-FTPFile', 'Remove-SFTPFile', 'Rename-FTPFile', 'Rename-SFTPFile', 'Request-FTPConfiguration', 'Set-FTPTracing', 'Test-FTPFile') -Alias @()
# SIG # Begin signature block
# MIIdWQYJKoZIhvcNAQcCoIIdSjCCHUYCAQExCzAJBgUrDgMCGgUAMGkGCisGAQQB
# gjcCAQSgWzBZMDQGCisGAQQBgjcCAR4wJgIDAQAABBAfzDtgWUsITrck0sYpfvNR
# AgEAAgEAAgEAAgEAAgEAMCEwCQYFKw4DAhoFAAQUxeJbvYpRFm24YdmM1YSqdyM8
# c/egghhnMIIDtzCCAp+gAwIBAgIQDOfg5RfYRv6P5WD8G/AwOTANBgkqhkiG9w0B
# AQUFADBlMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYD
# VQQLExB3d3cuZGlnaWNlcnQuY29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVk
# IElEIFJvb3QgQ0EwHhcNMDYxMTEwMDAwMDAwWhcNMzExMTEwMDAwMDAwWjBlMQsw
# CQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cu
# ZGlnaWNlcnQuY29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJvb3Qg
# Q0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCtDhXO5EOAXLGH87dg
# +XESpa7cJpSIqvTO9SA5KFhgDPiA2qkVlTJhPLWxKISKityfCgyDF3qPkKyK53lT
# XDGEKvYPmDI2dsze3Tyoou9q+yHyUmHfnyDXH+Kx2f4YZNISW1/5WBg1vEfNoTb5
# a3/UsDg+wRvDjDPZ2C8Y/igPs6eD1sNuRMBhNZYW/lmci3Zt1/GiSw0r/wty2p5g
# 0I6QNcZ4VYcgoc/lbQrISXwxmDNsIumH0DJaoroTghHtORedmTpyoeb6pNnVFzF1
# roV9Iq4/AUaG9ih5yLHa5FcXxH4cDrC0kqZWs72yl+2qp/C3xag/lRbQ/6GW6whf
# GHdPAgMBAAGjYzBhMA4GA1UdDwEB/wQEAwIBhjAPBgNVHRMBAf8EBTADAQH/MB0G
# A1UdDgQWBBRF66Kv9JLLgjEtUYunpyGd823IDzAfBgNVHSMEGDAWgBRF66Kv9JLL
# gjEtUYunpyGd823IDzANBgkqhkiG9w0BAQUFAAOCAQEAog683+Lt8ONyc3pklL/3
# cmbYMuRCdWKuh+vy1dneVrOfzM4UKLkNl2BcEkxY5NM9g0lFWJc1aRqoR+pWxnmr
# EthngYTffwk8lOa4JiwgvT2zKIn3X/8i4peEH+ll74fg38FnSbNd67IJKusm7Xi+
# fT8r87cmNW1fiQG2SVufAQWbqz0lwcy2f8Lxb4bG+mRo64EtlOtCt/qMHt1i8b5Q
# Z7dsvfPxH2sMNgcWfzd8qVttevESRmCD1ycEvkvOl77DZypoEd+A5wwzZr8TDRRu
# 838fYxAe+o0bJW1sj6W3YQGx0qMmoRBxna3iw/nDmVG3KwcIzi7mULKn+gpFL6Lw
# 8jCCBP4wggPmoAMCAQICEA1CSuC+Ooj/YEAhzhQA8N0wDQYJKoZIhvcNAQELBQAw
# cjELMAkGA1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZMBcGA1UECxMQ
# d3d3LmRpZ2ljZXJ0LmNvbTExMC8GA1UEAxMoRGlnaUNlcnQgU0hBMiBBc3N1cmVk
# IElEIFRpbWVzdGFtcGluZyBDQTAeFw0yMTAxMDEwMDAwMDBaFw0zMTAxMDYwMDAw
# MDBaMEgxCzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5EaWdpQ2VydCwgSW5jLjEgMB4G
# A1UEAxMXRGlnaUNlcnQgVGltZXN0YW1wIDIwMjEwggEiMA0GCSqGSIb3DQEBAQUA
# A4IBDwAwggEKAoIBAQDC5mGEZ8WK9Q0IpEXKY2tR1zoRQr0KdXVNlLQMULUmEP4d
# yG+RawyW5xpcSO9E5b+bYc0VkWJauP9nC5xj/TZqgfop+N0rcIXeAhjzeG28ffnH
# bQk9vmp2h+mKvfiEXR52yeTGdnY6U9HR01o2j8aj4S8bOrdh1nPsTm0zinxdRS1L
# sVDmQTo3VobckyON91Al6GTm3dOPL1e1hyDrDo4s1SPa9E14RuMDgzEpSlwMMYpK
# jIjF9zBa+RSvFV9sQ0kJ/SYjU/aNY+gaq1uxHTDCm2mCtNv8VlS8H6GHq756Wwog
# L0sJyZWnjbL61mOLTqVyHO6fegFz+BnW/g1JhL0BAgMBAAGjggG4MIIBtDAOBgNV
# HQ8BAf8EBAMCB4AwDAYDVR0TAQH/BAIwADAWBgNVHSUBAf8EDDAKBggrBgEFBQcD
# CDBBBgNVHSAEOjA4MDYGCWCGSAGG/WwHATApMCcGCCsGAQUFBwIBFhtodHRwOi8v
# d3d3LmRpZ2ljZXJ0LmNvbS9DUFMwHwYDVR0jBBgwFoAU9LbhIB3+Ka7S5GGlsqIl
# ssgXNW4wHQYDVR0OBBYEFDZEho6kurBmvrwoLR1ENt3janq8MHEGA1UdHwRqMGgw
# MqAwoC6GLGh0dHA6Ly9jcmwzLmRpZ2ljZXJ0LmNvbS9zaGEyLWFzc3VyZWQtdHMu
# Y3JsMDKgMKAuhixodHRwOi8vY3JsNC5kaWdpY2VydC5jb20vc2hhMi1hc3N1cmVk
# LXRzLmNybDCBhQYIKwYBBQUHAQEEeTB3MCQGCCsGAQUFBzABhhhodHRwOi8vb2Nz
# cC5kaWdpY2VydC5jb20wTwYIKwYBBQUHMAKGQ2h0dHA6Ly9jYWNlcnRzLmRpZ2lj
# ZXJ0LmNvbS9EaWdpQ2VydFNIQTJBc3N1cmVkSURUaW1lc3RhbXBpbmdDQS5jcnQw
# DQYJKoZIhvcNAQELBQADggEBAEgc3LXpmiO85xrnIA6OZ0b9QnJRdAojR6OrktIl
# xHBZvhSg5SeBpU0UFRkHefDRBMOG2Tu9/kQCZk3taaQP9rhwz2Lo9VFKeHk2eie3
# 8+dSn5On7UOee+e03UEiifuHokYDTvz0/rdkd2NfI1Jpg4L6GlPtkMyNoRdzDfTz
# ZTlwS/Oc1np72gy8PTLQG8v1Yfx1CAB2vIEO+MDhXM/EEXLnG2RJ2CKadRVC9S0y
# OIHa9GCiurRS+1zgYSQlT7LfySmoc0NR2r1j1h9bm/cuG08THfdKDXF+l7f0P4Tr
# weOjSaH6zqe/Vs+6WXZhiV9+p7SOZ3j5NpjhyyjaW4emii8wggUwMIIEGKADAgEC
# AhAECRgbX9W7ZnVTQ7VvlVAIMA0GCSqGSIb3DQEBCwUAMGUxCzAJBgNVBAYTAlVT
# MRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5j
# b20xJDAiBgNVBAMTG0RpZ2lDZXJ0IEFzc3VyZWQgSUQgUm9vdCBDQTAeFw0xMzEw
# MjIxMjAwMDBaFw0yODEwMjIxMjAwMDBaMHIxCzAJBgNVBAYTAlVTMRUwEwYDVQQK
# EwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xMTAvBgNV
# BAMTKERpZ2lDZXJ0IFNIQTIgQXNzdXJlZCBJRCBDb2RlIFNpZ25pbmcgQ0EwggEi
# MA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQD407Mcfw4Rr2d3B9MLMUkZz9D7
# RZmxOttE9X/lqJ3bMtdx6nadBS63j/qSQ8Cl+YnUNxnXtqrwnIal2CWsDnkoOn7p
# 0WfTxvspJ8fTeyOU5JEjlpB3gvmhhCNmElQzUHSxKCa7JGnCwlLyFGeKiUXULaGj
# 6YgsIJWuHEqHCN8M9eJNYBi+qsSyrnAxZjNxPqxwoqvOf+l8y5Kh5TsxHM/q8grk
# V7tKtel05iv+bMt+dDk2DZDv5LVOpKnqagqrhPOsZ061xPeM0SAlI+sIZD5SlsHy
# DxL0xY4PwaLoLFH3c7y9hbFig3NBggfkOItqcyDQD2RzPJ6fpjOp/RnfJZPRAgMB
# AAGjggHNMIIByTASBgNVHRMBAf8ECDAGAQH/AgEAMA4GA1UdDwEB/wQEAwIBhjAT
# BgNVHSUEDDAKBggrBgEFBQcDAzB5BggrBgEFBQcBAQRtMGswJAYIKwYBBQUHMAGG
# GGh0dHA6Ly9vY3NwLmRpZ2ljZXJ0LmNvbTBDBggrBgEFBQcwAoY3aHR0cDovL2Nh
# Y2VydHMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0QXNzdXJlZElEUm9vdENBLmNydDCB
# gQYDVR0fBHoweDA6oDigNoY0aHR0cDovL2NybDQuZGlnaWNlcnQuY29tL0RpZ2lD
# ZXJ0QXNzdXJlZElEUm9vdENBLmNybDA6oDigNoY0aHR0cDovL2NybDMuZGlnaWNl
# cnQuY29tL0RpZ2lDZXJ0QXNzdXJlZElEUm9vdENBLmNybDBPBgNVHSAESDBGMDgG
# CmCGSAGG/WwAAgQwKjAoBggrBgEFBQcCARYcaHR0cHM6Ly93d3cuZGlnaWNlcnQu
# Y29tL0NQUzAKBghghkgBhv1sAzAdBgNVHQ4EFgQUWsS5eyoKo6XqcQPAYPkt9mV1
# DlgwHwYDVR0jBBgwFoAUReuir/SSy4IxLVGLp6chnfNtyA8wDQYJKoZIhvcNAQEL
# BQADggEBAD7sDVoks/Mi0RXILHwlKXaoHV0cLToaxO8wYdd+C2D9wz0PxK+L/e8q
# 3yBVN7Dh9tGSdQ9RtG6ljlriXiSBThCk7j9xjmMOE0ut119EefM2FAaK95xGTlz/
# kLEbBw6RFfu6r7VRwo0kriTGxycqoSkoGjpxKAI8LpGjwCUR4pwUR6F6aGivm6dc
# IFzZcbEMj7uo+MUSaJ/PQMtARKUT8OZkDCUIQjKyNookAv4vcn4c10lFluhZHen6
# dGRrsutmQ9qzsIzV6Q3d9gEgzpkxYz0IGhizgZtPxpMQBvwHgfqL2vmCSfdibqFT
# +hKUGIUukpHqaGxEMrJmoecYpJpkUe8wggUxMIIEGaADAgECAhAKoSXW1jIbfkHk
# Bdo2l8IVMA0GCSqGSIb3DQEBCwUAMGUxCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxE
# aWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xJDAiBgNVBAMT
# G0RpZ2lDZXJ0IEFzc3VyZWQgSUQgUm9vdCBDQTAeFw0xNjAxMDcxMjAwMDBaFw0z
# MTAxMDcxMjAwMDBaMHIxCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJ
# bmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xMTAvBgNVBAMTKERpZ2lDZXJ0
# IFNIQTIgQXNzdXJlZCBJRCBUaW1lc3RhbXBpbmcgQ0EwggEiMA0GCSqGSIb3DQEB
# AQUAA4IBDwAwggEKAoIBAQC90DLuS82Pf92puoKZxTlUKFe2I0rEDgdFM1EQfdD5
# fU1ofue2oPSNs4jkl79jIZCYvxO8V9PD4X4I1moUADj3Lh477sym9jJZ/l9lP+Cb
# 6+NGRwYaVX4LJ37AovWg4N4iPw7/fpX786O6Ij4YrBHk8JkDbTuFfAnT7l3ImgtU
# 46gJcWvgzyIQD3XPcXJOCq3fQDpct1HhoXkUxk0kIzBdvOw8YGqsLwfM/fDqR9mI
# UF79Zm5WYScpiYRR5oLnRlD9lCosp+R1PrqYD4R/nzEU1q3V8mTLex4F0IQZchfx
# FwbvPc3WTe8GQv2iUypPhR3EHTyvz9qsEPXdrKzpVv+TAgMBAAGjggHOMIIByjAd
# BgNVHQ4EFgQU9LbhIB3+Ka7S5GGlsqIlssgXNW4wHwYDVR0jBBgwFoAUReuir/SS
# y4IxLVGLp6chnfNtyA8wEgYDVR0TAQH/BAgwBgEB/wIBADAOBgNVHQ8BAf8EBAMC
# AYYwEwYDVR0lBAwwCgYIKwYBBQUHAwgweQYIKwYBBQUHAQEEbTBrMCQGCCsGAQUF
# BzABhhhodHRwOi8vb2NzcC5kaWdpY2VydC5jb20wQwYIKwYBBQUHMAKGN2h0dHA6
# Ly9jYWNlcnRzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydEFzc3VyZWRJRFJvb3RDQS5j
# cnQwgYEGA1UdHwR6MHgwOqA4oDaGNGh0dHA6Ly9jcmw0LmRpZ2ljZXJ0LmNvbS9E
# aWdpQ2VydEFzc3VyZWRJRFJvb3RDQS5jcmwwOqA4oDaGNGh0dHA6Ly9jcmwzLmRp
# Z2ljZXJ0LmNvbS9EaWdpQ2VydEFzc3VyZWRJRFJvb3RDQS5jcmwwUAYDVR0gBEkw
# RzA4BgpghkgBhv1sAAIEMCowKAYIKwYBBQUHAgEWHGh0dHBzOi8vd3d3LmRpZ2lj
# ZXJ0LmNvbS9DUFMwCwYJYIZIAYb9bAcBMA0GCSqGSIb3DQEBCwUAA4IBAQBxlRLp
# UYdWac3v3dp8qmN6s3jPBjdAhO9LhL/KzwMC/cWnww4gQiyvd/MrHwwhWiq3BTQd
# aq6Z+CeiZr8JqmDfdqQ6kw/4stHYfBli6F6CJR7Euhx7LCHi1lssFDVDBGiy23UC
# 4HLHmNY8ZOUfSBAYX4k4YU1iRiSHY4yRUiyvKYnleB/WCxSlgNcSR3CzddWThZN+
# tpJn+1Nhiaj1a5bA9FhpDXzIAbG5KHW3mWOFIoxhynmUfln8jA/jb7UBJrZspe6H
# USHkWGCbugwtK22ixH67xCUrRwIIfEmuE7bhfEJCKMYYVs9BNLZmXbZ0e/VWMyIv
# IjayS6JKldj1po5SMIIFPTCCBCWgAwIBAgIQBNXcH0jqydhSALrNmpsqpzANBgkq
# hkiG9w0BAQsFADByMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5j
# MRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29tMTEwLwYDVQQDEyhEaWdpQ2VydCBT
# SEEyIEFzc3VyZWQgSUQgQ29kZSBTaWduaW5nIENBMB4XDTIwMDYyNjAwMDAwMFoX
# DTIzMDcwNzEyMDAwMFowejELMAkGA1UEBhMCUEwxEjAQBgNVBAgMCcWabMSFc2tp
# ZTERMA8GA1UEBxMIS2F0b3dpY2UxITAfBgNVBAoMGFByemVteXPFgmF3IEvFgnlz
# IEVWT1RFQzEhMB8GA1UEAwwYUHJ6ZW15c8WCYXcgS8WCeXMgRVZPVEVDMIIBIjAN
# BgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAv7KB3iyBrhkLUbbFe9qxhKKPBYqD
# Bqlnr3AtpZplkiVjpi9dMZCchSeT5ODsShPuZCIxJp5I86uf8ibo3vi2S9F9AlfF
# jVye3dTz/9TmCuGH8JQt13ozf9niHecwKrstDVhVprgxi5v0XxY51c7zgMA2g1Ub
# +3tii0vi/OpmKXdL2keNqJ2neQ5cYly/GsI8CREUEq9SZijbdA8VrRF3SoDdsWGf
# 3tZZzO6nWn3TLYKQ5/bw5U445u/V80QSoykszHRivTj+H4s8ABiforhi0i76beA6
# Ea41zcH4zJuAp48B4UhjgRDNuq8IzLWK4dlvqrqCBHKqsnrF6BmBrv+BXQIDAQAB
# o4IBxTCCAcEwHwYDVR0jBBgwFoAUWsS5eyoKo6XqcQPAYPkt9mV1DlgwHQYDVR0O
# BBYEFBixNSfoHFAgJk4JkDQLFLRNlJRmMA4GA1UdDwEB/wQEAwIHgDATBgNVHSUE
# DDAKBggrBgEFBQcDAzB3BgNVHR8EcDBuMDWgM6Axhi9odHRwOi8vY3JsMy5kaWdp
# Y2VydC5jb20vc2hhMi1hc3N1cmVkLWNzLWcxLmNybDA1oDOgMYYvaHR0cDovL2Ny
# bDQuZGlnaWNlcnQuY29tL3NoYTItYXNzdXJlZC1jcy1nMS5jcmwwTAYDVR0gBEUw
# QzA3BglghkgBhv1sAwEwKjAoBggrBgEFBQcCARYcaHR0cHM6Ly93d3cuZGlnaWNl
# cnQuY29tL0NQUzAIBgZngQwBBAEwgYQGCCsGAQUFBwEBBHgwdjAkBggrBgEFBQcw
# AYYYaHR0cDovL29jc3AuZGlnaWNlcnQuY29tME4GCCsGAQUFBzAChkJodHRwOi8v
# Y2FjZXJ0cy5kaWdpY2VydC5jb20vRGlnaUNlcnRTSEEyQXNzdXJlZElEQ29kZVNp
# Z25pbmdDQS5jcnQwDAYDVR0TAQH/BAIwADANBgkqhkiG9w0BAQsFAAOCAQEAmr1s
# z4lsLARi4wG1eg0B8fVJFowtect7SnJUrp6XRnUG0/GI1wXiLIeow1UPiI6uDMsR
# XPHUF/+xjJw8SfIbwava2eXu7UoZKNh6dfgshcJmo0QNAJ5PIyy02/3fXjbUREHI
# NrTCvPVbPmV6kx4Kpd7KJrCo7ED18H/XTqWJHXa8va3MYLrbJetXpaEPpb6zk+l8
# Rj9yG4jBVRhenUBUUj3CLaWDSBpOA/+sx8/XB9W9opYfYGb+1TmbCkhUg7TB3gD6
# o6ESJre+fcnZnPVAPESmstwsT17caZ0bn7zETKlNHbc1q+Em9kyBjaQRcEQoQQNp
# ezQug9ufqExx6lHYDjGCBFwwggRYAgEBMIGGMHIxCzAJBgNVBAYTAlVTMRUwEwYD
# VQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xMTAv
# BgNVBAMTKERpZ2lDZXJ0IFNIQTIgQXNzdXJlZCBJRCBDb2RlIFNpZ25pbmcgQ0EC
# EATV3B9I6snYUgC6zZqbKqcwCQYFKw4DAhoFAKB4MBgGCisGAQQBgjcCAQwxCjAI
# oAKAAKECgAAwGQYJKoZIhvcNAQkDMQwGCisGAQQBgjcCAQQwHAYKKwYBBAGCNwIB
# CzEOMAwGCisGAQQBgjcCARUwIwYJKoZIhvcNAQkEMRYEFM4to6P0aN8+8tQdhUCd
# iF0Uh9hwMA0GCSqGSIb3DQEBAQUABIIBAJRnNCeBfPBu/OYw2GP9xHkVhCLvBkal
# jfYvlol6l4g68daaG9o8/du4bKHul0q9t0lu+OI/rSvazY8T7p4pmJPfzUNty9z2
# U0XHU4nMRI5MflcnkDzFkyH6NKHGS+hfe/C8Pq2cuNxPLXOZ8UMg5h4rMsj/18C7
# +qP3T2kPtkQuY8UwNxNZZlAWLj9KmW4wPAS8sxgnWsYfhzjVGASxqR9oKM0NeAsK
# kP/gH+e7urkE9fDb/ud8aYYYuUvEMYU22nmy4ZzNOHi9UHI0c9jzZCg5WxP5x4RH
# q3uwpeHpcK/mY38Y26X0y6fkeVonK1m2F0t4Dn3Inb1JEY85UC1aHkqhggIwMIIC
# LAYJKoZIhvcNAQkGMYICHTCCAhkCAQEwgYYwcjELMAkGA1UEBhMCVVMxFTATBgNV
# BAoTDERpZ2lDZXJ0IEluYzEZMBcGA1UECxMQd3d3LmRpZ2ljZXJ0LmNvbTExMC8G
# A1UEAxMoRGlnaUNlcnQgU0hBMiBBc3N1cmVkIElEIFRpbWVzdGFtcGluZyBDQQIQ
# DUJK4L46iP9gQCHOFADw3TANBglghkgBZQMEAgEFAKBpMBgGCSqGSIb3DQEJAzEL
# BgkqhkiG9w0BBwEwHAYJKoZIhvcNAQkFMQ8XDTIxMDMzMDE5MzIzOVowLwYJKoZI
# hvcNAQkEMSIEILoP+EhWNcCvjxadTqiKc3D0lNNbGxs09U2p/dIMYHrRMA0GCSqG
# SIb3DQEBAQUABIIBACQ/8/RCwtTnCAhOXLYAPVfhGwGhNuUYkcGCD1v/pr0wtGKn
# Pv9CTIpdiadbJnIZtL0wnh6PMzKw8nHIAcgH+S9SG3V1sPURF1bFcFXl9eBunawX
# /KEgZHoV4ekxuLZAr3VAWF0UuTEknglYet/z/x5Oud0jhZUIJpbMDpmUNQKgsc95
# zxL/hKKHt4NbzNKVY1h/zGL9IuUmVEBZMN6gO06EgFgltglItX8f69ge7z0vuxRz
# ikOnizEfLwx6r2vKFDdF2+JBIkG7cp4RcV6DdPVuq+ahaByaM7xlVWu87iN92j4u
# C5HbVTAkxiQ7eiCMl71xcsCA1AsXPgnOuZWR3Ek=
# SIG # End signature block