FidoScript.ps1
<#PSScriptInfo .VERSION 1.0.0 .GUID 584fa6de-f040-4397-a49b-9d40b430986c .AUTHOR Jimmy Briggs <jimmy.briggs@jimbrig.com> .COMPANYNAME Jimmy Briggs .COPYRIGHT Jimmy Briggs | 2024 .TAGS Windows, UEFI, ISO, PowerShell, Script, Download .LICENSEURI https://github.com/pbatard/Fido/blob/master/LICENSE.txt .PROJECTURI https://github.com/pbatard/Fido/ .ICONURI .EXTERNALMODULEDEPENDENCIES .REQUIREDSCRIPTS .EXTERNALSCRIPTDEPENDENCIES .RELEASENOTES - Version 1.0.0 - Initial release of FidoScript.ps1 .PRIVATEDATA #> <# .DESCRIPTION A PowerShell download script for Microsoft Windows and UEFI Shell ISOs. #> Param( # (Optional) The title to display on the application window. [string]$AppTitle = 'Fido - Feature ISO Downloader', # (Optional) '|' separated UI localization strings. [string]$LocData, # (Optional) Forced locale [string]$Locale = 'en-US', # (Optional) Path to a file that should be used for the UI icon. [string]$Icon, # (Optional) Name of a pipe the download URL should be sent to. # If not provided, a browser window is opened instead. [string]$PipeName, # (Optional) Specify Windows version (e.g. "Windows 10") [Toggles commandline mode] [string]$Win, # (Optional) Specify Windows release (e.g. "21H1") [Toggles commandline mode] [string]$Rel, # (Optional) Specify Windows edition (e.g. "Pro") [Toggles commandline mode] [string]$Ed, # (Optional) Specify Windows language [Toggles commandline mode] [string]$Lang, # (Optional) Specify Windows architecture [Toggles commandline mode] [string]$Arch, # (Optional) Only display the download URL [Toggles commandline mode] [switch]$GetUrl = $false, # (Optional) Increase verbosity [switch]$Verbose = $false ) try { [Console]::OutputEncoding = [System.Text.Encoding]::UTF8 } catch {} $Cmd = $false if ($Win -or $Rel -or $Ed -or $Lang -or $Arch -or $GetUrl) { $Cmd = $true } # Return a decimal Windows version that we can then check for platform support. # Note that because we don't want to have to support this script on anything # other than Windows, this call returns 0.0 for PowerShell running on Linux/Mac. function Get-Platform-Version() { $version = 0.0 $platform = [string][System.Environment]::OSVersion.Platform # This will filter out non Windows platforms if ($platform.StartsWith('Win')) { # Craft a decimal numeric version of Windows $version = [System.Environment]::OSVersion.Version.Major * 1.0 + [System.Environment]::OSVersion.Version.Minor * 0.1 } return $version } $winver = Get-Platform-Version # The default TLS for Windows 8.x doesn't work with Microsoft's servers so we must force it if ($winver -lt 10.0) { [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls -bor [Net.SecurityProtocolType]::Tls11 -bor [Net.SecurityProtocolType]::Tls12 } #region Assembly Types $Drawing_Assembly = 'System.Drawing' # PowerShell 7 altered the name of the Drawing assembly... if ($host.version -ge '7.0') { $Drawing_Assembly += '.Common' } $Signature = @{ Namespace = 'WinAPI' Name = 'Utils' Language = 'CSharp' UsingNamespace = 'System.Runtime', 'System.IO', 'System.Text', 'System.Drawing', 'System.Globalization' ReferencedAssemblies = $Drawing_Assembly ErrorAction = 'Stop' WarningAction = 'Ignore' IgnoreWarnings = $true MemberDefinition = @' [DllImport("shell32.dll", CharSet = CharSet.Auto, SetLastError = true, BestFitMapping = false, ThrowOnUnmappableChar = true)] internal static extern int ExtractIconEx(string sFile, int iIndex, out IntPtr piLargeVersion, out IntPtr piSmallVersion, int amountIcons); [DllImport("user32.dll")] public static extern bool ShowWindow(IntPtr handle, int state); // Extract an icon from a DLL public static Icon ExtractIcon(string file, int number, bool largeIcon) { IntPtr large, small; ExtractIconEx(file, number, out large, out small, 1); try { return Icon.FromHandle(largeIcon ? large : small); } catch { return null; } } '@ } if (!$Cmd) { Write-Host Please Wait... if (!('WinAPI.Utils' -as [type])) { Add-Type @Signature } Add-Type -AssemblyName PresentationFramework # Hide the powershell window: https://stackoverflow.com/a/27992426/1069307 [WinAPI.Utils]::ShowWindow(([System.Diagnostics.Process]::GetCurrentProcess() | Get-Process).MainWindowHandle, 0) | Out-Null } #endregion #region Data $zh = 0x10000 $ko = 0x20000 $WindowsVersions = @( @( @('Windows 11', 'windows11'), @( '23H2 v2 (Build 22631.2861 - 2023.12)', @('Windows 11 Home/Pro/Edu', 2935), @('Windows 11 Home China ', ($zh + 2936)) ) ), @( @('Windows 10', 'Windows10ISO'), @( '22H2 v1 (Build 19045.2965 - 2023.05)', @('Windows 10 Home/Pro/Edu', 2618), @('Windows 10 Home China ', ($zh + 2378)) ) ) @( @('Windows 8.1', 'windows8ISO'), @( 'Update 3 (build 9600)', @('Windows 8.1 Standard', 52), @('Windows 8.1 N', 55) @('Windows 8.1 Single Language', 48), @('Windows 8.1 K', ($ko + 61)), @('Windows 8.1 KN', ($ko + 62)) ) ), @( @('UEFI Shell 2.2', 'UEFI_SHELL 2.2'), @( '23H2 (edk2-stable202311)', @('Release', 0), @('Debug', 1) ), @( '23H1 (edk2-stable202305)', @('Release', 0), @('Debug', 1) ), @( '22H2 (edk2-stable202211)', @('Release', 0), @('Debug', 1) ), @( '22H1 (edk2-stable202205)', @('Release', 0), @('Debug', 1) ), @( '21H2 (edk2-stable202108)', @('Release', 0), @('Debug', 1) ), @( '21H1 (edk2-stable202105)', @('Release', 0), @('Debug', 1) ), @( '20H2 (edk2-stable202011)', @('Release', 0), @('Debug', 1) ) ), @( @('UEFI Shell 2.0', 'UEFI_SHELL 2.0'), @( '4.632 [20100426]', @('Release', 0) ) ) ) #endregion #region Functions function Select-Language([string]$LangName) { # Use the system locale to try select the most appropriate language [string]$SysLocale = [System.Globalization.CultureInfo]::CurrentUICulture.Name if (($SysLocale.StartsWith('ar') -and $LangName -like '*Arabic*') -or ` ($SysLocale -eq 'pt-BR' -and $LangName -like '*Brazil*') -or ` ($SysLocale.StartsWith('ar') -and $LangName -like '*Bulgar*') -or ` ($SysLocale -eq 'zh-CN' -and $LangName -like '*Chinese*' -and $LangName -like '*simp*') -or ` ($SysLocale -eq 'zh-TW' -and $LangName -like '*Chinese*' -and $LangName -like '*trad*') -or ` ($SysLocale.StartsWith('hr') -and $LangName -like '*Croat*') -or ` ($SysLocale.StartsWith('cz') -and $LangName -like '*Czech*') -or ` ($SysLocale.StartsWith('da') -and $LangName -like '*Danish*') -or ` ($SysLocale.StartsWith('nl') -and $LangName -like '*Dutch*') -or ` ($SysLocale -eq 'en-US' -and $LangName -eq 'English') -or ` ($SysLocale.StartsWith('en') -and $LangName -like '*English*' -and ($LangName -like '*inter*' -or $LangName -like '*ingdom*')) -or ` ($SysLocale.StartsWith('et') -and $LangName -like '*Eston*') -or ` ($SysLocale.StartsWith('fi') -and $LangName -like '*Finn*') -or ` ($SysLocale -eq 'fr-CA' -and $LangName -like '*French*' -and $LangName -like '*Canad*') -or ` ($SysLocale.StartsWith('fr') -and $LangName -eq 'French') -or ` ($SysLocale.StartsWith('de') -and $LangName -like '*German*') -or ` ($SysLocale.StartsWith('el') -and $LangName -like '*Greek*') -or ` ($SysLocale.StartsWith('he') -and $LangName -like '*Hebrew*') -or ` ($SysLocale.StartsWith('hu') -and $LangName -like '*Hungar*') -or ` ($SysLocale.StartsWith('id') -and $LangName -like '*Indones*') -or ` ($SysLocale.StartsWith('it') -and $LangName -like '*Italia*') -or ` ($SysLocale.StartsWith('ja') -and $LangName -like '*Japan*') -or ` ($SysLocale.StartsWith('ko') -and $LangName -like '*Korea*') -or ` ($SysLocale.StartsWith('lv') -and $LangName -like '*Latvia*') -or ` ($SysLocale.StartsWith('lt') -and $LangName -like '*Lithuania*') -or ` ($SysLocale.StartsWith('ms') -and $LangName -like '*Malay*') -or ` ($SysLocale.StartsWith('nb') -and $LangName -like '*Norw*') -or ` ($SysLocale.StartsWith('fa') -and $LangName -like '*Persia*') -or ` ($SysLocale.StartsWith('pl') -and $LangName -like '*Polish*') -or ` ($SysLocale -eq 'pt-PT' -and $LangName -eq 'Portuguese') -or ` ($SysLocale.StartsWith('ro') -and $LangName -like '*Romania*') -or ` ($SysLocale.StartsWith('ru') -and $LangName -like '*Russia*') -or ` ($SysLocale.StartsWith('sr') -and $LangName -like '*Serbia*') -or ` ($SysLocale.StartsWith('sk') -and $LangName -like '*Slovak*') -or ` ($SysLocale.StartsWith('sl') -and $LangName -like '*Slovenia*') -or ` ($SysLocale -eq 'es-ES' -and $LangName -eq 'Spanish') -or ` ($SysLocale.StartsWith('es') -and $Locale -ne 'es-ES' -and $LangName -like '*Spanish*') -or ` ($SysLocale.StartsWith('sv') -and $LangName -like '*Swed*') -or ` ($SysLocale.StartsWith('th') -and $LangName -like '*Thai*') -or ` ($SysLocale.StartsWith('tr') -and $LangName -like '*Turk*') -or ` ($SysLocale.StartsWith('uk') -and $LangName -like '*Ukrain*') -or ` ($SysLocale.StartsWith('vi') -and $LangName -like '*Vietnam*')) { return $true } return $false } function Add-Entry([int]$pos, [string]$Name, [array]$Items, [string]$DisplayName) { $Title = New-Object System.Windows.Controls.TextBlock $Title.FontSize = $WindowsVersionTitle.FontSize $Title.Height = $WindowsVersionTitle.Height; $Title.Width = $WindowsVersionTitle.Width; $Title.HorizontalAlignment = 'Left' $Title.VerticalAlignment = 'Top' $Margin = $WindowsVersionTitle.Margin $Margin.Top += $pos * $dh $Title.Margin = $Margin $Title.Text = Get-Translation($Name) $XMLGrid.Children.Insert(2 * $Stage + 2, $Title) $Combo = New-Object System.Windows.Controls.ComboBox $Combo.FontSize = $WindowsVersion.FontSize $Combo.Height = $WindowsVersion.Height; $Combo.Width = $WindowsVersion.Width; $Combo.HorizontalAlignment = 'Left' $Combo.VerticalAlignment = 'Top' $Margin = $WindowsVersion.Margin $Margin.Top += $pos * $script:dh $Combo.Margin = $Margin $Combo.SelectedIndex = 0 if ($Items) { $Combo.ItemsSource = $Items if ($DisplayName) { $Combo.DisplayMemberPath = $DisplayName } else { $Combo.DisplayMemberPath = $Name } } $XMLGrid.Children.Insert(2 * $Stage + 3, $Combo) $XMLForm.Height += $dh; $Margin = $Continue.Margin $Margin.Top += $dh $Continue.Margin = $Margin $Margin = $Back.Margin $Margin.Top += $dh $Back.Margin = $Margin return $Combo } function Refresh-Control([object]$Control) { $Control.Dispatcher.Invoke('Render', [Windows.Input.InputEventHandler] { $Continue.UpdateLayout() }, $null, $null) | Out-Null } function Send-Message([string]$PipeName, [string]$Message) { [System.Text.Encoding]$Encoding = [System.Text.Encoding]::UTF8 $Pipe = New-Object -TypeName System.IO.Pipes.NamedPipeClientStream -ArgumentList '.', $PipeName, ([System.IO.Pipes.PipeDirection]::Out), ([System.IO.Pipes.PipeOptions]::None), ([System.Security.Principal.TokenImpersonationLevel]::Impersonation) try { $Pipe.Connect(1000) } catch { Write-Host $_.Exception.Message } $bRequest = $Encoding.GetBytes($Message) $cbRequest = $bRequest.Length; $Pipe.Write($bRequest, 0, $cbRequest); $Pipe.Dispose() } # From https://www.powershellgallery.com/packages/IconForGUI/1.5.2 # Copyright © 2016 Chris Carter. All rights reserved. # License: https://creativecommons.org/licenses/by-sa/4.0/ function ConvertTo-ImageSource { [CmdletBinding()] Param( [Parameter(Mandatory = $true, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)] [System.Drawing.Icon]$Icon ) Process { foreach ($i in $Icon) { [System.Windows.Interop.Imaging]::CreateBitmapSourceFromHIcon( $i.Handle, (New-Object System.Windows.Int32Rect -Args 0, 0, $i.Width, $i.Height), [System.Windows.Media.Imaging.BitmapSizeOptions]::FromEmptyOptions() ) } } } function Throw-Error([object]$Req, [string]$Alt) { $Err = $(GetElementById -Request $Req -Id 'errorModalMessage').innerText -replace '<[^>]+>' -replace '\s+', ' ' if (!$Err) { $Err = $Alt } else { $Err = [System.Text.Encoding]::UTF8.GetString([byte[]][char[]]$Err) } throw $Err } # Translate a message string function Get-Translation([string]$Text) { if (!($English -contains $Text)) { Write-Host "Error: '$Text' is not a translatable string" return '(Untranslated)' } if ($Localized) { if ($Localized.Length -ne $English.Length) { Write-Host "Error: '$Text' is not a translatable string" } for ($i = 0; $i -lt $English.Length; $i++) { if ($English[$i] -eq $Text) { if ($Localized[$i]) { return $Localized[$i] } else { return $Text } } } } return $Text } # Some PowerShells don't have Microsoft.mshtml assembly (comes with MS Office?) # so we can't use ParsedHtml or IHTMLDocument[2|3] features there... function GetElementById([object]$Request, [string]$Id) { try { return $Request.ParsedHtml.IHTMLDocument3_GetElementByID($Id) } catch { return $Request.AllElements | Where-Object { $_.id -eq $Id } } } function Error([string]$ErrorMessage) { Write-Host Error: $ErrorMessage if (!$Cmd) { $XMLForm.Title = $(Get-Translation('Error')) + ': ' + $ErrorMessage Refresh-Control($XMLForm) $XMLGrid.Children[2 * $script:Stage + 1].IsEnabled = $true $UserInput = [System.Windows.MessageBox]::Show($XMLForm.Title, $(Get-Translation('Error')), 'OK', 'Error') $script:ExitCode = $script:Stage-- } else { $script:ExitCode = 2 } } function Get-RandomDate() { [DateTime]$Min = '1/1/2008' [DateTime]$Max = [DateTime]::Now $RandomGen = New-Object random $RandomTicks = [Convert]::ToInt64( ($Max.ticks * 1.0 - $Min.Ticks * 1.0 ) * $RandomGen.NextDouble() + $Min.Ticks * 1.0 ) $Date = New-Object DateTime($RandomTicks) return $Date.ToString('yyyyMMdd') } #endregion #region Form [xml]$XAML = @' <Window xmlns = "http://schemas.microsoft.com/winfx/2006/xaml/presentation" Height = "162" Width = "384" ResizeMode = "NoResize"> <Grid Name = "XMLGrid"> <Button Name = "Continue" FontSize = "16" Height = "26" Width = "160" HorizontalAlignment = "Left" VerticalAlignment = "Top" Margin = "14,78,0,0"/> <Button Name = "Back" FontSize = "16" Height = "26" Width = "160" HorizontalAlignment = "Left" VerticalAlignment = "Top" Margin = "194,78,0,0"/> <TextBlock Name = "WindowsVersionTitle" FontSize = "16" Width="340" HorizontalAlignment="Left" VerticalAlignment="Top" Margin="16,8,0,0"/> <ComboBox Name = "WindowsVersion" FontSize = "14" Height = "24" Width = "340" HorizontalAlignment = "Left" VerticalAlignment="Top" Margin = "14,34,0,0" SelectedIndex = "0"/> <CheckBox Name = "Check" FontSize = "14" Width = "340" HorizontalAlignment = "Left" VerticalAlignment="Top" Margin = "14,0,0,0" Visibility="Collapsed" /> </Grid> </Window> '@ #endregion #region Globals $ErrorActionPreference = 'Stop' $DefaultTimeout = 30 $dh = 58 $Stage = 0 $SelectedIndex = 0 $ltrm = '‎' if ($Cmd) { $ltrm = '' } $MaxStage = 4 $SessionId = [guid]::NewGuid() $ExitCode = 100 $Locale = $Locale $RequestData = @{} # This GUID applies to all visitors, regardless of their locale $RequestData['GetLangs'] = @('a8f8f489-4c7f-463a-9ca6-5cff94d8d041', 'getskuinformationbyproductedition' ) # This GUID applies to visitors of the en-US download page. Other locales may get a different GUID. $RequestData['GetLinks'] = @('6e2a1789-ef16-4f27-a296-74ef7ef5d96b', 'GetProductDownloadLinksBySku' ) # Create a semi-random Linux User-Agent string $FirefoxVersion = Get-Random -Minimum 90 -Maximum 110 $FirefoxDate = Get-RandomDate $UserAgent = "Mozilla/5.0 (X11; Linux i586; rv:$FirefoxVersion.0) Gecko/$FirefoxDate Firefox/$FirefoxVersion.0" $Verbosity = 2 if ($Cmd) { if ($GetUrl) { $Verbosity = 0 } elseif (!$Verbose) { $Verbosity = 1 } } #endregion # Localization $EnglishMessages = 'en-US|Version|Release|Edition|Language|Architecture|Download|Continue|Back|Close|Cancel|Error|Please wait...|' + 'Download using a browser|Download of Windows ISOs is unavailable due to Microsoft having altered their website to prevent it.|' + 'PowerShell 3.0 or later is required to run this script.|Do you want to go online and download it?|' + 'This feature is not available on this platform.' [string[]]$English = $EnglishMessages.Split('|') [string[]]$Localized = $null if ($LocData -and !$LocData.StartsWith('en-US')) { $Localized = $LocData.Split('|') # Adjust the $Localized array if we have more or fewer strings than in $EnglishMessages if ($Localized.Length -lt $English.Length) { while ($Localized.Length -ne $English.Length) { $Localized += $English[$Localized.Length] } } elseif ($Localized.Length -gt $English.Length) { $Localized = $LocData.Split('|')[0..($English.Length - 1)] } $Locale = $Localized[0] } $QueryLocale = $Locale # Convert a size in bytes to a human readable string function Size-To-Human-Readable([uint64]$size) { $suffix = 'bytes', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB' $i = 0 while ($size -gt 1kb) { $size = $size / 1kb $i++ } '{0:N1} {1}' -f $size, $suffix[$i] } # Check if the locale we want is available - Fall back to en-US otherwise function Check-Locale { try { $url = 'https://www.microsoft.com/' + $QueryLocale + '/software-download/' if ($Verbosity -ge 2) { Write-Host Querying $url } # Looks Microsoft are filtering our script according to the first query it performs with the spoofed user agent. # So, to continue this pointless cat and mouse game, we simply add an extra first query with the default user agent. # Also: "Hi Microsoft. You sure have A LOT OF RESOURCES TO WASTE to have assigned folks of yours to cripple scripts # that merely exist because you have chosen to make the user experience from your download website utterly subpar. # And while I am glad senpai noticed me (UwU), I feel compelled to ask: Don't you guys have better things to do?" curl -UseBasicParsing -TimeoutSec $DefaultTimeout -MaximumRedirection 0 $url | Out-Null curl -UseBasicParsing -TimeoutSec $DefaultTimeout -MaximumRedirection 0 -UserAgent $UserAgent $url | Out-Null } catch { # Of course PowerShell 7 had to BREAK $_.Exception.Status on timeouts... if ($_.Exception.Status -eq 'Timeout' -or $_.Exception.GetType().Name -eq 'TaskCanceledException') { Write-Host Operation Timed out } $script:QueryLocale = 'en-US' } } # Return an array of releases (e.g. 20H2, 21H1, ...) for the selected Windows version function Get-Windows-Releases([int]$SelectedVersion) { $i = 0 $releases = @() foreach ($version in $WindowsVersions[$SelectedVersion]) { if (($i -ne 0) -and ($version -is [array])) { $releases += @(New-Object PsObject -Property @{ Release = $ltrm + $version[0].Replace(')', ')' + $ltrm); Index = $i }) } $i++ } return $releases } # Return an array of editions (e.g. Home, Pro, etc) for the selected Windows release function Get-Windows-Editions([int]$SelectedVersion, [int]$SelectedRelease) { $editions = @() foreach ($release in $WindowsVersions[$SelectedVersion][$SelectedRelease]) { if ($release -is [array]) { if (($release[1] -lt 0x10000) -or ($Locale.StartsWith('ko') -and ($release[1] -band $ko)) -or ($Locale.StartsWith('zh') -and ($release[1] -band $zh))) { $editions += @(New-Object PsObject -Property @{ Edition = $release[0]; Id = $($release[1] -band 0xFFFF) }) } } } return $editions } # Return an array of languages for the selected edition function Get-Windows-Languages([int]$SelectedVersion, [int]$SelectedEdition) { $languages = @() $i = 0; if ($WindowsVersions[$SelectedVersion][0][1] -eq 'WIN7') { foreach ($entry in $Windows7Versions[$SelectedEdition]) { if ($entry[0] -ne '') { $languages += @(New-Object PsObject -Property @{ DisplayLanguage = $entry[0]; Language = $entry[1]; Id = $i }) } $i++ } } elseif ($WindowsVersions[$SelectedVersion][0][1].StartsWith('UEFI_SHELL')) { $languages += @(New-Object PsObject -Property @{ DisplayLanguage = 'English (US)'; Language = 'en-us'; Id = 0 }) } else { # Microsoft download protection now requires the sessionId to be whitelisted through vlscppe.microsoft.com/tags $url = 'https://vlscppe.microsoft.com/tags?org_id=y6jn8c31&session_id=' + $SessionId if ($Verbosity -ge 2) { Write-Host Querying $url } try { curl -UseBasicParsing -TimeoutSec $DefaultTimeout -MaximumRedirection 0 -UserAgent $UserAgent $url | Out-Null } catch { Get-Error($_.Exception.Message) return @() } $url = 'https://www.microsoft.com/' + $QueryLocale + '/api/controls/contentinclude/html' $url += '?pageId=' + $RequestData['GetLangs'][0] $url += '&host=www.microsoft.com' $url += '&segments=software-download,' + $WindowsVersions[$SelectedVersion][0][1] $url += '&query=&action=' + $RequestData['GetLangs'][1] $url += '&sessionId=' + $SessionId $url += '&productEditionId=' + [Math]::Abs($SelectedEdition) $url += '&sdVersion=2' if ($Verbosity -ge 2) { Write-Host Querying $url } $script:SelectedIndex = 0 try { $r = curl -Method Post -UseBasicParsing -TimeoutSec $DefaultTimeout -UserAgent $UserAgent -SessionVariable 'Session' $url if ($r -match 'errorModalMessage') { Throw-Error -Req $r -Alt 'Could not retrieve languages from server' } $r = $r -replace "`n" -replace "`r" $pattern = '.*<select id="product-languages"[^>]*>(.*)</select>.*' $html = [regex]::Match($r, $pattern).Groups[1].Value # Go through an XML conversion to keep all PowerShells happy... $html = $html.Replace('selected value', 'value') $html = '<options>' + $html + '</options>' $xml = [xml]$html foreach ($var in $xml.options.option) { $json = $var.value | ConvertFrom-Json; if ($json) { $languages += @(New-Object PsObject -Property @{ DisplayLanguage = $var.InnerText; Language = $json.language; Id = $json.id }) if (Select-Language($json.language)) { $script:SelectedIndex = $i } $i++ } } if ($languages.Length -eq 0) { Throw-Error -Req $r -Alt 'Could not parse languages' } } catch { Get-Error($_.Exception.Message) return @() } } return $languages } # Return an array of download links for each supported arch function Get-Windows-Download-Links([int]$SelectedVersion, [int]$SelectedRelease, [int]$SelectedEdition, [string]$SkuId, [string]$LanguageName) { $links = @() if ($WindowsVersions[$SelectedVersion][0][1] -eq 'WIN7') { foreach ($Version in $Windows7Versions[$SelectedEdition][$SkuId][2]) { $links += @(New-Object PsObject -Property @{ Type = $Version[0]; Link = $Version[1] }) } } elseif ($WindowsVersions[$SelectedVersion][0][1].StartsWith('UEFI_SHELL')) { $tag = $WindowsVersions[$SelectedVersion][$SelectedRelease][0].Split(' ')[0] $shell_version = $WindowsVersions[$SelectedVersion][0][1].Split(' ')[1] $url = 'https://github.com/pbatard/UEFI-Shell/releases/download/' + $tag $link = $url + '/UEFI-Shell-' + $shell_version + '-' + $tag if ($SelectedEdition -eq 0) { $link += '-RELEASE.iso' } else { $link += '-DEBUG.iso' } try { # Read the supported archs from the release URL $url += '/Version.xml' $xml = New-Object System.Xml.XmlDocument if ($Verbosity -ge 2) { Write-Host Querying $url } $xml.Load($url) $sep = '' $archs = '' foreach ($arch in $xml.release.supported_archs.arch) { $archs += $sep + $arch $sep = ', ' } $links += @(New-Object PsObject -Property @{ Type = $archs; Link = $link }) } catch { Get-Error($_.Exception.Message) return @() } } else { $url = 'https://www.microsoft.com/' + $QueryLocale + '/api/controls/contentinclude/html' $url += '?pageId=' + $RequestData['GetLinks'][0] $url += '&host=www.microsoft.com' $url += '&segments=software-download,' + $WindowsVersions[$SelectedVersion][0][1] $url += '&query=&action=' + $RequestData['GetLinks'][1] $url += '&sessionId=' + $SessionId $url += '&skuId=' + $SkuId $url += '&language=' + $LanguageName $url += '&sdVersion=2' if ($Verbosity -ge 2) { Write-Host Querying $url } $i = 0 $script:SelectedIndex = 0 try { $Is64 = [Environment]::Is64BitOperatingSystem # Must add a referer for this request, else Microsoft's servers will deny it $ref = 'https://www.microsoft.com/software-download/windows11' $r = curl -Method Post -Headers @{ 'Referer' = $ref } -UseBasicParsing -TimeoutSec $DefaultTimeout -UserAgent $UserAgent -WebSession $Session $url if ($r -match 'errorModalMessage') { $Alt = [regex]::Match($r.Content, '<p id="errorModalMessage">(.+?)<\/p>').Groups[1].Value -replace '<[^>]+>' -replace '\s+', ' ' -replace '\?\?\?', '-' $Alt = [System.Text.Encoding]::UTF8.GetString([byte[]][char[]]$Alt) if (!$Alt) { $Alt = 'Could not retrieve architectures from server' } elseif ($Alt -match '715-123130') { $Alt += ' ' + $SessionId + '.' } Throw-Error -Req $r -Alt $Alt } $pattern = '(?s)(<input.*?></input>)' ForEach-Object { [regex]::Matches($r, $pattern) } | ForEach-Object { $html += $_.Groups[1].value } # Need to fix the HTML and JSON data so that it is well-formed $html = $html.Replace('class=product-download-hidden', '') $html = $html.Replace('type=hidden', '') $html = $html.Replace(' ', ' ') $html = $html.Replace('IsoX86', '"x86"') $html = $html.Replace('IsoX64', '"x64"') $html = '<inputs>' + $html + '</inputs>' $xml = [xml]$html foreach ($var in $xml.inputs.input) { $json = $var.value | ConvertFrom-Json; if ($json) { if (($Is64 -and $json.DownloadType -eq 'x64') -or (!$Is64 -and $json.DownloadType -eq 'x86')) { $script:SelectedIndex = $i } $links += @(New-Object PsObject -Property @{ Type = $json.DownloadType; Link = $json.Uri }) $i++ } } if ($links.Length -eq 0) { Throw-Error -Req $r -Alt 'Could not retrieve ISO download links' } } catch { Get-Error($_.Exception.Message) return @() } } return $links } # Process the download URL by either sending it through the pipe or by opening the browser function Process-Download-Link([string]$Url) { try { if ($PipeName -and !$Check.IsChecked) { Send-Message -PipeName $PipeName -Message $Url } else { if ($Cmd) { $pattern = '.*\/(.*\.iso).*' $File = [regex]::Match($Url, $pattern).Groups[1].Value # PowerShell implicit conversions are iffy, so we need to force them... $str_size = (curl -UseBasicParsing -TimeoutSec $DefaultTimeout -Uri $Url -Method Head).Headers.'Content-Length' $tmp_size = [uint64]::Parse($str_size) $Size = Size-To-Human-Readable $tmp_size Write-Host "Downloading '$File' ($Size)..." Start-BitsTransfer -Source $Url -Destination $File } else { Write-Host Download Link: $Url Start-Process -FilePath $Url } } } catch { Get-Error($_.Exception.Message) return 404 } return 0 } if ($Cmd) { $winVersionId = $null $winReleaseId = $null $winEditionId = $null $winLanguageId = $null $winLanguageName = $null $winLink = $null # Windows 7 and non Windows platforms are too much of a liability if ($winver -le 6.1) { Get-Error(Get-Translation('This feature is not available on this platform.')) exit 403 } $i = 0 $Selected = '' if ($Win -eq 'List') { Write-Host 'Please select a Windows Version (-Win):' } foreach ($version in $WindowsVersions) { if ($Win -eq 'List') { Write-Host ' -' $version[0][0] } elseif ($version[0][0] -match $Win) { $Selected += $version[0][0] $winVersionId = $i break; } $i++ } if ($winVersionId -eq $null) { if ($Win -ne 'List') { Write-Host 'Invalid Windows version provided.' Write-Host "Use '-Win List' for a list of available Windows versions." } exit 1 } # Windows Version selection $releases = Get-Windows-Releases $winVersionId if ($Rel -eq 'List') { Write-Host "Please select a Windows Release (-Rel) for ${Selected} (or use 'Latest' for most recent):" } foreach ($release in $releases) { if ($Rel -eq 'List') { Write-Host ' -' $release.Release } elseif (!$Rel -or $release.Release.StartsWith($Rel) -or $Rel -eq 'Latest') { if (!$Rel -and $Verbosity -ge 1) { Write-Host "No release specified (-Rel). Defaulting to '$($release.Release)'." } $Selected += ' ' + $release.Release $winReleaseId = $release.Index break; } } if ($winReleaseId -eq $null) { if ($Rel -ne 'List') { Write-Host 'Invalid Windows release provided.' Write-Host "Use '-Rel List' for a list of available $Selected releases or '-Rel Latest' for latest." } exit 1 } # Windows Release selection => Populate Product Edition $editions = Get-Windows-Editions $winVersionId $winReleaseId if ($Ed -eq 'List') { Write-Host "Please select a Windows Edition (-Ed) for ${Selected}:" } foreach ($edition in $editions) { if ($Ed -eq 'List') { Write-Host ' -' $edition.Edition } elseif (!$Ed -or $edition.Edition -match $Ed) { if (!$Ed -and $Verbosity -ge 1) { Write-Host "No edition specified (-Ed). Defaulting to '$($edition.Edition)'." } $Selected += ',' + $edition.Edition -replace 'Windows [0-9\.]*' $winEditionId = $edition.Id break; } } if ($winEditionId -eq $null) { if ($Ed -ne 'List') { Write-Host 'Invalid Windows edition provided.' Write-Host "Use '-Ed List' for a list of available editions or remove the -Ed parameter to use default." } exit 1 } # Product Edition selection => Request and populate Languages $languages = Get-Windows-Languages $winVersionId $winEditionId if (!$languages) { exit 3 } if ($Lang -eq 'List') { Write-Host "Please select a Language (-Lang) for ${Selected}:" } elseif ($Lang) { # Escape parentheses so that they aren't interpreted as regex $Lang = $Lang.replace('(', '\(') $Lang = $Lang.replace(')', '\)') } $i = 0 foreach ($language in $languages) { if ($Lang -eq 'List') { Write-Host ' -' $language.Language } elseif ((!$Lang -and $script:SelectedIndex -eq $i) -or ($Lang -and $language.Language -match $Lang)) { if (!$Lang -and $Verbosity -ge 1) { Write-Host "No language specified (-Lang). Defaulting to '$($language.Language)'." } $Selected += ', ' + $language.Language $winLanguageId = $language.Id $winLanguageName = $language.Language break; } $i++ } if (!$winLanguageId -or !$winLanguageName) { if ($Lang -ne 'List') { Write-Host 'Invalid Windows language provided.' Write-Host "Use '-Lang List' for a list of available languages or remove the option to use system default." } exit 1 } # Language selection => Request and populate Arch download links $links = Get-Windows-Download-Links $winVersionId $winReleaseId $winEditionId $winLanguageId $winLanguageName if (!$links) { exit 3 } if ($Arch -eq 'List') { Write-Host "Please select an Architecture (-Arch) for ${Selected}:" } $i = 0 foreach ($link in $links) { if ($Arch -eq 'List') { Write-Host ' -' $link.Type } elseif ((!$Arch -and $script:SelectedIndex -eq $i) -or ($Arch -and $link.Type -match $Arch)) { if (!$Arch -and $Verbosity -ge 1) { Write-Host "No architecture specified (-Arch). Defaulting to '$($link.Type)'." } $Selected += ', [' + $link.Type + ']' $winLink = $link break; } $i++ } if ($winLink -eq $null) { if ($Arch -ne 'List') { Write-Host 'Invalid Windows architecture provided.' Write-Host "Use '-Arch List' for a list of available architectures or remove the option to use system default." } exit 1 } # Arch selection => Return selected download link if ($GetUrl) { Return $winLink.Link $ExitCode = 0 } else { Write-Host "Selected: $Selected" $ExitCode = Process-Download-Link $winLink.Link } # Clean up & exit exit $ExitCode } # Form creation $XMLForm = [Windows.Markup.XamlReader]::Load((New-Object System.Xml.XmlNodeReader $XAML)) $XAML.SelectNodes('//*[@Name]') | ForEach-Object { Set-Variable -Name ($_.Name) -Value $XMLForm.FindName($_.Name) -Scope Script } $XMLForm.Title = $AppTitle if ($Icon) { $XMLForm.Icon = $Icon } else { $XMLForm.Icon = [WinAPI.Utils]::ExtractIcon('imageres.dll', -5205, $true) | ConvertTo-ImageSource } if ($Locale.StartsWith('ar') -or $Locale.StartsWith('fa') -or $Locale.StartsWith('he')) { $XMLForm.FlowDirection = 'RightToLeft' } $WindowsVersionTitle.Text = Get-Translation('Version') $Continue.Content = Get-Translation('Continue') $Back.Content = Get-Translation('Close') # Windows 7 and non Windows platforms are too much of a liability if ($winver -le 6.1) { Get-Error(Get-Translation('This feature is not available on this platform.')) exit 403 } # Populate the Windows versions $i = 0 $versions = @() foreach ($version in $WindowsVersions) { $versions += @(New-Object PsObject -Property @{ Version = $version[0][0]; PageType = $version[0][1]; Index = $i }) $i++ } $WindowsVersion.ItemsSource = $versions $WindowsVersion.DisplayMemberPath = 'Version' # Button Action $Continue.add_click({ $script:Stage++ $XMLGrid.Children[2 * $Stage + 1].IsEnabled = $false $Continue.IsEnabled = $false $Back.IsEnabled = $false Refresh-Control($Continue) Refresh-Control($Back) switch ($Stage) { 1 { # Windows Version selection $XMLForm.Title = Get-Translation($English[12]) Refresh-Control($XMLForm) if ($WindowsVersion.SelectedValue.Version.StartsWith('Windows')) { Check-Locale } $releases = Get-Windows-Releases $WindowsVersion.SelectedValue.Index $script:WindowsRelease = Add-Entry $Stage 'Release' $releases $Back.Content = Get-Translation($English[8]) $XMLForm.Title = $AppTitle } 2 { # Windows Release selection => Populate Product Edition $editions = Get-Windows-Editions $WindowsVersion.SelectedValue.Index $WindowsRelease.SelectedValue.Index $script:ProductEdition = Add-Entry $Stage 'Edition' $editions } 3 { # Product Edition selection => Request and populate languages $XMLForm.Title = Get-Translation($English[12]) Refresh-Control($XMLForm) $languages = Get-Windows-Languages $WindowsVersion.SelectedValue.Index $ProductEdition.SelectedValue.Id if ($languages.Length -eq 0) { break } $script:Language = Add-Entry $Stage 'Language' $languages 'DisplayLanguage' $Language.SelectedIndex = $script:SelectedIndex $XMLForm.Title = $AppTitle } 4 { # Language selection => Request and populate Arch download links $XMLForm.Title = Get-Translation($English[12]) Refresh-Control($XMLForm) $links = Get-Windows-Download-Links $WindowsVersion.SelectedValue.Index $WindowsRelease.SelectedValue.Index $ProductEdition.SelectedValue.Id $Language.SelectedValue.Id $Language.SelectedValue.Language if ($links.Length -eq 0) { break } $script:Architecture = Add-Entry $Stage 'Architecture' $links 'Type' if ($PipeName) { $XMLForm.Height += $dh / 2; $Margin = $Continue.Margin $top = $Margin.Top $Margin.Top += $dh / 2 $Continue.Margin = $Margin $Margin = $Back.Margin $Margin.Top += $dh / 2 $Back.Margin = $Margin $Margin = $Check.Margin $Margin.Top = $top - 2 $Check.Margin = $Margin $Check.Content = Get-Translation($English[13]) $Check.Visibility = 'Visible' } $Architecture.SelectedIndex = $script:SelectedIndex $Continue.Content = Get-Translation('Download') $XMLForm.Title = $AppTitle } 5 { # Arch selection => Return selected download link $script:ExitCode = Process-Download-Link $Architecture.SelectedValue.Link $XMLForm.Close() } } $Continue.IsEnabled = $true if ($Stage -ge 0) { $Back.IsEnabled = $true } }) $Back.add_click({ if ($Stage -eq 0) { $XMLForm.Close() } else { $XMLGrid.Children.RemoveAt(2 * $Stage + 3) $XMLGrid.Children.RemoveAt(2 * $Stage + 2) $XMLGrid.Children[2 * $Stage + 1].IsEnabled = $true $dh2 = $dh if ($Stage -eq 4 -and $PipeName) { $Check.Visibility = 'Collapsed' $dh2 += $dh / 2 } $XMLForm.Height -= $dh2; $Margin = $Continue.Margin $Margin.Top -= $dh2 $Continue.Margin = $Margin $Margin = $Back.Margin $Margin.Top -= $dh2 $Back.Margin = $Margin $script:Stage = $Stage - 1 $XMLForm.Title = $AppTitle if ($Stage -eq 0) { $Back.Content = Get-Translation('Close') } else { $Continue.Content = Get-Translation('Continue') Refresh-Control($Continue) } } }) # Display the dialog $XMLForm.Add_Loaded({ $XMLForm.Activate() }) $XMLForm.ShowDialog() | Out-Null # Clean up & exit exit $ExitCode # SIG # Begin signature block # MIIkWAYJKoZIhvcNAQcCoIIkSTCCJEUCAQExDzANBglghkgBZQMEAgEFADB5Bgor # BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG # KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCBHf44+d5nlaax1 # zDK20N+s1BjbUkc/aTGHLUQuXBU9BqCCElkwggVvMIIEV6ADAgECAhBI/JO0YFWU # jTanyYqJ1pQWMA0GCSqGSIb3DQEBDAUAMHsxCzAJBgNVBAYTAkdCMRswGQYDVQQI # DBJHcmVhdGVyIE1hbmNoZXN0ZXIxEDAOBgNVBAcMB1NhbGZvcmQxGjAYBgNVBAoM # EUNvbW9kbyBDQSBMaW1pdGVkMSEwHwYDVQQDDBhBQUEgQ2VydGlmaWNhdGUgU2Vy # dmljZXMwHhcNMjEwNTI1MDAwMDAwWhcNMjgxMjMxMjM1OTU5WjBWMQswCQYDVQQG # EwJHQjEYMBYGA1UEChMPU2VjdGlnbyBMaW1pdGVkMS0wKwYDVQQDEyRTZWN0aWdv # IFB1YmxpYyBDb2RlIFNpZ25pbmcgUm9vdCBSNDYwggIiMA0GCSqGSIb3DQEBAQUA # A4ICDwAwggIKAoICAQCN55QSIgQkdC7/FiMCkoq2rjaFrEfUI5ErPtx94jGgUW+s # hJHjUoq14pbe0IdjJImK/+8Skzt9u7aKvb0Ffyeba2XTpQxpsbxJOZrxbW6q5KCD # J9qaDStQ6Utbs7hkNqR+Sj2pcaths3OzPAsM79szV+W+NDfjlxtd/R8SPYIDdub7 # P2bSlDFp+m2zNKzBenjcklDyZMeqLQSrw2rq4C+np9xu1+j/2iGrQL+57g2extme # me/G3h+pDHazJyCh1rr9gOcB0u/rgimVcI3/uxXP/tEPNqIuTzKQdEZrRzUTdwUz # T2MuuC3hv2WnBGsY2HH6zAjybYmZELGt2z4s5KoYsMYHAXVn3m3pY2MeNn9pib6q # RT5uWl+PoVvLnTCGMOgDs0DGDQ84zWeoU4j6uDBl+m/H5x2xg3RpPqzEaDux5mcz # mrYI4IAFSEDu9oJkRqj1c7AGlfJsZZ+/VVscnFcax3hGfHCqlBuCF6yH6bbJDoEc # QNYWFyn8XJwYK+pF9e+91WdPKF4F7pBMeufG9ND8+s0+MkYTIDaKBOq3qgdGnA2T # OglmmVhcKaO5DKYwODzQRjY1fJy67sPV+Qp2+n4FG0DKkjXp1XrRtX8ArqmQqsV/ # AZwQsRb8zG4Y3G9i/qZQp7h7uJ0VP/4gDHXIIloTlRmQAOka1cKG8eOO7F/05QID # AQABo4IBEjCCAQ4wHwYDVR0jBBgwFoAUoBEKIz6W8Qfs4q8p74Klf9AwpLQwHQYD # VR0OBBYEFDLrkpr/NZZILyhAQnAgNpFcF4XmMA4GA1UdDwEB/wQEAwIBhjAPBgNV # HRMBAf8EBTADAQH/MBMGA1UdJQQMMAoGCCsGAQUFBwMDMBsGA1UdIAQUMBIwBgYE # VR0gADAIBgZngQwBBAEwQwYDVR0fBDwwOjA4oDagNIYyaHR0cDovL2NybC5jb21v # ZG9jYS5jb20vQUFBQ2VydGlmaWNhdGVTZXJ2aWNlcy5jcmwwNAYIKwYBBQUHAQEE # KDAmMCQGCCsGAQUFBzABhhhodHRwOi8vb2NzcC5jb21vZG9jYS5jb20wDQYJKoZI # hvcNAQEMBQADggEBABK/oe+LdJqYRLhpRrWrJAoMpIpnuDqBv0WKfVIHqI0fTiGF # OaNrXi0ghr8QuK55O1PNtPvYRL4G2VxjZ9RAFodEhnIq1jIV9RKDwvnhXRFAZ/ZC # J3LFI+ICOBpMIOLbAffNRk8monxmwFE2tokCVMf8WPtsAO7+mKYulaEMUykfb9gZ # pk+e96wJ6l2CxouvgKe9gUhShDHaMuwV5KZMPWw5c9QLhTkg4IUaaOGnSDip0TYl # d8GNGRbFiExmfS9jzpjoad+sPKhdnckcW67Y8y90z7h+9teDnRGWYpquRRPaf9xH # +9/DUp/mBlXpnYzyOmJRvOwkDynUWICE5EV7WtgwggYcMIIEBKADAgECAhAz1wio # kUBTGeKlu9M5ua1uMA0GCSqGSIb3DQEBDAUAMFYxCzAJBgNVBAYTAkdCMRgwFgYD # VQQKEw9TZWN0aWdvIExpbWl0ZWQxLTArBgNVBAMTJFNlY3RpZ28gUHVibGljIENv # ZGUgU2lnbmluZyBSb290IFI0NjAeFw0yMTAzMjIwMDAwMDBaFw0zNjAzMjEyMzU5 # NTlaMFcxCzAJBgNVBAYTAkdCMRgwFgYDVQQKEw9TZWN0aWdvIExpbWl0ZWQxLjAs # BgNVBAMTJVNlY3RpZ28gUHVibGljIENvZGUgU2lnbmluZyBDQSBFViBSMzYwggGi # MA0GCSqGSIb3DQEBAQUAA4IBjwAwggGKAoIBgQC70f4et0JbePWQp64sg/GNIdMw # hoV739PN2RZLrIXFuwHP4owoEXIEdiyBxasSekBKxRDogRQ5G19PB/YwMDB/NSXl # wHM9QAmU6Kj46zkLVdW2DIseJ/jePiLBv+9l7nPuZd0o3bsffZsyf7eZVReqskmo # PBBqOsMhspmoQ9c7gqgZYbU+alpduLyeE9AKnvVbj2k4aOqlH1vKI+4L7bzQHkND # brBTjMJzKkQxbr6PuMYC9ruCBBV5DFIg6JgncWHvL+T4AvszWbX0w1Xn3/YIIq62 # 0QlZ7AGfc4m3Q0/V8tm9VlkJ3bcX9sR0gLqHRqwG29sEDdVOuu6MCTQZlRvmcBME # Jd+PuNeEM4xspgzraLqVT3xE6NRpjSV5wyHxNXf4T7YSVZXQVugYAtXueciGoWnx # G06UE2oHYvDQa5mll1CeHDOhHu5hiwVoHI717iaQg9b+cYWnmvINFD42tRKtd3V6 # zOdGNmqQU8vGlHHeBzoh+dYyZ+CcblSGoGSgg8sCAwEAAaOCAWMwggFfMB8GA1Ud # IwQYMBaAFDLrkpr/NZZILyhAQnAgNpFcF4XmMB0GA1UdDgQWBBSBMpJBKyjNRsjE # osYqORLsSKk/FDAOBgNVHQ8BAf8EBAMCAYYwEgYDVR0TAQH/BAgwBgEB/wIBADAT # BgNVHSUEDDAKBggrBgEFBQcDAzAaBgNVHSAEEzARMAYGBFUdIAAwBwYFZ4EMAQMw # SwYDVR0fBEQwQjBAoD6gPIY6aHR0cDovL2NybC5zZWN0aWdvLmNvbS9TZWN0aWdv # UHVibGljQ29kZVNpZ25pbmdSb290UjQ2LmNybDB7BggrBgEFBQcBAQRvMG0wRgYI # KwYBBQUHMAKGOmh0dHA6Ly9jcnQuc2VjdGlnby5jb20vU2VjdGlnb1B1YmxpY0Nv # ZGVTaWduaW5nUm9vdFI0Ni5wN2MwIwYIKwYBBQUHMAGGF2h0dHA6Ly9vY3NwLnNl # Y3RpZ28uY29tMA0GCSqGSIb3DQEBDAUAA4ICAQBfNqz7+fZyWhS38Asd3tj9lwHS # /QHumS2G6Pa38Dn/1oFKWqdCSgotFZ3mlP3FaUqy10vxFhJM9r6QZmWLLXTUqwj3 # ahEDCHd8vmnhsNufJIkD1t5cpOCy1rTP4zjVuW3MJ9bOZBHoEHJ20/ng6SyJ6UnT # s5eWBgrh9grIQZqRXYHYNneYyoBBl6j4kT9jn6rNVFRLgOr1F2bTlHH9nv1HMePp # GoYd074g0j+xUl+yk72MlQmYco+VAfSYQ6VK+xQmqp02v3Kw/Ny9hA3s7TSoXpUr # OBZjBXXZ9jEuFWvilLIq0nQ1tZiao/74Ky+2F0snbFrmuXZe2obdq2TWauqDGIgb # MYL1iLOUJcAhLwhpAuNMu0wqETDrgXkG4UGVKtQg9guT5Hx2DJ0dJmtfhAH2KpnN # r97H8OQYok6bLyoMZqaSdSa+2UA1E2+upjcaeuitHFFjBypWBmztfhj24+xkc6Zt # CDaLrw+ZrnVrFyvCTWrDUUZBVumPwo3/E3Gb2u2e05+r5UWmEsUUWlJBl6MGAAjF # 5hzqJ4I8O9vmRsTvLQA1E802fZ3lqicIBczOwDYOSxlP0GOabb/FKVMxItt1UHeG # 0PL4au5rBhs+hSMrl8h+eplBDN1Yfw6owxI9OjWb4J0sjBeBVESoeh2YnZZ/WVim # VGX/UUIL+Efrz/jlvzCCBsIwggUqoAMCAQICEQC/sVABu/WS1JYqd5fqc2+jMA0G # CSqGSIb3DQEBCwUAMFcxCzAJBgNVBAYTAkdCMRgwFgYDVQQKEw9TZWN0aWdvIExp # bWl0ZWQxLjAsBgNVBAMTJVNlY3RpZ28gUHVibGljIENvZGUgU2lnbmluZyBDQSBF # ViBSMzYwHhcNMjEwOTI5MDAwMDAwWhcNMjQwOTI4MjM1OTU5WjCBmDEPMA0GA1UE # BRMGNDA3OTUwMRMwEQYLKwYBBAGCNzwCAQMTAklFMR0wGwYDVQQPExRQcml2YXRl # IE9yZ2FuaXphdGlvbjELMAkGA1UEBhMCSUUxEDAOBgNVBAgMB0RvbmVnYWwxGDAW # BgNVBAoMD0FrZW8gQ29uc3VsdGluZzEYMBYGA1UEAwwPQWtlbyBDb25zdWx0aW5n # MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAsP4naIgrdLeFHiNbZfuF # tjkWIo0jOTo0xpiy0eBzx8YYSRMJGAxXlaKIN+Sq15Te16nW7jWmdh1WGUwC3/9a # HhPc9pJk5qGBZ2O3mOdLpoe03aKEPqlHlcgic0bXxZ3CVc/gkDb71q4jAZYMU3Ii # uD1887ib9Zpis3MnC04zK+ZugpBFjsWajnxY6bR19F+P34I6lS9G7lAKZogbux3g # w2OOo2NHUmOFrsCeLXFRSmFHcjFwk2Mv9B2o34UsQpcfVCuAwnCfPMlbHiIom+BV # cKJlmbgTkTvmN6eWMSdgG0whMUDjwEcfoBpd5VZq2275kgxgJI+NebT/4RGVx0U/ # grKF4X2WWyBA/jikAOMWp7BsTbIsQdpsCVxwR+b3Y04LNk0SOaCzFVSug3G7EvWM # ylpkH+bfc2SYC0H76iEZo3pA7NifEtjY0xyTAAijolG/fldjsypxDYobZm3hIpgo # 8oHP/spHKy1cWUJig/XZ3kI5KTTC/o3Wz1zPg7nUQ4WAG9VdgNTtAeWJUY/yE0gh # eXmZ+Dm0bkHD4dTTC6QLehBj6//97tmbYEYq6WCRxGtxqJVCr+KbkxTh4DCNjTE3 # Xxlkldxd8Oe+Gbqi5MgVHWlHOO4iwpygRsWK1rHjQCktxUZoomeDiqIG0N0f6RXh # 3khwwxinvStxYwFNz6o5V/UCAwEAAaOCAcUwggHBMB8GA1UdIwQYMBaAFIEykkEr # KM1GyMSixio5EuxIqT8UMB0GA1UdDgQWBBQMqWgdye+tyAoAMRo20NXirVSc/zAO # BgNVHQ8BAf8EBAMCB4AwDAYDVR0TAQH/BAIwADATBgNVHSUEDDAKBggrBgEFBQcD # AzARBglghkgBhvhCAQEEBAMCBBAwSQYDVR0gBEIwQDA1BgwrBgEEAbIxAQIBBgEw # JTAjBggrBgEFBQcCARYXaHR0cHM6Ly9zZWN0aWdvLmNvbS9DUFMwBwYFZ4EMAQMw # SwYDVR0fBEQwQjBAoD6gPIY6aHR0cDovL2NybC5zZWN0aWdvLmNvbS9TZWN0aWdv # UHVibGljQ29kZVNpZ25pbmdDQUVWUjM2LmNybDB7BggrBgEFBQcBAQRvMG0wRgYI # KwYBBQUHMAKGOmh0dHA6Ly9jcnQuc2VjdGlnby5jb20vU2VjdGlnb1B1YmxpY0Nv # ZGVTaWduaW5nQ0FFVlIzNi5jcnQwIwYIKwYBBQUHMAGGF2h0dHA6Ly9vY3NwLnNl # Y3RpZ28uY29tMCQGA1UdEQQdMBugGQYIKwYBBQUHCAOgDTALDAlJRS00MDc5NTAw # DQYJKoZIhvcNAQELBQADggGBADVgKUXFLzqffLRAF5qyixRuIeEqQ7Al8ku2DlbJ # WvWHhZoaKaS2ZmjZ/XNMlg1re4FatvwSbNCb0CL7pC81TrCk4qVEXnuY8xdDfQPe # 3Do922CFG6GrQWQ4C/ShFWqGMlZq2+Oxwj+iGSKS5VDDVQOya7G5StVvvIc4uSxX # S6rsNC65oFbTQqkGe9EzIakNkY0Hzh6s6O12+1HXgkZsKlPbpeoPIHV+t9Tny65e # sqn/MUVE1qecEEv72UveyVsyi96OgEPXe/yaiiIO6aSVsQGey98i9HfAayyH7KKZ # QTYOb9goARWPNlkKbyF9bndu5kLWIlZcOS7IIznOcS4y1J5ZJewBRH4kbuNfCbSl # HMZS/rmpFprXXFdje6TRXwgvBs6UOR1zTe5ycumyo5FYBVEFGR1Ps6ZC3z62yLPk # pb5YSma1/ut/KplOxOnK74ELd/vTS2i10qmsqP5+m+U2jznmCEwm8g8V1mg94acL # iyM9uR5+U3y6OrVRkMnG9K9ZuTGCEVUwghFRAgEBMGwwVzELMAkGA1UEBhMCR0Ix # GDAWBgNVBAoTD1NlY3RpZ28gTGltaXRlZDEuMCwGA1UEAxMlU2VjdGlnbyBQdWJs # aWMgQ29kZSBTaWduaW5nIENBIEVWIFIzNgIRAL+xUAG79ZLUlip3l+pzb6MwDQYJ # YIZIAWUDBAIBBQCgfDAQBgorBgEEAYI3AgEMMQIwADAZBgkqhkiG9w0BCQMxDAYK # KwYBBAGCNwIBBDAcBgorBgEEAYI3AgELMQ4wDAYKKwYBBAGCNwIBFTAvBgkqhkiG # 9w0BCQQxIgQgqqe1F1mEJpX4dnl4MXR5prBRZLQto2sBRVwggJo/MmswDQYJKoZI # hvcNAQEBBQAEggIAIV2/sxOW2vNsi2RIR5heiBkJXL9Cr3JkCA8XFvLfKwXe/ut9 # mROkV8+DB5mDf3jngzXMtI07yx+hMuA/Lu9cJe2/DTBy4KQI8GC0JSVALVYrm2NU # XclHamxx7ab2MLtxX6tSB3fR1LY14oclKNMgdBu6sWF9vmTS5O/T3qEihKjtAdNZ # qRTwgVvQHvK8YKN58uK7Vbu32Pd0j2Eik4oxmc70MXxFmCjIBB8ncdqCQFXG1Yb1 # VO7zHeRa+Y0MRjQEa8KsPR9uD1luklRD6vg4iknGGmlc+Nmly44EeBpK7sRepF+e # i+kLEzw7IekZOUzLKJCjucWjS1NpJwAobz3aGqQBQ1UB0P8T3ewCx2XX54T8moqJ # AfVM+ZqQV7X6FPBv6CrXD4wnRBIQ5dl6d0tzdFn2L5khY1HUEPGYpN/2l6BWtXjX # 0oi5ekkrRWwlRN4l3fY7fNfFoNet5RYjaGQ/i+w8pBaP2K5ww+VTm9nGSfl20DVp # WFjT+tV758SPgC+fmLBmYd1FCIPZpY4OMSMa18d6lItom3t940LzSEprtiMy+36f # 6SZ2PxYWX1FeEH4lFoMEDwZnq+eyOEgzwYBYORaJBLp7kMByLl8llFZehQEN1LAt # ej05ISFVCVTXdor6r+gehODD2vtAQzITU6wXHoV7Lql73bpb+InUDRQMIvmhgg48 # MIIOOAYKKwYBBAGCNwMDATGCDigwgg4kBgkqhkiG9w0BBwKggg4VMIIOEQIBAzEN # MAsGCWCGSAFlAwQCATCCAQ4GCyqGSIb3DQEJEAEEoIH+BIH7MIH4AgEBBgtghkgB # hvhFAQcXAzAxMA0GCWCGSAFlAwQCAQUABCAvatjeRykuVBr5SLwlvgA58YXzCwoH # 9wFdhHo4uzpN1wIUPApJ6noKhPzZkCXyFW/KtK7iM8oYDzIwMjQwMjA3MTU1NDAy # WjADAgEeoIGGpIGDMIGAMQswCQYDVQQGEwJVUzEdMBsGA1UEChMUU3ltYW50ZWMg # Q29ycG9yYXRpb24xHzAdBgNVBAsTFlN5bWFudGVjIFRydXN0IE5ldHdvcmsxMTAv # BgNVBAMTKFN5bWFudGVjIFNIQTI1NiBUaW1lU3RhbXBpbmcgU2lnbmVyIC0gRzOg # ggqLMIIFODCCBCCgAwIBAgIQewWx1EloUUT3yYnSnBmdEjANBgkqhkiG9w0BAQsF # ADCBvTELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYD # VQQLExZWZXJpU2lnbiBUcnVzdCBOZXR3b3JrMTowOAYDVQQLEzEoYykgMjAwOCBW # ZXJpU2lnbiwgSW5jLiAtIEZvciBhdXRob3JpemVkIHVzZSBvbmx5MTgwNgYDVQQD # Ey9WZXJpU2lnbiBVbml2ZXJzYWwgUm9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0 # eTAeFw0xNjAxMTIwMDAwMDBaFw0zMTAxMTEyMzU5NTlaMHcxCzAJBgNVBAYTAlVT # MR0wGwYDVQQKExRTeW1hbnRlYyBDb3Jwb3JhdGlvbjEfMB0GA1UECxMWU3ltYW50 # ZWMgVHJ1c3QgTmV0d29yazEoMCYGA1UEAxMfU3ltYW50ZWMgU0hBMjU2IFRpbWVT # dGFtcGluZyBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALtZnVlV # T52Mcl0agaLrVfOwAa08cawyjwVrhponADKXak3JZBRLKbvC2Sm5Luxjs+HPPwtW # kPhiG37rpgfi3n9ebUA41JEG50F8eRzLy60bv9iVkfPw7mz4rZY5Ln/BJ7h4OcWE # pe3tr4eOzo3HberSmLU6Hx45ncP0mqj0hOHE0XxxxgYptD/kgw0mw3sIPk35Crcz # Sf/KO9T1sptL4YiZGvXA6TMU1t/HgNuR7v68kldyd/TNqMz+CfWTN76ViGrF3PSx # S9TO6AmRX7WEeTWKeKwZMo8jwTJBG1kOqT6xzPnWK++32OTVHW0ROpL2k8mc40ju # u1MO1DaXhnjFoTcCAwEAAaOCAXcwggFzMA4GA1UdDwEB/wQEAwIBBjASBgNVHRMB # Af8ECDAGAQH/AgEAMGYGA1UdIARfMF0wWwYLYIZIAYb4RQEHFwMwTDAjBggrBgEF # BQcCARYXaHR0cHM6Ly9kLnN5bWNiLmNvbS9jcHMwJQYIKwYBBQUHAgIwGRoXaHR0 # cHM6Ly9kLnN5bWNiLmNvbS9ycGEwLgYIKwYBBQUHAQEEIjAgMB4GCCsGAQUFBzAB # hhJodHRwOi8vcy5zeW1jZC5jb20wNgYDVR0fBC8wLTAroCmgJ4YlaHR0cDovL3Mu # c3ltY2IuY29tL3VuaXZlcnNhbC1yb290LmNybDATBgNVHSUEDDAKBggrBgEFBQcD # CDAoBgNVHREEITAfpB0wGzEZMBcGA1UEAxMQVGltZVN0YW1wLTIwNDgtMzAdBgNV # HQ4EFgQUr2PWyqNOhXLgp7xB8ymiOH+AdWIwHwYDVR0jBBgwFoAUtnf6aUhHn1MS # 1cLqBzJ2B9GXBxkwDQYJKoZIhvcNAQELBQADggEBAHXqsC3VNBlcMkX+DuHUT6Z4 # wW/X6t3cT/OhyIGI96ePFeZAKa3mXfSi2VZkhHEwKt0eYRdmIFYGmBmNXXHy+Je8 # Cf0ckUfJ4uiNA/vMkC/WCmxOM+zWtJPITJBjSDlAIcTd1m6JmDy1mJfoqQa3CcmP # U1dBkC/hHk1O3MoQeGxCbvC2xfhhXFL1TvZrjfdKer7zzf0D19n2A6gP41P3CnXs # xnUuqmaFBJm3+AZX4cYO9uiv2uybGB+queM6AL/OipTLAduexzi7D1Kr0eOUA2AK # TaD+J20UMvw/l0Dhv5mJ2+Q5FL3a5NPD6itas5VYVQR9x5rsIwONhSrS/66pYYEw # ggVLMIIEM6ADAgECAhB71OWvuswHP6EBIwQiQU0SMA0GCSqGSIb3DQEBCwUAMHcx # CzAJBgNVBAYTAlVTMR0wGwYDVQQKExRTeW1hbnRlYyBDb3Jwb3JhdGlvbjEfMB0G # A1UECxMWU3ltYW50ZWMgVHJ1c3QgTmV0d29yazEoMCYGA1UEAxMfU3ltYW50ZWMg # U0hBMjU2IFRpbWVTdGFtcGluZyBDQTAeFw0xNzEyMjMwMDAwMDBaFw0yOTAzMjIy # MzU5NTlaMIGAMQswCQYDVQQGEwJVUzEdMBsGA1UEChMUU3ltYW50ZWMgQ29ycG9y # YXRpb24xHzAdBgNVBAsTFlN5bWFudGVjIFRydXN0IE5ldHdvcmsxMTAvBgNVBAMT # KFN5bWFudGVjIFNIQTI1NiBUaW1lU3RhbXBpbmcgU2lnbmVyIC0gRzMwggEiMA0G # CSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCvDoqq+Ny/aXtUF3FHCb2NPIH4dBV3 # Z5Cc/d5OAp5LdvblNj5l1SQgbTD53R2D6T8nSjNObRaK5I1AjSKqvqcLG9IHtjy1 # GiQo+BtyUT3ICYgmCDr5+kMjdUdwDLNfW48IHXJIV2VNrwI8QPf03TI4kz/lLKbz # WSPLgN4TTfkQyaoKGGxVYVfR8QIsxLWr8mwj0p8NDxlsrYViaf1OhcGKUjGrW9jJ # dFLjV2wiv1V/b8oGqz9KtyJ2ZezsNvKWlYEmLP27mKoBONOvJUCbCVPwKVeFWF7q # hUhBIYfl3rTTJrJ7QFNYeY5SMQZNlANFxM48A+y3API6IsW0b+XvsIqbAgMBAAGj # ggHHMIIBwzAMBgNVHRMBAf8EAjAAMGYGA1UdIARfMF0wWwYLYIZIAYb4RQEHFwMw # TDAjBggrBgEFBQcCARYXaHR0cHM6Ly9kLnN5bWNiLmNvbS9jcHMwJQYIKwYBBQUH # AgIwGRoXaHR0cHM6Ly9kLnN5bWNiLmNvbS9ycGEwQAYDVR0fBDkwNzA1oDOgMYYv # aHR0cDovL3RzLWNybC53cy5zeW1hbnRlYy5jb20vc2hhMjU2LXRzcy1jYS5jcmww # FgYDVR0lAQH/BAwwCgYIKwYBBQUHAwgwDgYDVR0PAQH/BAQDAgeAMHcGCCsGAQUF # BwEBBGswaTAqBggrBgEFBQcwAYYeaHR0cDovL3RzLW9jc3Aud3Muc3ltYW50ZWMu # Y29tMDsGCCsGAQUFBzAChi9odHRwOi8vdHMtYWlhLndzLnN5bWFudGVjLmNvbS9z # aGEyNTYtdHNzLWNhLmNlcjAoBgNVHREEITAfpB0wGzEZMBcGA1UEAxMQVGltZVN0 # YW1wLTIwNDgtNjAdBgNVHQ4EFgQUpRMBqZ+FzBtuFh5fOzGqeTYAex0wHwYDVR0j # BBgwFoAUr2PWyqNOhXLgp7xB8ymiOH+AdWIwDQYJKoZIhvcNAQELBQADggEBAEae # r/C4ol+imUjPqCdLIc2yuaZycGMv41UpezlGTud+ZQZYi7xXipINCNgQujYk+gp7 # +zvTYr9KlBXmgtuKVG3/KP5nz3E/5jMJ2aJZEPQeSv5lzN7Ua+NSKXUASiulzMub # 6KlN97QXWZJBw7c/hub2wH9EPEZcF1rjpDvVaSbVIX3hgGd+Yqy3Ti4VmuWcI69b # EepxqUH5DXk4qaENz7Sx2j6aescixXTN30cJhsT8kSWyG5bphQjo3ep0YG5gpVZ6 # DchEWNzm+UgUnuW/3gC9d7GYFHIUJN/HESwfAD/DSxTGZxzMHgajkF9cVIs+4zNb # gg/Ft4YCTnGf6WZFP3YxggJaMIICVgIBATCBizB3MQswCQYDVQQGEwJVUzEdMBsG # A1UEChMUU3ltYW50ZWMgQ29ycG9yYXRpb24xHzAdBgNVBAsTFlN5bWFudGVjIFRy # dXN0IE5ldHdvcmsxKDAmBgNVBAMTH1N5bWFudGVjIFNIQTI1NiBUaW1lU3RhbXBp # bmcgQ0ECEHvU5a+6zAc/oQEjBCJBTRIwCwYJYIZIAWUDBAIBoIGkMBoGCSqGSIb3 # DQEJAzENBgsqhkiG9w0BCRABBDAcBgkqhkiG9w0BCQUxDxcNMjQwMjA3MTU1NDAy # WjAvBgkqhkiG9w0BCQQxIgQg570COHsaZPS9rjX6BejuZj8Sq+pKr21RdiXdWz3K # O1MwNwYLKoZIhvcNAQkQAi8xKDAmMCQwIgQgxHTOdgB9AjlODaXk3nwUxoD54oIB # PP72U+9dtx/fYfgwCwYJKoZIhvcNAQEBBIIBAInhOfqTOXDdnHraAsocyn30sofK # m/jHs++YFntM7KV18KMzYmuDtWJlZf98q2LqryJ/4Lq9rwbR+F83GprDYWD7LnpW # XI/GdSxdUDIO4BDQp6ckAbQ3uzh6DxtlsW9CCMIHdJpPpY5pjeI0Gv4YyGFPDgY+ # QC4d/95+yC3pUOqxMdeK5O0IHuHOF0iER+52doaqbnAPaA6s0UD1jkJKVk7SXej5 # YXa+0hI0Oseu6j1yaqkoFSV24uecmEh+xnfRvVM7dlltGsVJ5L1xLx396/7LW9Wr # J8HxJBxc775T9RcpIMTzxPkrRfngY0e7+D13hVLyfmYNkC0OxuekJ7f16O8= # SIG # End signature block |