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 |