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, [switch] $DoNotRemoveNull, [switch] $DoNotRemoveEmpty, [switch] $DoNotRemoveEmptyArray, [switch] $DoNotRemoveEmptyDictionary) 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) { if (-not $DoNotRemoveEmptyDictionary) { $Hashtable.Remove($Key) } } else { Remove-EmptyValue -Hashtable $Hashtable[$Key] -Recursive:$Recursive } } else { if (-not $DoNotRemoveNull -and $null -eq $Hashtable[$Key]) { $Hashtable.Remove($Key) } elseif (-not $DoNotRemoveEmpty -and $Hashtable[$Key] -is [string] -and $Hashtable[$Key] -eq '') { $Hashtable.Remove($Key) } elseif (-not $DoNotRemoveEmptyArray -and $Hashtable[$Key] -is [System.Collections.IList] -and $Hashtable[$Key].Count -eq 0) { $Hashtable.Remove($Key) } } } else { if (-not $DoNotRemoveNull -and $null -eq $Hashtable[$Key]) { $Hashtable.Remove($Key) } elseif (-not $DoNotRemoveEmpty -and $Hashtable[$Key] -is [string] -and $Hashtable[$Key] -eq '') { $Hashtable.Remove($Key) } elseif (-not $DoNotRemoveEmptyArray -and $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 = "Receive-FTPFile - Given path $($RemoteFile.FullName) is $($RemoteFile.Type). Skipping." } } else { Write-Warning "Receive-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 "Receive-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 = "Receive-FTPFile - Given path $($RemoteFile.FullName) is $($RemoteFile.Type). Skipping." } } else { Write-Warning "Receive-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 "Receive-FTPFile - Error: $($_.Exception.Message)" } } $Status } function Compare-FTPFile { [cmdletBinding()] param([Parameter(Mandatory)][FluentFTP.FtpClient] $Client, [Parameter(Mandatory)][string] $LocalPath, [Parameter(Mandatory)][string] $RemotePath, [FluentFTP.FtpCompareOption] $CompareOption = [FluentFTP.FtpCompareOption]::Auto) if ($Client -and $Client.IsConnected -and -not $Client.Error) { $Client.CompareFile($LocalPath, $RemotePath, $CompareOption) } } 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', Mandatory)] [Parameter(ParameterSetName = 'Password', Mandatory)] [Parameter(ParameterSetName = 'PrivateKey', Mandatory)] [string] $Server, [Parameter(ParameterSetName = 'ClearText', Mandatory)] [Parameter(ParameterSetName = 'PrivateKey', Mandatory)] [string] $Username, [Parameter(ParameterSetName = 'ClearText', Mandatory)] [string] $Password, [Parameter(ParameterSetName = 'Password', Mandatory)] [pscredential] $Credential, [Parameter(Mandatory, ParameterSetName = 'PrivateKey')] [string] $PrivateKey, [Parameter(ParameterSetName = 'ClearText')] [Parameter(ParameterSetName = 'Password')] [Parameter(ParameterSetName = 'PrivateKey')] [int] $Port) try { if ($Username -and $Password) { if ($Port) { $SftpClient = [Renci.SshNet.SftpClient]::new($Server, $Port, $Username, $Password) } else { $SftpClient = [Renci.SshNet.SftpClient]::new($Server, $Username, $Password) } } elseif ($Credential) { if ($Port) { $SftpClient = [Renci.SshNet.SftpClient]::new($Server, $Port, $Credential.Username, $Credential.GetNetworkCredential().Password) } else { $SftpClient = [Renci.SshNet.SftpClient]::new($Server, $Credential.Username, $Credential.GetNetworkCredential().Password) } } elseif ($PrivateKey) { if (Test-Path -LiteralPath $PrivateKey) { [string]$PrivateKey = Resolve-Path -LiteralPath $PrivateKey -ErrorAction Stop | Select-Object -ExpandProperty ProviderPath if ($Port) { $SftpClient = [Renci.SshNet.SftpClient]::new($Server, $Port, $Username, [Renci.SshNet.PrivateKeyFile]$PrivateKey) } else { $SftpClient = [Renci.SshNet.SftpClient]::new($Server, $Username, [Renci.SshNet.PrivateKeyFile]$PrivateKey) } } else { if ($PSBoundParameters.ErrorAction -eq 'Stop') { throw "PrivateKey $PrivateKey doesn't exists." return } else { Write-Warning "Connect-SFTP - PrivateKey $PrivateKey doesn't exists." return } } } else { throw 'Not implemented and unexepected.' return } } catch { if ($PSBoundParameters.ErrorAction -eq 'Stop') { Write-Error $_ return } else { Write-Warning "Connect-SFTP - Error: $($_.Exception.Message)" } } 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 Connect-SSH { [cmdletBinding(DefaultParameterSetName = 'Password')] param([Parameter(Mandatory, ParameterSetName = 'ClearText')] [Parameter(Mandatory, ParameterSetName = 'Password')] [Parameter(Mandatory, ParameterSetName = 'PrivateKey')] [string] $Server, [Parameter(Mandatory, ParameterSetName = 'ClearText')] [Parameter(Mandatory, ParameterSetName = 'PrivateKey')] [string] $Username, [Parameter(Mandatory, ParameterSetName = 'ClearText')] [string] $Password, [Parameter(Mandatory, ParameterSetName = 'Password')] [pscredential] $Credential, [Parameter(Mandatory, ParameterSetName = 'PrivateKey')] [string] $PrivateKey, [Parameter(ParameterSetName = 'ClearText')] [Parameter(ParameterSetName = 'Password')] [Parameter(ParameterSetName = 'PrivateKey')] [int] $Port) if ($Username -and $Password) { if ($Port) { $SshClient = [Renci.SshNet.SshClient]::new($Server, $Port, $Username, $Password) } else { $SshClient = [Renci.SshNet.SshClient]::new($Server, $Username, $Password) } } elseif ($Credential) { if ($Port) { $SshClient = [Renci.SshNet.SshClient]::new($Server, $Port, $Credential.Username, $Credential.GetNetworkCredential().Password) } else { $SshClient = [Renci.SshNet.SshClient]::new($Server, $Credential.Username, $Credential.GetNetworkCredential().Password) } } elseif ($PrivateKey) { [string]$PrivateKey = Resolve-Path $PrivateKey | Select-Object -ExpandProperty ProviderPath if ($Port) { $SshClient = [Renci.SshNet.SshClient]::new($Server, $Port, $Username, [Renci.SshNet.PrivateKeyFile]$PrivateKey) } else { $SshClient = [Renci.SshNet.SshClient]::new($Server, $Username, [Renci.SshNet.PrivateKeyFile]$PrivateKey) } } else { throw 'Not implemented and unexpected.' } try { $SshClient.Connect() $SshClient | Add-Member -Name 'Error' -Value $null -Force -MemberType NoteProperty } catch { $SshClient | Add-Member -Name 'Error' -Value $($_.Exception.Message) -Force -MemberType NoteProperty if ($PSBoundParameters.ErrorAction -eq 'Stop') { Write-Error $_ return } else { Write-Warning "Connect-SSH - Error: $($_.Exception.Message)" } } $SshClient } 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-FTPChecksum { [cmdletBinding()] param([Parameter(Mandatory)][FluentFTP.FtpClient] $Client, [Parameter(Mandatory)][string] $RemotePath, [FluentFTP.FtpHashAlgorithm] $HashAlgorithm = [FluentFTP.FtpHashAlgorithm]::MD5) if ($Client -and $Client.IsConnected -and -not $Client.Error) { $Client.GetChecksum($RemotePath, $HashAlgorithm) } } function Get-FTPChmod { [cmdletBinding()] param([Parameter(Mandatory)][FluentFTP.FtpClient] $Client, [string] $RemotePath) if ($Client -and $Client.IsConnected -and -not $Client.Error) { $Client.GetChmod($RemotePath) } } 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-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 Move-FTPDirectory { [cmdletBinding()] param([Parameter(Mandatory)][FluentFTP.FtpClient] $Client, [Parameter(Mandatory)][string] $RemoteSource, [Parameter(Mandatory)][string] $RemoteDestination, [FluentFTP.FtpRemoteExists] $RemoteExists = [FluentFTP.FtpRemoteExists]::Skip) if ($Client -and $Client.IsConnected -and -not $Client.Error) { $Client.MoveDirectory($RemoteSource, $RemoteDestination, $RemoteExists) } } function Move-FTPFile { [cmdletBinding()] param([Parameter(Mandatory)][FluentFTP.FtpClient] $Client, [Parameter(Mandatory)][string] $RemoteSource, [Parameter(Mandatory)][string] $RemoteDestination, [FluentFTP.FtpRemoteExists] $RemoteExists = [FluentFTP.FtpRemoteExists]::Skip) if ($Client -and $Client.IsConnected -and -not $Client.Error) { $Client.MoveFile($RemoteSource, $RemoteDestination, $RemoteExists) } } function Receive-FTPDirectory { [alias('Get-FTPDirectory')] [cmdletBinding()] param([Parameter(Mandatory)][FluentFTP.FtpClient] $Client, [string] $LocalPath, [Parameter(Mandatory)][string] $RemotePath, [FluentFTP.FtpFolderSyncMode] $FolderSyncMode = [FluentFTP.FtpFolderSyncMode]::Update, [FluentFTP.FtpLocalExists] $LocalExists, [FluentFTP.FtpVerify] $VerifyOptions, [FluentFTP.Rules.FtpRule[]] $Rules) if ($Client -and $Client.IsConnected -and -not $Client.Error) { $Client.DownloadDirectory($LocalPath, $RemotePath, $FolderSyncMode) } } function Receive-FTPFile { [alias('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 -and -not $Client.Error) { $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 "Receive-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 = "Multiple files detected, but $LocalPath is not a directory or directory doesn't exists." } } 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 Receive-SFTPFile { [alias('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 "Receive-SFTPFile - Error: $($_.Exception.Message)" } } finally { $FileStream.Close() if ($Status.Status -eq $false) { Remove-Item -LiteralPath $LocalPath } } $Status } } function Remove-FTPDirectory { [cmdletBinding()] param([Parameter(Mandatory)][FluentFTP.FtpClient] $Client, [Parameter(Mandatory)][string] $RemotePath, [FluentFTP.FtpListOption] $FtpListOption) if ($Client -and $Client.IsConnected -and -not $Client.Error) { if (-not $FtpListOption) { $Client.DeleteDirectory($RemotePath) } else { $Client.DeleteDirectory($RemotePath, $FtpListOption) } } } function Remove-FTPFile { [cmdletBinding()] param([Parameter(Mandatory)][FluentFTP.FtpClient] $Client, [Parameter(Mandatory)][string] $RemotePath) if ($Client -and $Client.IsConnected -and -not $Client.Error) { $Client.DeleteFile($RemotePath) } } 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([Parameter(Mandatory)][FluentFTP.FtpClient] $Client, [Parameter(Mandatory)][string] $Path, [Parameter(Mandatory)][string] $DestinationPath) if ($Client -and $Client.IsConnected -and -not $Client.Error) { $Client.Rename($Path, $DestinationPath) } } function Rename-SFTPFile { <# .SYNOPSIS Allows renaming remote file over SFTP protocol .DESCRIPTION Allows renaming remote file over SFTP protocol .PARAMETER SftpClient Parameter that contains the SFTP client object .PARAMETER SourcePath Path to file that is going to be renamed .PARAMETER DestinationPath New path to file, with new name .PARAMETER Suppress Suppress returning an object with information about the operation .EXAMPLE $SftpClient = Connect-SFTP -Server '192.168.240.29' -Username 'przemek' -Password 'Password' Rename-SFTPFile -SftpClient $SftpClient -SourcePath '/home/przemek/mmm.txt' -DestinationPath '/home/przemek/mmm1.txt' Disconnect-SFTP -SftpClient $SftpClient .NOTES General notes #> [cmdletBinding()] param([Parameter(Mandatory)][Renci.SshNet.SftpClient] $SftpClient, [alias('OldPath')][string] $SourcePath, [alias('NewPath')][string] $DestinationPath, [switch] $Suppress) if ($SftpClient -and $SftpClient.IsConnected) { try { $SftpClient.RenameFile($SourcePath, $DestinationPath) $Status = [PSCustomObject] @{Action = 'RenameFile' Status = $true OldPath = $SourcePath NewPath = $DestinationPath Message = "" } } catch { $Status = [PSCustomObject] @{Action = 'RenameFile' Status = $false OldPath = $SourcePath NewPath = $DestinationPath 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 Send-FTPDirectory { <# .SYNOPSIS Uploads a directory to an FTP server. .DESCRIPTION Uploads a directory to an FTP server. .PARAMETER Client The Client to use for connection. .PARAMETER LocalPath Path on the local machine to upload to FTP Server .PARAMETER RemotePath Path on the FTP Server where to upload the content .PARAMETER FolderSyncMode Update - upload a folder and all its files Mirror - upload a folder and all its files, and delete extra files on the server .PARAMETER RemoteExists Provide decision what to do when file on the server exits. Options available: Append, AppendNoChek, NoCheck, Skip, , Overwrite Default: Skip. .PARAMETER VerifyOptions Provide options for verification of files on the remote server. Options available: Delete, OnlyChecksum, None, Retry Default: None. .PARAMETER Rules Provide rules and conditions for uploading files. .EXAMPLE Set-FTPTracing -Enable -DisplayConsole $Client = Connect-FTP -Server '192.168.241.187' -Verbose -Username 'test' -Password 'BiPassword90A' -EncryptionMode Explicit -ValidateAnyCertificate $Upload = Send-FTPDirectory -Client $Client -LocalPath $PSScriptRoot\Upload -RemotePath '/Temporary' -Verbose -FolderSyncMode Update $Upload | Format-Table * Disconnect-FTP -Client $Client Set-FTPTracing -Disable .NOTES General notes #> [alias('Add-FTPDirectory')] [cmdletBinding()] param([Parameter(Mandatory)][FluentFTP.FtpClient] $Client, [string] $LocalPath, [Parameter(Mandatory)][string] $RemotePath, [FluentFTP.FtpFolderSyncMode] $FolderSyncMode = [FluentFTP.FtpFolderSyncMode]::Update, [FluentFTP.FtpRemoteExists] $RemoteExists = [FluentFTP.FtpRemoteExists]::Skip, [FluentFTP.FtpVerify] $VerifyOptions = [FluentFTP.FtpVerify]::None, [FluentFTP.Rules.FtpRule[]] $Rules) if ($Client -and $Client.IsConnected -and -not $Client.Error) { $Client.UploadDirectory($LocalPath, $RemotePath, $FolderSyncMode, $RemoteExists, $VerifyOptions, @($Rules)) } } function Send-FTPFile { [alias('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 -and -not $Client.Error) { 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 Send-SFTPFile { [alias('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 "Send-SFTPFile - Error: $($_.Exception.Message)" } } finally { $FileStream.Close() } } else { Write-Warning "Send-SFTPFile - File $LocalPath doesn't exists." $Status = [PSCustomObject] @{Action = 'UploadFile' Status = $false LocalPath = $LocalPath RemotePath = $RemotePath Message = "LocalPath doesn't exists $LocalPath" } } $Status } } function Send-SSHCommand { [cmdletBinding()] param([Parameter(Mandatory)][Renci.SshNet.SshClient] $SshClient, [scriptblock] $Command, [switch] $Status) if ($SshClient -and $SshClient.IsConnected -and -not $SshClient.Error) { if ($Command) { $CommandsToExecute = & $Command [string] $SendCommand = foreach ($C in $CommandsToExecute) { if ($C.Trim().EndsWith(';')) { $C } else { "$C;" } } try { Write-Verbose -Message "Send-SSHCommand - Executing command: $SendCommand" if ($Status) { [PSCustomObject] @{Status = $true Output = $SshClient.CreateCommand($SendCommand).Execute() Error = $null } } else { $SshClient.CreateCommand($SendCommand).Execute() } } catch { if ($PSBoundParameters.ErrorAction -eq 'Stop') { Write-Error $_ return } else { Write-Warning "Send-SSHCommand - Error: $($_.Exception.Message)" } if ($Status) { [PSCustomObject] @{Status = $false Output = '' Error = "Error: $($_.Exception.Message)" } } } } } } function Set-FTPChmod { [cmdletBinding(DefaultParameterSetName = 'ByInt')] param([Parameter(Mandatory, ParameterSetName = 'ByInt')] [Parameter(Mandatory, ParameterSetName = 'Explicit')] [FluentFTP.FtpClient] $Client, [Parameter(Mandatory, ParameterSetName = 'ByInt')] [Parameter(Mandatory, ParameterSetName = 'Explicit')] [string] $RemotePath, [Parameter(Mandatory, ParameterSetName = 'ByInt')] [nullable[int]] $Permissions, [Parameter(Mandatory, ParameterSetName = 'Explicit')] [FluentFTP.FtpPermission] $Owner, [Parameter(Mandatory, ParameterSetName = 'Explicit')] [FluentFTP.FtpPermission] $Group, [Parameter(Mandatory, ParameterSetName = 'Explicit')] [FluentFTP.FtpPermission] $Other) if ($Client -and $Client.IsConnected -and -not $Client.Error) { if ($Permissions) { $Client.Chmod($RemotePath, $Permissions) } else { $Client.Chmod($RemotePath, $Owner, $Group, $Other) } } } function Set-FTPOption { [cmdletBinding()] param([Parameter(Mandatory)][FluentFTP.FtpClient] $Client, [nullable[int]] $RetryAttempts, [nullable[bool]] $DownloadZeroByteFiles) if ($RetryAttempts) { $Client.RetryAttempts = $RetryAttempts } if ($DownloadZeroByteFiles) { $Client.DownloadZeroByteFiles = $DownloadZeroByteFiles } } function Set-FTPTracing { <# .SYNOPSIS Allows enabling/disabling tracing ftp commands being sent and received during communucation with the server. .DESCRIPTION Allows enabling/disabling tracing ftp commands being sent and received during communucation with the server. .PARAMETER LogPath Set this to a file path to append all FTP communication to it. Default: false. .PARAMETER Enable Enable tracing .PARAMETER Disable Disable tracing .PARAMETER ShowPassword Include FTP passwords in logs? Default: false. .PARAMETER HideUserName Hide FTP usernames in logs? Default: false. .PARAMETER HideIP Hide server IP addresses in logs? Default: true. .PARAMETER HideFunctions Hide high-level function calls in logs? Default: false. .PARAMETER DisplayConsole Should FTP communication be be logged to the console? Default: false. .EXAMPLE Set-FTPTracing -Enable -DisplayConsole .NOTES General notes #> [cmdletBinding()] param([string] $LogPath, [switch] $Enable, [switch] $Disable, [switch] $ShowPassword, [switch] $HideUserName, [switch] $HideIP, [switch] $HideFunctions, [switch] $DisplayConsole) if ($Enable) { [FluentFTP.Helpers.FtpTrace]::EnableTracing = $true } if ($Disable) { [FluentFTP.Helpers.FtpTrace]::EnableTracing = $false } if ($LogPath) { [FluentFTP.Helpers.FtpTrace]::LogToFile = $LogPath } if (-not $HideFunctions) { [FluentFTP.Helpers.FtpTrace]::LogFunctions = $true } if ($DisplayConsole) { [FluentFTP.Helpers.FtpTrace]::LogToConsole = $true } if ($HideUserName) { [FluentFTP.Helpers.FtpTrace]::LogUserName = $false } if ($ShowPassword) { [FluentFTP.Helpers.FtpTrace]::LogPassword = $false } if (-not $HideIP) { [FluentFTP.Helpers.FtpTrace]::LogIP = $true } } function Start-FXPDirectoryTransfer { [alias('Start-FXPDirectory')] [cmdletBinding()] param([alias('SourceClient')][Parameter(Mandatory)][FluentFTP.FtpClient] $Client, [Parameter(Mandatory)][string] $SourcePath, [Parameter(Mandatory)][FluentFTP.FtpClient] $DestinationClient, [Parameter(Mandatory)][string] $DestinationPath, [FluentFTP.FtpFolderSyncMode] $FolderSyncMode = [FluentFTP.FtpFolderSyncMode]::Update, [FluentFTP.FtpRemoteExists] $RemoteExists = [FluentFTP.FtpRemoteExists]::Skip, [FluentFTP.FtpVerify[]] $VerifyOptions = [FluentFTP.FtpVerify]::None) if ($Client -and $Client.IsConnected -and -not $Client.Error) { $Client.TransferDirectory($SourcePath, $DestinationClient, $DestinationPath, $FolderSyncMode, $RemoteExists, $VerifyOptions) } } function Start-FXPFileTransfer { [alias('Start-FXPFile')] [cmdletBinding()] param([alias('SourceClient')][Parameter(Mandatory)][FluentFTP.FtpClient] $Client, [Parameter(Mandatory)][string] $SourcePath, [Parameter(Mandatory)][FluentFTP.FtpClient] $DestinationClient, [Parameter(Mandatory)][string] $DestinationPath, [switch] $CreateRemoteDirectory, [FluentFTP.FtpRemoteExists] $RemoteExists = [FluentFTP.FtpRemoteExists]::Skip, [FluentFTP.FtpVerify[]] $VerifyOptions = [FluentFTP.FtpVerify]::None) if ($Client -and $Client.IsConnected -and -not $Client.Error) { $Client.TransferFile($SourcePath, $DestinationClient, $DestinationPath, $CreateRemoteDirectory.IsPresent, $RemoteExists, $VerifyOptions) } } function Test-FTPDirectory { [cmdletBinding()] param([Parameter(Mandatory)][FluentFTP.FtpClient] $Client, [Parameter(Mandatory)][string] $RemotePath) if ($Client -and $Client.IsConnected -and -not $Client.Error) { $Client.DirectoryExists($RemotePath) } } function Test-FTPFile { [cmdletBinding()] param([Parameter(Mandatory)][FluentFTP.FtpClient] $Client, [Parameter(Mandatory)][string] $RemotePath) if ($Client -and $Client.IsConnected -and -not $Client.Error) { $Client.FileExists($RemotePath) } } if ($PSVersionTable.PSEdition -eq 'Desktop' -and (Get-ItemProperty "HKLM:\SOFTWARE\Microsoft\NET Framework Setup\NDP\v4\Full").Release -lt 461808) { Write-Warning "This module requires .NET Framework 4.7.2 or later."; return } Export-ModuleMember -Function @('Compare-FTPFile', 'Connect-FTP', 'Connect-SFTP', 'Connect-SSH', 'Disconnect-FTP', 'Disconnect-SFTP', 'Get-FTPChecksum', 'Get-FTPChmod', 'Get-FTPList', 'Get-SFTPList', 'Move-FTPDirectory', 'Move-FTPFile', 'Receive-FTPDirectory', 'Receive-FTPFile', 'Receive-SFTPFile', 'Remove-FTPDirectory', 'Remove-FTPFile', 'Remove-SFTPFile', 'Rename-FTPFile', 'Rename-SFTPFile', 'Request-FTPConfiguration', 'Send-FTPDirectory', 'Send-FTPFile', 'Send-SFTPFile', 'Send-SSHCommand', 'Set-FTPChmod', 'Set-FTPOption', 'Set-FTPTracing', 'Start-FXPDirectoryTransfer', 'Start-FXPFileTransfer', 'Test-FTPDirectory', 'Test-FTPFile') -Alias @('Add-FTPDirectory', 'Add-FTPFile', 'Add-SFTPFile', 'Get-FTPDirectory', 'Get-FTPFile', 'Get-SFTPFile', 'Start-FXPDirectory', 'Start-FXPFile') # SIG # Begin signature block # MIInPgYJKoZIhvcNAQcCoIInLzCCJysCAQExDzANBglghkgBZQMEAgEFADB5Bgor # BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG # KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCBNRYYVj51mMWGP # 3QlO65xLqjkxlRE0yXEdSqXo2uApiKCCITcwggO3MIICn6ADAgECAhAM5+DlF9hG # /o/lYPwb8DA5MA0GCSqGSIb3DQEBBQUAMGUxCzAJBgNVBAYTAlVTMRUwEwYDVQQK # EwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xJDAiBgNV # BAMTG0RpZ2lDZXJ0IEFzc3VyZWQgSUQgUm9vdCBDQTAeFw0wNjExMTAwMDAwMDBa # Fw0zMTExMTAwMDAwMDBaMGUxCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2Vy # dCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xJDAiBgNVBAMTG0RpZ2lD # ZXJ0IEFzc3VyZWQgSUQgUm9vdCBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCC # AQoCggEBAK0OFc7kQ4BcsYfzt2D5cRKlrtwmlIiq9M71IDkoWGAM+IDaqRWVMmE8 # tbEohIqK3J8KDIMXeo+QrIrneVNcMYQq9g+YMjZ2zN7dPKii72r7IfJSYd+fINcf # 4rHZ/hhk0hJbX/lYGDW8R82hNvlrf9SwOD7BG8OMM9nYLxj+KA+zp4PWw25EwGE1 # lhb+WZyLdm3X8aJLDSv/C3LanmDQjpA1xnhVhyChz+VtCshJfDGYM2wi6YfQMlqi # uhOCEe05F52ZOnKh5vqk2dUXMXWuhX0irj8BRob2KHnIsdrkVxfEfhwOsLSSplaz # vbKX7aqn8LfFqD+VFtD/oZbrCF8Yd08CAwEAAaNjMGEwDgYDVR0PAQH/BAQDAgGG # MA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFEXroq/0ksuCMS1Ri6enIZ3zbcgP # MB8GA1UdIwQYMBaAFEXroq/0ksuCMS1Ri6enIZ3zbcgPMA0GCSqGSIb3DQEBBQUA # A4IBAQCiDrzf4u3w43JzemSUv/dyZtgy5EJ1Yq6H6/LV2d5Ws5/MzhQouQ2XYFwS # TFjk0z2DSUVYlzVpGqhH6lbGeasS2GeBhN9/CTyU5rgmLCC9PbMoifdf/yLil4Qf # 6WXvh+DfwWdJs13rsgkq6ybteL59PyvztyY1bV+JAbZJW58BBZurPSXBzLZ/wvFv # hsb6ZGjrgS2U60K3+owe3WLxvlBnt2y98/Efaww2BxZ/N3ypW2168RJGYIPXJwS+ # S86XvsNnKmgR34DnDDNmvxMNFG7zfx9jEB76jRslbWyPpbdhAbHSoyahEHGdreLD # +cOZUbcrBwjOLuZQsqf6CkUvovDyMIIFMDCCBBigAwIBAgIQBAkYG1/Vu2Z1U0O1 # b5VQCDANBgkqhkiG9w0BAQsFADBlMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGln # aUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29tMSQwIgYDVQQDExtE # aWdpQ2VydCBBc3N1cmVkIElEIFJvb3QgQ0EwHhcNMTMxMDIyMTIwMDAwWhcNMjgx # MDIyMTIwMDAwWjByMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5j # MRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29tMTEwLwYDVQQDEyhEaWdpQ2VydCBT # SEEyIEFzc3VyZWQgSUQgQ29kZSBTaWduaW5nIENBMIIBIjANBgkqhkiG9w0BAQEF # AAOCAQ8AMIIBCgKCAQEA+NOzHH8OEa9ndwfTCzFJGc/Q+0WZsTrbRPV/5aid2zLX # cep2nQUut4/6kkPApfmJ1DcZ17aq8JyGpdglrA55KDp+6dFn08b7KSfH03sjlOSR # I5aQd4L5oYQjZhJUM1B0sSgmuyRpwsJS8hRniolF1C2ho+mILCCVrhxKhwjfDPXi # TWAYvqrEsq5wMWYzcT6scKKrzn/pfMuSoeU7MRzP6vIK5Fe7SrXpdOYr/mzLfnQ5 # Ng2Q7+S1TqSp6moKq4TzrGdOtcT3jNEgJSPrCGQ+UpbB8g8S9MWOD8Gi6CxR93O8 # vYWxYoNzQYIH5DiLanMg0A9kczyen6Yzqf0Z3yWT0QIDAQABo4IBzTCCAckwEgYD # VR0TAQH/BAgwBgEB/wIBADAOBgNVHQ8BAf8EBAMCAYYwEwYDVR0lBAwwCgYIKwYB # BQUHAwMweQYIKwYBBQUHAQEEbTBrMCQGCCsGAQUFBzABhhhodHRwOi8vb2NzcC5k # aWdpY2VydC5jb20wQwYIKwYBBQUHMAKGN2h0dHA6Ly9jYWNlcnRzLmRpZ2ljZXJ0 # LmNvbS9EaWdpQ2VydEFzc3VyZWRJRFJvb3RDQS5jcnQwgYEGA1UdHwR6MHgwOqA4 # oDaGNGh0dHA6Ly9jcmw0LmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydEFzc3VyZWRJRFJv # b3RDQS5jcmwwOqA4oDaGNGh0dHA6Ly9jcmwzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2Vy # dEFzc3VyZWRJRFJvb3RDQS5jcmwwTwYDVR0gBEgwRjA4BgpghkgBhv1sAAIEMCow # KAYIKwYBBQUHAgEWHGh0dHBzOi8vd3d3LmRpZ2ljZXJ0LmNvbS9DUFMwCgYIYIZI # AYb9bAMwHQYDVR0OBBYEFFrEuXsqCqOl6nEDwGD5LfZldQ5YMB8GA1UdIwQYMBaA # FEXroq/0ksuCMS1Ri6enIZ3zbcgPMA0GCSqGSIb3DQEBCwUAA4IBAQA+7A1aJLPz # ItEVyCx8JSl2qB1dHC06GsTvMGHXfgtg/cM9D8Svi/3vKt8gVTew4fbRknUPUbRu # pY5a4l4kgU4QpO4/cY5jDhNLrddfRHnzNhQGivecRk5c/5CxGwcOkRX7uq+1UcKN # JK4kxscnKqEpKBo6cSgCPC6Ro8AlEeKcFEehemhor5unXCBc2XGxDI+7qPjFEmif # z0DLQESlE/DmZAwlCEIysjaKJAL+L3J+HNdJRZboWR3p+nRka7LrZkPas7CM1ekN # 3fYBIM6ZMWM9CBoYs4GbT8aTEAb8B4H6i9r5gkn3Ym6hU/oSlBiFLpKR6mhsRDKy # ZqHnGKSaZFHvMIIFPTCCBCWgAwIBAgIQBNXcH0jqydhSALrNmpsqpzANBgkqhkiG # 9w0BAQsFADByMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkw # FwYDVQQLExB3d3cuZGlnaWNlcnQuY29tMTEwLwYDVQQDEyhEaWdpQ2VydCBTSEEy # IEFzc3VyZWQgSUQgQ29kZSBTaWduaW5nIENBMB4XDTIwMDYyNjAwMDAwMFoXDTIz # MDcwNzEyMDAwMFowejELMAkGA1UEBhMCUEwxEjAQBgNVBAgMCcWabMSFc2tpZTER # MA8GA1UEBxMIS2F0b3dpY2UxITAfBgNVBAoMGFByemVteXPFgmF3IEvFgnlzIEVW # T1RFQzEhMB8GA1UEAwwYUHJ6ZW15c8WCYXcgS8WCeXMgRVZPVEVDMIIBIjANBgkq # hkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAv7KB3iyBrhkLUbbFe9qxhKKPBYqDBqln # r3AtpZplkiVjpi9dMZCchSeT5ODsShPuZCIxJp5I86uf8ibo3vi2S9F9AlfFjVye # 3dTz/9TmCuGH8JQt13ozf9niHecwKrstDVhVprgxi5v0XxY51c7zgMA2g1Ub+3ti # i0vi/OpmKXdL2keNqJ2neQ5cYly/GsI8CREUEq9SZijbdA8VrRF3SoDdsWGf3tZZ # zO6nWn3TLYKQ5/bw5U445u/V80QSoykszHRivTj+H4s8ABiforhi0i76beA6Ea41 # zcH4zJuAp48B4UhjgRDNuq8IzLWK4dlvqrqCBHKqsnrF6BmBrv+BXQIDAQABo4IB # xTCCAcEwHwYDVR0jBBgwFoAUWsS5eyoKo6XqcQPAYPkt9mV1DlgwHQYDVR0OBBYE # FBixNSfoHFAgJk4JkDQLFLRNlJRmMA4GA1UdDwEB/wQEAwIHgDATBgNVHSUEDDAK # BggrBgEFBQcDAzB3BgNVHR8EcDBuMDWgM6Axhi9odHRwOi8vY3JsMy5kaWdpY2Vy # dC5jb20vc2hhMi1hc3N1cmVkLWNzLWcxLmNybDA1oDOgMYYvaHR0cDovL2NybDQu # ZGlnaWNlcnQuY29tL3NoYTItYXNzdXJlZC1jcy1nMS5jcmwwTAYDVR0gBEUwQzA3 # BglghkgBhv1sAwEwKjAoBggrBgEFBQcCARYcaHR0cHM6Ly93d3cuZGlnaWNlcnQu # Y29tL0NQUzAIBgZngQwBBAEwgYQGCCsGAQUFBwEBBHgwdjAkBggrBgEFBQcwAYYY # aHR0cDovL29jc3AuZGlnaWNlcnQuY29tME4GCCsGAQUFBzAChkJodHRwOi8vY2Fj # ZXJ0cy5kaWdpY2VydC5jb20vRGlnaUNlcnRTSEEyQXNzdXJlZElEQ29kZVNpZ25p # bmdDQS5jcnQwDAYDVR0TAQH/BAIwADANBgkqhkiG9w0BAQsFAAOCAQEAmr1sz4ls # LARi4wG1eg0B8fVJFowtect7SnJUrp6XRnUG0/GI1wXiLIeow1UPiI6uDMsRXPHU # F/+xjJw8SfIbwava2eXu7UoZKNh6dfgshcJmo0QNAJ5PIyy02/3fXjbUREHINrTC # vPVbPmV6kx4Kpd7KJrCo7ED18H/XTqWJHXa8va3MYLrbJetXpaEPpb6zk+l8Rj9y # G4jBVRhenUBUUj3CLaWDSBpOA/+sx8/XB9W9opYfYGb+1TmbCkhUg7TB3gD6o6ES # Jre+fcnZnPVAPESmstwsT17caZ0bn7zETKlNHbc1q+Em9kyBjaQRcEQoQQNpezQu # g9ufqExx6lHYDjCCBY0wggR1oAMCAQICEA6bGI750C3n79tQ4ghAGFowDQYJKoZI # hvcNAQEMBQAwZTELMAkGA1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZ # MBcGA1UECxMQd3d3LmRpZ2ljZXJ0LmNvbTEkMCIGA1UEAxMbRGlnaUNlcnQgQXNz # dXJlZCBJRCBSb290IENBMB4XDTIyMDgwMTAwMDAwMFoXDTMxMTEwOTIzNTk1OVow # YjELMAkGA1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZMBcGA1UECxMQ # d3d3LmRpZ2ljZXJ0LmNvbTEhMB8GA1UEAxMYRGlnaUNlcnQgVHJ1c3RlZCBSb290 # IEc0MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAv+aQc2jeu+RdSjww # IjBpM+zCpyUuySE98orYWcLhKac9WKt2ms2uexuEDcQwH/MbpDgW61bGl20dq7J5 # 8soR0uRf1gU8Ug9SH8aeFaV+vp+pVxZZVXKvaJNwwrK6dZlqczKU0RBEEC7fgvMH # hOZ0O21x4i0MG+4g1ckgHWMpLc7sXk7Ik/ghYZs06wXGXuxbGrzryc/NrDRAX7F6 # Zu53yEioZldXn1RYjgwrt0+nMNlW7sp7XeOtyU9e5TXnMcvak17cjo+A2raRmECQ # ecN4x7axxLVqGDgDEI3Y1DekLgV9iPWCPhCRcKtVgkEy19sEcypukQF8IUzUvK4b # A3VdeGbZOjFEmjNAvwjXWkmkwuapoGfdpCe8oU85tRFYF/ckXEaPZPfBaYh2mHY9 # WV1CdoeJl2l6SPDgohIbZpp0yt5LHucOY67m1O+SkjqePdwA5EUlibaaRBkrfsCU # tNJhbesz2cXfSwQAzH0clcOP9yGyshG3u3/y1YxwLEFgqrFjGESVGnZifvaAsPvo # ZKYz0YkH4b235kOkGLimdwHhD5QMIR2yVCkliWzlDlJRR3S+Jqy2QXXeeqxfjT/J # vNNBERJb5RBQ6zHFynIWIgnffEx1P2PsIV/EIFFrb7GrhotPwtZFX50g/KEexcCP # orF+CiaZ9eRpL5gdLfXZqbId5RsCAwEAAaOCATowggE2MA8GA1UdEwEB/wQFMAMB # Af8wHQYDVR0OBBYEFOzX44LScV1kTN8uZz/nupiuHA9PMB8GA1UdIwQYMBaAFEXr # oq/0ksuCMS1Ri6enIZ3zbcgPMA4GA1UdDwEB/wQEAwIBhjB5BggrBgEFBQcBAQRt # MGswJAYIKwYBBQUHMAGGGGh0dHA6Ly9vY3NwLmRpZ2ljZXJ0LmNvbTBDBggrBgEF # BQcwAoY3aHR0cDovL2NhY2VydHMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0QXNzdXJl # ZElEUm9vdENBLmNydDBFBgNVHR8EPjA8MDqgOKA2hjRodHRwOi8vY3JsMy5kaWdp # Y2VydC5jb20vRGlnaUNlcnRBc3N1cmVkSURSb290Q0EuY3JsMBEGA1UdIAQKMAgw # BgYEVR0gADANBgkqhkiG9w0BAQwFAAOCAQEAcKC/Q1xV5zhfoKN0Gz22Ftf3v1cH # vZqsoYcs7IVeqRq7IviHGmlUIu2kiHdtvRoU9BNKei8ttzjv9P+Aufih9/Jy3iS8 # UgPITtAq3votVs/59PesMHqai7Je1M/RQ0SbQyHrlnKhSLSZy51PpwYDE3cnRNTn # f+hZqPC/Lwum6fI0POz3A8eHqNJMQBk1RmppVLC4oVaO7KTVPeix3P0c2PR3WlxU # jG/voVA9/HYJaISfb8rbII01YBwCA8sgsKxYoA5AY8WYIsGyWfVVa88nq2x2zm8j # LfR+cWojayL/ErhULSd+2DrZ8LaHlv1b0VysGMNNn3O3AamfV6peKOK5lDCCBq4w # ggSWoAMCAQICEAc2N7ckVHzYR6z9KGYqXlswDQYJKoZIhvcNAQELBQAwYjELMAkG # A1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZMBcGA1UECxMQd3d3LmRp # Z2ljZXJ0LmNvbTEhMB8GA1UEAxMYRGlnaUNlcnQgVHJ1c3RlZCBSb290IEc0MB4X # DTIyMDMyMzAwMDAwMFoXDTM3MDMyMjIzNTk1OVowYzELMAkGA1UEBhMCVVMxFzAV # BgNVBAoTDkRpZ2lDZXJ0LCBJbmMuMTswOQYDVQQDEzJEaWdpQ2VydCBUcnVzdGVk # IEc0IFJTQTQwOTYgU0hBMjU2IFRpbWVTdGFtcGluZyBDQTCCAiIwDQYJKoZIhvcN # AQEBBQADggIPADCCAgoCggIBAMaGNQZJs8E9cklRVcclA8TykTepl1Gh1tKD0Z5M # om2gsMyD+Vr2EaFEFUJfpIjzaPp985yJC3+dH54PMx9QEwsmc5Zt+FeoAn39Q7SE # 2hHxc7Gz7iuAhIoiGN/r2j3EF3+rGSs+QtxnjupRPfDWVtTnKC3r07G1decfBmWN # lCnT2exp39mQh0YAe9tEQYncfGpXevA3eZ9drMvohGS0UvJ2R/dhgxndX7RUCyFo # bjchu0CsX7LeSn3O9TkSZ+8OpWNs5KbFHc02DVzV5huowWR0QKfAcsW6Th+xtVhN # ef7Xj3OTrCw54qVI1vCwMROpVymWJy71h6aPTnYVVSZwmCZ/oBpHIEPjQ2OAe3Vu # JyWQmDo4EbP29p7mO1vsgd4iFNmCKseSv6De4z6ic/rnH1pslPJSlRErWHRAKKtz # Q87fSqEcazjFKfPKqpZzQmiftkaznTqj1QPgv/CiPMpC3BhIfxQ0z9JMq++bPf4O # uGQq+nUoJEHtQr8FnGZJUlD0UfM2SU2LINIsVzV5K6jzRWC8I41Y99xh3pP+OcD5 # sjClTNfpmEpYPtMDiP6zj9NeS3YSUZPJjAw7W4oiqMEmCPkUEBIDfV8ju2TjY+Cm # 4T72wnSyPx4JduyrXUZ14mCjWAkBKAAOhFTuzuldyF4wEr1GnrXTdrnSDmuZDNIz # tM2xAgMBAAGjggFdMIIBWTASBgNVHRMBAf8ECDAGAQH/AgEAMB0GA1UdDgQWBBS6 # FtltTYUvcyl2mi91jGogj57IbzAfBgNVHSMEGDAWgBTs1+OC0nFdZEzfLmc/57qY # rhwPTzAOBgNVHQ8BAf8EBAMCAYYwEwYDVR0lBAwwCgYIKwYBBQUHAwgwdwYIKwYB # BQUHAQEEazBpMCQGCCsGAQUFBzABhhhodHRwOi8vb2NzcC5kaWdpY2VydC5jb20w # QQYIKwYBBQUHMAKGNWh0dHA6Ly9jYWNlcnRzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2Vy # dFRydXN0ZWRSb290RzQuY3J0MEMGA1UdHwQ8MDowOKA2oDSGMmh0dHA6Ly9jcmwz # LmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydFRydXN0ZWRSb290RzQuY3JsMCAGA1UdIAQZ # MBcwCAYGZ4EMAQQCMAsGCWCGSAGG/WwHATANBgkqhkiG9w0BAQsFAAOCAgEAfVmO # wJO2b5ipRCIBfmbW2CFC4bAYLhBNE88wU86/GPvHUF3iSyn7cIoNqilp/GnBzx0H # 6T5gyNgL5Vxb122H+oQgJTQxZ822EpZvxFBMYh0MCIKoFr2pVs8Vc40BIiXOlWk/ # R3f7cnQU1/+rT4osequFzUNf7WC2qk+RZp4snuCKrOX9jLxkJodskr2dfNBwCnzv # qLx1T7pa96kQsl3p/yhUifDVinF2ZdrM8HKjI/rAJ4JErpknG6skHibBt94q6/ae # sXmZgaNWhqsKRcnfxI2g55j7+6adcq/Ex8HBanHZxhOACcS2n82HhyS7T6NJuXdm # kfFynOlLAlKnN36TU6w7HQhJD5TNOXrd/yVjmScsPT9rp/Fmw0HNT7ZAmyEhQNC3 # EyTN3B14OuSereU0cZLXJmvkOHOrpgFPvT87eK1MrfvElXvtCl8zOYdBeHo46Zzh # 3SP9HSjTx/no8Zhf+yvYfvJGnXUsHicsJttvFXseGYs2uJPU5vIXmVnKcPA3v5gA # 3yAWTyf7YGcWoWa63VXAOimGsJigK+2VQbc61RWYMbRiCQ8KvYHZE/6/pNHzV9m8 # BPqC3jLfBInwAM1dwvnQI38AC+R2AibZ8GV2QqYphwlHK+Z/GqSFD/yYlvZVVCsf # gPrA8g4r5db7qS9EFUrnEw4d2zc4GqEr9u3WfPwwggbAMIIEqKADAgECAhADyzT9 # Pf8SETOf8HxLIVfHMA0GCSqGSIb3DQEBCwUAMGMxCzAJBgNVBAYTAlVTMRcwFQYD # VQQKEw5EaWdpQ2VydCwgSW5jLjE7MDkGA1UEAxMyRGlnaUNlcnQgVHJ1c3RlZCBH # NCBSU0E0MDk2IFNIQTI1NiBUaW1lU3RhbXBpbmcgQ0EwHhcNMjIwODMwMDAwMDAw # WhcNMjMwODI5MjM1OTU5WjBGMQswCQYDVQQGEwJVUzERMA8GA1UEChMIRGlnaUNl # cnQxJDAiBgNVBAMTG0RpZ2lDZXJ0IFRpbWVzdGFtcCAyMDIyIC0gMjCCAiIwDQYJ # KoZIhvcNAQEBBQADggIPADCCAgoCggIBAM/spSY6xqnya7uNwQ2a26HoFIV0Mxom # rNAcVR4eNm28klUMYfSdCXc9FZYIL2tkpP0GgxbXkZI4HDEClvtysZc6Va8z7GGK # 6aYo25BjXL2JU+A6LYyHQq4mpOS7eHi5ehbhVsbAumRTuyoW51BIu4hpDIjG8b7g # L307scpTjUCDHufLckkoHkyAHoVW54Xt8mG8qjoHffarbuVm3eJc9S/tjdRNlYRo # 44DLannR0hCRRinrPibytIzNTLlmyLuqUDgN5YyUXRlav/V7QG5vFqianJVHhoV5 # PgxeZowaCiS+nKrSnLb3T254xCg/oxwPUAY3ugjZNaa1Htp4WB056PhMkRCWfk3h # 3cKtpX74LRsf7CtGGKMZ9jn39cFPcS6JAxGiS7uYv/pP5Hs27wZE5FX/NurlfDHn # 88JSxOYWe1p+pSVz28BqmSEtY+VZ9U0vkB8nt9KrFOU4ZodRCGv7U0M50GT6Vs/g # 9ArmFG1keLuY/ZTDcyHzL8IuINeBrNPxB9ThvdldS24xlCmL5kGkZZTAWOXlLimQ # prdhZPrZIGwYUWC6poEPCSVT8b876asHDmoHOWIZydaFfxPZjXnPYsXs4Xu5zGcT # B5rBeO3GiMiwbjJ5xwtZg43G7vUsfHuOy2SJ8bHEuOdTXl9V0n0ZKVkDTvpd6kVz # HIR+187i1Dp3AgMBAAGjggGLMIIBhzAOBgNVHQ8BAf8EBAMCB4AwDAYDVR0TAQH/ # BAIwADAWBgNVHSUBAf8EDDAKBggrBgEFBQcDCDAgBgNVHSAEGTAXMAgGBmeBDAEE # AjALBglghkgBhv1sBwEwHwYDVR0jBBgwFoAUuhbZbU2FL3MpdpovdYxqII+eyG8w # HQYDVR0OBBYEFGKK3tBh/I8xFO2XC809KpQU31KcMFoGA1UdHwRTMFEwT6BNoEuG # SWh0dHA6Ly9jcmwzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydFRydXN0ZWRHNFJTQTQw # OTZTSEEyNTZUaW1lU3RhbXBpbmdDQS5jcmwwgZAGCCsGAQUFBwEBBIGDMIGAMCQG # CCsGAQUFBzABhhhodHRwOi8vb2NzcC5kaWdpY2VydC5jb20wWAYIKwYBBQUHMAKG # TGh0dHA6Ly9jYWNlcnRzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydFRydXN0ZWRHNFJT # QTQwOTZTSEEyNTZUaW1lU3RhbXBpbmdDQS5jcnQwDQYJKoZIhvcNAQELBQADggIB # AC0UyaEGSS3dimxaHgXjrMnYnjeKsKYhIj9EyjE9ywwM33xT5ZRqdiX3Isk7nEIE # lPWCRN5u4oTo7k5EGGktx3ZsrHpzf0siEEmEdDfygtNBlXYxLvlZab8HVrslWfex # M+66XRCFK19PgSnudu0gC3XaxWbC6eAeWmgBTLRktDRpqbY9fj1d6REtuXxf4RNr # N0MDT+kVDdt1BVTHDTlfGDbA6HAXR1Vc+khF8cv4RMJ8vvP3p6z05qFttPe3RMWP # CC+d8hKtJI+2C3hBwdKChzJizkfq60Vrqqj+dEeBnrUYhUcYIIz6WeVYk72r/31a # 9SowYPuTzNCktU59LF6Y2/bMPIpHeHhsBAvg2RMxDzH4TfzgKkGM8F8VDpTAKUXe # 8vlzzsNjJ4m+oeGi72Kj6if/M07iiT4kMEQV5Fg8BotKdIqx7a1Cf+aqpZq5+DAc # FhPwo4uoKtSLAWY0aIACxRKSFqIHngiuc2t9n+vB/oM/rtlQNnnlt8E2hvC3yQl5 # +M/7sqzX4vI3BBv6ASmOsDaYOGrb90BA77kpxccgavKscb/UdmJ+yGZjMyuuUzjP # pKpGxMG95S9ATieDVuDFi68taSY81PJVmxBD/MrBbfTZ9JBLS5F1s0ecKEr6OOY1 # PvLIry+8TrgnFUP5KT019GjiRV2GVCOBx9aBB9M+oTliMYIFXTCCBVkCAQEwgYYw # cjELMAkGA1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZMBcGA1UECxMQ # d3d3LmRpZ2ljZXJ0LmNvbTExMC8GA1UEAxMoRGlnaUNlcnQgU0hBMiBBc3N1cmVk # IElEIENvZGUgU2lnbmluZyBDQQIQBNXcH0jqydhSALrNmpsqpzANBglghkgBZQME # AgEFAKCBhDAYBgorBgEEAYI3AgEMMQowCKACgAChAoAAMBkGCSqGSIb3DQEJAzEM # BgorBgEEAYI3AgEEMBwGCisGAQQBgjcCAQsxDjAMBgorBgEEAYI3AgEVMC8GCSqG # SIb3DQEJBDEiBCDvhcycG5lwhfXF/osi1JfVFh4hnV2pN2pW82wKbimsDjANBgkq # hkiG9w0BAQEFAASCAQB51DhA7e++eN6vmtD8xv+kTXzZjp3ieY+EjTxqFQXNWXCO # JEcSNzBQP43xpl6qdu0fTCm67e8ne14tm9v9X7T6o15XPE7rrBhTq20YVbSFzb7+ # htI5XvGoG7VuBCGNkES9HRi+r139OKy46eFHl/6Edf9U2qq9MMugfGTPiI/E7Eul # VtVrbTaB5tegFhgAkb1GTt0NrESAz27DxWqWoJvWUYgsQy2gbCBhboIbQJ6ULBdw # TSUNbllH3ta+htjAl5XdRoWUg2pQsy04es7sa9/4s6RzZUV+Kbh6gGnYZd1tQWKA # LIbkS9rr+LvZuJgP+zvN4hu7+e/v5kbP00AccDbQoYIDIDCCAxwGCSqGSIb3DQEJ # BjGCAw0wggMJAgEBMHcwYzELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDkRpZ2lDZXJ0 # LCBJbmMuMTswOQYDVQQDEzJEaWdpQ2VydCBUcnVzdGVkIEc0IFJTQTQwOTYgU0hB # MjU2IFRpbWVTdGFtcGluZyBDQQIQA8s0/T3/EhEzn/B8SyFXxzANBglghkgBZQME # AgEFAKBpMBgGCSqGSIb3DQEJAzELBgkqhkiG9w0BBwEwHAYJKoZIhvcNAQkFMQ8X # DTIyMDkyMjE4MTkzOVowLwYJKoZIhvcNAQkEMSIEIOOcxkhpoCksh4BASQcuWMCI # vwIpiOFWL/14l2pUEsR6MA0GCSqGSIb3DQEBAQUABIICAMjU20+qXijJ26Mg+Q9S # 6fzwms4MfUaC5TQ0ELXC9lLi0F4dPFTl/1sk+CVL8/jYAZgWJ64HC2M+2vetpLWL # eu+NqFmIuDre85dVrolFDD1mdT0+deMsnRWJd4V/FYOXrgY53HmslXxMMtCRmEId # Ut4/8jJaewhfTTytJePfLB4gMmgtQZOmN2BzTHuvZtBAfqsex96Yi8HEr2DuGFr6 # jqUCEDYEGzif86i7N4iVfNRBayPmQq3dKUbxsYc9RUDt9oSikkZ8xP1pO4S0z/ph # g20fXzbfRu6S7YTrC9veVxq7jDUPJjk4J+02JLuGpKOTrsXVDvdjDpvs7Qisv6vM # viOo5mHi5UgdORAj1wP1t6FmRG7BGmAZq0ZaTlVT8skATO4VWBRFldo7Id6Mo2Sp # B/OW9itPGjAZl+nUEEfBnzXyVy5n9sin/xu5DjW47ORjtZUyxTXdi6NJMYOJLx12 # zCNEogQk/6875zjqmiHtlLCgKtYX/k6nJp9XkgRIRYimsutCWFcKDyOxotRIqwIj # KSKWV2sIDfhsH1S4w+zPfoQ135QqIMoKYxZFsPCGose0AT7EJnzYAn/GrloGNdr2 # HbEWQHBuhap5GEjLBzyOLptMZtAPwayLlxa7VzQD4XjGxbl2OGgTNC/JUdQbBke+ # kHxj8o1Mu9iu0aweZn4zbq3C # SIG # End signature block |