Support sftp:// and ftps:// URL schemes in host values; fix TLS-flag bug

CAMBS's partner turned out to receive insurance files over SFTP - Melissa
pasted "SFTP://sft.polluxsystems.com/" as ftps.host, which the engine fed to
WinSCP as a literal FTP hostname. Host values are now parsed: an sftp://
scheme switches the upload to the SFTP protocol (default port 22), ftps://
forces explicit TLS, a bare hostname behaves as before per the tls flag, and
the scheme/trailing slash are stripped from the hostname either way.

Also fixes a real bug this exposed: GiveUpSecurityAndAcceptAnyTlsHostCertificate
was set unconditionally, and WinSCP refuses that combination when FtpSecure
is None - exactly the "TlsHostCertificateFingerprint ... is set, but neither
FtpSecure nor Secure is enabled" error from her run. The flag is now only set
when TLS is actually on.
This commit is contained in:
2026-07-05 20:33:01 -05:00
parent 3105150341
commit 76dc830580
2 changed files with 30 additions and 8 deletions
+29 -7
View File
@@ -168,16 +168,37 @@ function Get-ArchiveDir($parent, $name) {
return $path
}
# Host values may be pasted as URLs, e.g. "SFTP://server.com/" - pull out the
# scheme (decides the protocol) and the bare hostname.
function Get-HostInfo($rawHost) {
$h = ([string]$rawHost).Trim()
$scheme = $null
if ($h -imatch '^(\w+)://') {
$scheme = $Matches[1].ToLower()
$h = $h -replace '^\w+://', ''
}
$h = $h.Split('/')[0]
return @{ Scheme = $scheme; HostName = $h }
}
# -- Upload via FTPS -----------------------------------------------------------
function Invoke-FTPSUpload($ftpConfig, $files, $remotePath, $entity, $practice) {
$hostInfo = Get-HostInfo $ftpConfig.host
$opts = New-Object WinSCP.SessionOptions
$opts.Protocol = [WinSCP.Protocol]::Ftp
$opts.FtpSecure = if ($ftpConfig.tls) { [WinSCP.FtpSecure]::Explicit } else { [WinSCP.FtpSecure]::None }
$opts.HostName = $ftpConfig.host
$opts.PortNumber = if ($ftpConfig.port) { [int]$ftpConfig.port } else { 21 }
if ($hostInfo.Scheme -eq "sftp") {
$opts.Protocol = [WinSCP.Protocol]::Sftp
$opts.PortNumber = if ($ftpConfig.port) { [int]$ftpConfig.port } else { 22 }
$opts.GiveUpSecurityAndAcceptAnySshHostKey = $true
} else {
$useTls = ($ftpConfig.tls -eq $true) -or ($hostInfo.Scheme -eq "ftps")
$opts.Protocol = [WinSCP.Protocol]::Ftp
$opts.FtpSecure = if ($useTls) { [WinSCP.FtpSecure]::Explicit } else { [WinSCP.FtpSecure]::None }
$opts.PortNumber = if ($ftpConfig.port) { [int]$ftpConfig.port } else { 21 }
if ($useTls) { $opts.GiveUpSecurityAndAcceptAnyTlsHostCertificate = $true }
}
$opts.HostName = $hostInfo.HostName
$opts.UserName = $ftpConfig.username
$opts.Password = $ftpConfig.password
$opts.GiveUpSecurityAndAcceptAnyTlsHostCertificate = $true
$session = New-Object WinSCP.Session
if ($logLevel -eq "debug") {
@@ -186,7 +207,7 @@ function Invoke-FTPSUpload($ftpConfig, $files, $remotePath, $entity, $practice)
$ok = 0; $fail = 0
try {
Write-Debug "FTPS connect: $($ftpConfig.host) user=$($ftpConfig.username) tls=$($ftpConfig.tls)"
Write-Debug "Connect: $($opts.Protocol) $($hostInfo.HostName):$($opts.PortNumber) user=$($ftpConfig.username)"
$session.Open($opts)
foreach ($file in $files) {
$remote = $remotePath.TrimEnd("/") + "/" + $file.Name
@@ -216,9 +237,10 @@ function Invoke-FTPSUpload($ftpConfig, $files, $remotePath, $entity, $practice)
# -- Upload via SFTP -----------------------------------------------------------
function Invoke-SFTPUpload($sftpConfig, $files, $remotePath, $entity, $practice) {
$hostInfo = Get-HostInfo $sftpConfig.host
$opts = New-Object WinSCP.SessionOptions
$opts.Protocol = [WinSCP.Protocol]::Sftp
$opts.HostName = $sftpConfig.host
$opts.HostName = $hostInfo.HostName
$opts.PortNumber = if ($sftpConfig.port) { [int]$sftpConfig.port } else { 22 }
$opts.UserName = $sftpConfig.username
$opts.Password = $sftpConfig.password