+ $tmpZip = Join-Path $RsyncDir "download-$RsyncAsset"\r
+ Invoke-WebRequest -Uri $RsyncUrl -OutFile $tmpZip -UseBasicParsing\r
+ Write-Host " Downloaded $RsyncAsset ($([math]::Round((Get-Item $tmpZip).Length / 1MB, 2)) MB)"\r
+\r
+ # Verify against the .sha256 published beside it. Same origin, so this is an\r
+ # integrity check on the transfer rather than a defence against a hostile\r
+ # release - but a truncated or proxy-mangled download is the failure that\r
+ # actually happens, and it fails here instead of mid-transfer later.\r
+ #\r
+ # -OutFile, not .Content: GitHub serves the .sha256 as\r
+ # application/octet-stream, and Invoke-WebRequest hands back a byte[] rather\r
+ # than a string for any non-text content type, so .Content would compare the\r
+ # first BYTE against the hash and fail on every correct download.\r
+ $tmpSha = "$tmpZip.sha256"\r
+ Invoke-WebRequest -Uri "$RsyncUrl.sha256" -OutFile $tmpSha -UseBasicParsing\r
+ $want = (((Get-Content $tmpSha -Raw) -split '\s+')[0]).Trim().ToLower()\r
+ Remove-Item $tmpSha -Force -ErrorAction SilentlyContinue\r
+ $got = (Get-FileHash $tmpZip -Algorithm SHA256).Hash.ToLower()\r
+ if ($want -and $want -ne $got) {\r
+ Remove-Item $tmpZip -Force\r
+ throw "SHA-256 mismatch for ${RsyncAsset}: expected $want, got $got"\r
+ }\r
+ Write-Host " SHA-256 verified: $got"\r
+\r
+ # Unpack to a scratch directory and move out the files we asked for, rather\r
+ # than expanding straight over the install directory: the zip is the unit\r
+ # that was checksummed, and this way a future release adding something to it\r
+ # cannot quietly drop that something onto the machine PATH.\r
+ $unpack = Join-Path $RsyncDir '.unpack'\r
+ if (Test-Path $unpack) { Remove-Item -Recurse -Force $unpack }\r
+ Expand-Archive -Path $tmpZip -DestinationPath $unpack -Force\r
+ Remove-Item $tmpZip -Force\r
+ foreach ($f in 'rsync.exe', 'ssh.exe', 'COPYING.txt', 'NOTICE-ssh.txt') {\r
+ $src = Join-Path $unpack $f\r
+ if (-not (Test-Path $src)) { continue }\r
+ if ($f -eq 'ssh.exe' -and -not $WantSsh) { continue }\r
+ Move-Item -Path $src -Destination (Join-Path $RsyncDir $f) -Force\r
+ }\r
+ Remove-Item -Recurse -Force $unpack\r
+ Write-Host " Installed $RsyncExe$(if ($WantSsh) { ' and the ssh.exe it runs' })"\r