Downloader.ps1

. $PSScriptRoot\UserInterface.ps1
. $PSScriptRoot\SessionManager.ps1


Add-Type -TypeDefinition @"
    public enum ChecksumStatus
    {
        Skipped,
        Passed,
        Failed,
    }
"@



Function DownloadFiles {
    Param(
        [Parameter(Mandatory=$True)]
        [System.Collections.ArrayList]
        $Uris,

        [Parameter(Mandatory=$True)]
        [String]
        $DestinationFolder,

        [Switch]
        $WithChecksums,

        [Parameter(Mandatory=$True)]
        [Microsoft.PowerShell.Commands.WebRequestSession]
        $AuthenticatedSession
    )

    $OldPreference = $ProgressPreference
    $ProgressPreference = 'SilentlyContinue'
    $PadLeft = GetLeftNumberPadding -Count $Uris.Count

    $DownloadStats = [PSCustomObject]@{
        FilesFound =           [System.Collections.ArrayList]::new();
        FilesNotFound =        [System.Collections.ArrayList]::new();
        FilesChecksumPassed =  [System.Collections.ArrayList]::new();
        FilesChecksumFailed =  [System.Collections.ArrayList]::new();
        FilesChecksumSkipped = [System.Collections.ArrayList]::new();
    }

    $Count = 0
    ForEach ($PSObj in $Uris) {
        $Count += 1
        $ObjectUri = $PSObj.Uri
        $StringUri = [String] $ObjectUri
        $OutputFileName = $ObjectUri.Segments[$ObjectUri.Segments.Length - 1]
        $OutputFilePath = Join-Path -Path $DestinationFolder -ChildPath $OutputFileName
        $StringCount = ([String] $Count).PadLeft($PadLeft, ' ')
        Write-Host "$StringCount - $OutputFileName " -NoNewline

        $Response = DownloadSingleFile -Uri $ObjectUri `
                                       -WebSession $AuthenticatedSession `
                                       -OutFile $OutputFilePath

        If ($Response.Status -ne 200) {
            Write-Host "($($Reponse.Status) $($Response.Description))" -ForegroundColor Red
            $DownloadStats.FilesNotFound.Add($StringUri) | Out-Null
            If ($WithChecksums) {
                $DownloadStats.FilesChecksumSkipped.Add($StringUri) | Out-Null
            }
            Continue
        }

        $DownloadStats.FilesFound.Add($StringUri) | Out-Null

        $FileLength = GetHumanReadableSize -Bytes $Response.FileSize

        If ($WithChecksums) {
            $Status = VerifyChecksum -File $OutputFilePath `
                                     -Algorithm $PSObj.Algorithm `
                                     -Checksum $PSObj.Checksum

            Write-Host "($FileLength | " -NoNewline
            Switch($Status) {
                "Skipped" {
                    Write-Host 'EMPTY CHECKSUM - SKIPPED' -ForegroundColor Yellow -NoNewline
                    Write-Host ')'
                    $DownloadStats.FilesChecksumSkipped.Add($StringUri) | Out-Null
                    Break
                }
                "Passed" {
                    Write-Host 'CHECKSUM OK' -ForegroundColor Green -NoNewline
                    Write-Host ')'
                    $DownloadStats.FilesChecksumPassed.Add($StringUri) | Out-Null
                    Break
                }
                "Failed" {
                    Write-Host 'CHECKSUM FAILED' -ForegroundColor Red -NoNewline
                    Write-Host ')'
                    $DownloadStats.FilesChecksumFailed.Add($StringUri) | Out-Null
                    Break
                }
            }
        }
        Else {
            Write-Host "($FileLength)"
        }
    }
    $ProgressPreference = $OldPreference
    $DownloadStats
}

Function DownloadSingleFile {
    Param(
        [Parameter(Mandatory=$True)]
        [System.Uri]
        $Uri,

        [Parameter(Mandatory=$True)]
        [String]
        $OutFile,

        [Parameter(Mandatory=$True)]
        [Microsoft.PowerShell.Commands.WebRequestSession]
        $WebSession
    )
    $Status = 200
    $Description = 'OK'
    $FileSize = $Null

    Try {
        $Response = Invoke-WebRequest -Uri $Uri -WebSession $WebSession -OutFile $OutFile
        $FileSize = (Get-Item $OutFile).Length
    }
    Catch {
        If ($Response -or $_.Exception.Response) {
            If (-Not $Response) {
                $Response = $_.Exception.Response
            }
            $Result = ExtractStatusCodeAndDescription -Response $Response
            $Status = $Result.Code
            $Description = ([String] $Result.Description).ToUpper()
        }
        Else {
            $Status = '???'
            $Description = $_.Exception.Message
        }
    }

    [PSCustomObject] @{
        Status = $Status;
        Description = $Description;
        FileSize = $FileSize;
    }
}

Function VerifyChecksum {
    Param(
        [Parameter(Mandatory=$True)]
        [String]
        $File,

        [AllowEmptyString()]
        [AllowNull()]
        [String]
        $Checksum,

        [AllowEmptyString()]
        [AllowNull()]
        [String]
        $Algorithm
    )

    If (-Not $Checksum) {
        Return [ChecksumStatus]::Skipped
    }

    $ComputedHash = (Get-FileHash -Path $OutputFilePath -Algorithm $Algorithm).Hash.ToLower()
    If ($ComputedHash -eq $Checksum) {
        Return [ChecksumStatus]::Passed
    }
    Else {
        Return [ChecksumStatus]::Failed
    }
}