mirror of
https://github.com/pbatard/Fido.git
synced 2025-09-17 06:38:03 +02:00
Compare commits
5 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
6f8c9cb6ed | ||
![]() |
4cebdef31a | ||
![]() |
bd9475773c | ||
![]() |
c6ac4acc4a | ||
![]() |
ef9e43d1eb |
1
.gitattributes
vendored
1
.gitattributes
vendored
@@ -1,2 +1,3 @@
|
|||||||
* text=auto
|
* text=auto
|
||||||
*.ps1 eol=crlf
|
*.ps1 eol=crlf
|
||||||
|
*.sh eol=lf
|
||||||
|
3
.gitignore
vendored
3
.gitignore
vendored
@@ -259,3 +259,6 @@ paket-files/
|
|||||||
# Python Tools for Visual Studio (PTVS)
|
# Python Tools for Visual Studio (PTVS)
|
||||||
__pycache__/
|
__pycache__/
|
||||||
*.pyc
|
*.pyc
|
||||||
|
|
||||||
|
# Signatures
|
||||||
|
*.sig
|
||||||
|
279
Fido.ps1
279
Fido.ps1
@@ -1,5 +1,5 @@
|
|||||||
#
|
#
|
||||||
# Fido - Retail Windows ISO Downloader
|
# Fido v1.05 - Retail Windows ISO Downloader
|
||||||
# Copyright © 2019 Pete Batard <pete@akeo.ie>
|
# Copyright © 2019 Pete Batard <pete@akeo.ie>
|
||||||
# ConvertTo-ImageSource: Copyright © 2016 Chris Carter
|
# ConvertTo-ImageSource: Copyright © 2016 Chris Carter
|
||||||
#
|
#
|
||||||
@@ -22,21 +22,21 @@
|
|||||||
|
|
||||||
#region Parameters
|
#region Parameters
|
||||||
param(
|
param(
|
||||||
|
# (Optional) The title to display on the application window.
|
||||||
|
[string]$AppTitle = "Fido - Retail Windows ISO Downloader",
|
||||||
|
# (Optional) '|' separated UI localization strings.
|
||||||
|
[string]$LocData,
|
||||||
|
# (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.
|
# (Optional) Name of a pipe the download URL should be sent to.
|
||||||
# If not provided, a browser window is opened instead.
|
# If not provided, a browser window is opened instead.
|
||||||
[string]$PipeName,
|
[string]$PipeName,
|
||||||
# (Optional) '|' separated UI localization strings.
|
# (Optional) Disable IE First Run Customize so that Invoke-WebRequest
|
||||||
[string]$LocData,
|
# doesn't throw an exception if the user has never launched IE.
|
||||||
# (Optional) Path to the file that should be used for the UI icon.
|
# Note that this requires the script to run elevated.
|
||||||
[string]$Icon,
|
[switch]$DisableFirstRunCustomize,
|
||||||
# (Optional) The title to display on the application window
|
# (Optional) Toggle expert mode (additional ISOs to choose).
|
||||||
[string]$AppTitle = "Fido - Retail Windows ISO Downloader",
|
[switch]$Expert = $False
|
||||||
# (Optional) Whether to show the "Download in a browser" checkbox
|
|
||||||
[switch]$ShowBrowserOption = $True,
|
|
||||||
# (Optional) Test/Debug options
|
|
||||||
[switch]$Debug = $False,
|
|
||||||
[switch]$Expert = $False,
|
|
||||||
[switch]$Testing = $False
|
|
||||||
)
|
)
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
@@ -61,8 +61,6 @@ $code = @"
|
|||||||
if (!File.Exists(muiPath))
|
if (!File.Exists(muiPath))
|
||||||
muiPath = Environment.SystemDirectory + @"\en-US\" + dll + ".mui";
|
muiPath = Environment.SystemDirectory + @"\en-US\" + dll + ".mui";
|
||||||
IntPtr hMui = LoadLibrary(muiPath);
|
IntPtr hMui = LoadLibrary(muiPath);
|
||||||
if (hMui == null)
|
|
||||||
return "";
|
|
||||||
StringBuilder szString = new StringBuilder(MAX_PATH);
|
StringBuilder szString = new StringBuilder(MAX_PATH);
|
||||||
LoadString(hMui, (uint)index, szString, MAX_PATH);
|
LoadString(hMui, (uint)index, szString, MAX_PATH);
|
||||||
return szString.ToString();
|
return szString.ToString();
|
||||||
@@ -296,9 +294,9 @@ function Add-Entry([int]$pos, [string]$Name, [array]$Items, [string]$DisplayName
|
|||||||
$XMLGrid.Children.Insert(2 * $Stage + 3, $Combo)
|
$XMLGrid.Children.Insert(2 * $Stage + 3, $Combo)
|
||||||
|
|
||||||
$XMLForm.Height += $dh;
|
$XMLForm.Height += $dh;
|
||||||
$Margin = $Confirm.Margin
|
$Margin = $Continue.Margin
|
||||||
$Margin.Top += $dh
|
$Margin.Top += $dh
|
||||||
$Confirm.Margin = $Margin
|
$Continue.Margin = $Margin
|
||||||
$Margin = $Back.Margin
|
$Margin = $Back.Margin
|
||||||
$Margin.Top += $dh
|
$Margin.Top += $dh
|
||||||
$Back.Margin = $Margin
|
$Back.Margin = $Margin
|
||||||
@@ -308,7 +306,7 @@ function Add-Entry([int]$pos, [string]$Name, [array]$Items, [string]$DisplayName
|
|||||||
|
|
||||||
function Refresh-Control([object]$Control)
|
function Refresh-Control([object]$Control)
|
||||||
{
|
{
|
||||||
$Control.Dispatcher.Invoke("Render", [Windows.Input.InputEventHandler] { $Confirm.UpdateLayout() }, $null, $null)
|
$Control.Dispatcher.Invoke("Render", [Windows.Input.InputEventHandler] { $Continue.UpdateLayout() }, $null, $null)
|
||||||
}
|
}
|
||||||
|
|
||||||
function Send-Message([string]$PipeName, [string]$Message)
|
function Send-Message([string]$PipeName, [string]$Message)
|
||||||
@@ -316,7 +314,6 @@ function Send-Message([string]$PipeName, [string]$Message)
|
|||||||
[System.Text.Encoding]$Encoding = [System.Text.Encoding]::UTF8
|
[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)
|
$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 {
|
try {
|
||||||
Write-Host Connecting to $PipeName
|
|
||||||
$Pipe.Connect(1000)
|
$Pipe.Connect(1000)
|
||||||
} catch {
|
} catch {
|
||||||
Write-Host $_.Exception.Message
|
Write-Host $_.Exception.Message
|
||||||
@@ -373,17 +370,39 @@ function Get-Translation([string]$Text)
|
|||||||
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 | ? {$_.id -eq $Id}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function Error([string]$ErrorMessage)
|
function Error([string]$ErrorMessage)
|
||||||
{
|
{
|
||||||
Write-Host $ErrorMessage
|
Write-Host $ErrorMessage
|
||||||
$XMLForm.Title = $(Get-Translation("Error")) + ": " + $ErrorMessage
|
$XMLForm.Title = $(Get-Translation("Error")) + ": " + $ErrorMessage
|
||||||
Refresh-Control($XMLForm)
|
Refresh-Control($XMLForm)
|
||||||
$Confirm.Content = Get-Translation("Close")
|
$Continue.Content = Get-Translation("Close")
|
||||||
Refresh-Control($Confirm)
|
Refresh-Control($Continue)
|
||||||
$UserInput = [System.Windows.MessageBox]::Show($XMLForm.Title, $(Get-Translation("Error")), "OK", "Error")
|
$UserInput = [System.Windows.MessageBox]::Show($XMLForm.Title, $(Get-Translation("Error")), "OK", "Error")
|
||||||
$script:ExitCode = $Stage
|
$script:ExitCode = $Stage
|
||||||
$script:Stage = -1
|
$script:Stage = -1
|
||||||
$Confirm.IsEnabled = $True
|
$Continue.IsEnabled = $True
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
#endregion
|
||||||
|
|
||||||
@@ -391,7 +410,7 @@ function Error([string]$ErrorMessage)
|
|||||||
[xml]$XAML = @"
|
[xml]$XAML = @"
|
||||||
<Window xmlns = "http://schemas.microsoft.com/winfx/2006/xaml/presentation" Height = "162" Width = "384" ResizeMode = "NoResize">
|
<Window xmlns = "http://schemas.microsoft.com/winfx/2006/xaml/presentation" Height = "162" Width = "384" ResizeMode = "NoResize">
|
||||||
<Grid Name = "XMLGrid">
|
<Grid Name = "XMLGrid">
|
||||||
<Button Name = "Confirm" FontSize = "16" Height = "26" Width = "160" HorizontalAlignment = "Left" VerticalAlignment = "Top" Margin = "14,78,0,0"/>
|
<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"/>
|
<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"/>
|
<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"/>
|
<ComboBox Name = "WindowsVersion" FontSize = "14" Height = "24" Width = "340" HorizontalAlignment = "Left" VerticalAlignment="Top" Margin = "14,34,0,0" SelectedIndex = "0"/>
|
||||||
@@ -402,39 +421,71 @@ function Error([string]$ErrorMessage)
|
|||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region Globals
|
#region Globals
|
||||||
|
$ErrorActionPreference = "Stop"
|
||||||
$dh = 58;
|
$dh = 58;
|
||||||
$Stage = 0
|
$Stage = 0
|
||||||
$MaxStage = 4
|
$MaxStage = 4
|
||||||
$SessionId = ""
|
$SessionId = ""
|
||||||
$ExitCode = -1
|
$ExitCode = 100
|
||||||
$Locale = "en-US"
|
$Locale = "en-US"
|
||||||
|
$DFRCKey = "HKLM:\Software\Policies\Microsoft\Internet Explorer\Main\"
|
||||||
|
$DFRCName = "DisableFirstRunCustomize"
|
||||||
|
$DFRCAdded = $False
|
||||||
$RequestData = @{}
|
$RequestData = @{}
|
||||||
$RequestData["GetLangs"] = @("a8f8f489-4c7f-463a-9ca6-5cff94d8d041", "GetSkuInformationByProductEdition" )
|
$RequestData["GetLangs"] = @("a8f8f489-4c7f-463a-9ca6-5cff94d8d041", "getskuinformationbyproductedition" )
|
||||||
$RequestData["GetLinks"] = @("cfa9e580-a81e-4a4b-a846-7b21bf4e2e5b", "GetProductDownloadLinksBySku" )
|
$RequestData["GetLinks"] = @("cfa9e580-a81e-4a4b-a846-7b21bf4e2e5b", "GetProductDownloadLinksBySku" )
|
||||||
|
# Create a semi-random Linux User-Agent string
|
||||||
|
$FirefoxVersion = Get-Random -Minimum 30 -Maximum 60
|
||||||
|
$FirefoxDate = Get-RandomDate
|
||||||
|
$UserAgent = "Mozilla/5.0 (X11; Linux i586; rv:$FirefoxVersion.0) Gecko/$FirefoxDate Firefox/$FirefoxVersion.0"
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
# Localization
|
# Localization
|
||||||
$EnglishMessages = "en-US|Version|Release|Edition|Language|Architecture|Download|Confirm|Modify|Close|Cancel|Error|Please wait...|Download using a browser"
|
$EnglishMessages = "en-US|Version|Release|Edition|Language|Architecture|Download|Continue|Back|Close|Cancel|Error|Please wait...|" +
|
||||||
if ($Testing) {
|
"Download using a browser|Temporarily banned by Microsoft for requesting too many downloads - Please try again later...|" +
|
||||||
$LocData = "fr-FR|||Édition|Langue de produit||Télécharger|Confirmer|Modifier|Fermer|Annuler|Erreur|Veuillez patienter...|Télécharger avec un navigateur"
|
"PowerShell 3.0 or later is required to run this script.|Do you want to go online and download it?"
|
||||||
$TestLangs = '{"languages":[
|
|
||||||
{ "language":"English", "text":"Anglais", "id":"100" },
|
|
||||||
{ "language":"English (International)", "text":"Anglais (International)", "id":"101" },
|
|
||||||
{ "language":"French", "text":"Français", "id":"102" },
|
|
||||||
{ "language":"French (Canadian)", "text":"Français (Canadien)", "id":"103" }
|
|
||||||
]}'
|
|
||||||
}
|
|
||||||
[string[]]$English = $EnglishMessages.Split('|')
|
[string[]]$English = $EnglishMessages.Split('|')
|
||||||
[string[]]$Localized = $null
|
[string[]]$Localized = $null
|
||||||
if ($LocData -and (-not $LocData.StartsWith("en-US"))) {
|
if ($LocData -and (-not $LocData.StartsWith("en-US"))) {
|
||||||
$Localized = $LocData.Split('|')
|
$Localized = $LocData.Split('|')
|
||||||
if ($Localized.Length -ne $English.Length) {
|
if ($Localized.Length -ne $English.Length) {
|
||||||
Write-Host "Error: Missing or extra translated messages provided ($($Localized.Length)/$($English.Length))"
|
Write-Host "Error: Missing or extra translated messages provided ($($Localized.Length)/$($English.Length))"
|
||||||
exit 1
|
exit 101
|
||||||
}
|
}
|
||||||
$Locale = $Localized[0]
|
$Locale = $Localized[0]
|
||||||
}
|
}
|
||||||
|
# Test if the Microsoft servers can handle our locale. If not, fall back to en-US.
|
||||||
|
$QueryLocale = $Locale
|
||||||
|
try {
|
||||||
|
$url = "https://www.microsoft.com/" + $QueryLocale + "/software-download/"
|
||||||
|
Invoke-WebRequest -UseBasicParsing -MaximumRedirection 0 -UserAgent $UserAgent $url | Out-Null
|
||||||
|
} catch {
|
||||||
|
$QueryLocale = "en-US"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Make sure PowerShell 3.0 or later is used (for Invoke-WebRequest)
|
||||||
|
if ($PSVersionTable.PSVersion.Major -lt 3) {
|
||||||
|
Write-Host Error: PowerShell 3.0 or later is required to run this script.
|
||||||
|
$Msg = "$(Get-Translation($English[15]))`n$(Get-Translation($English[16]))"
|
||||||
|
if ([System.Windows.MessageBox]::Show($Msg, $(Get-Translation("Error")), "YesNo", "Error") -eq "Yes") {
|
||||||
|
Start-Process -FilePath https://www.microsoft.com/download/details.aspx?id=34595
|
||||||
|
}
|
||||||
|
exit 102
|
||||||
|
}
|
||||||
|
|
||||||
|
# If asked, disable IE's first run customize prompt as it interferes with Invoke-WebRequest
|
||||||
|
if ($DisableFirstRunCustomize) {
|
||||||
|
try {
|
||||||
|
# Only create the key if it doesn't already exist
|
||||||
|
Get-ItemProperty -Path $DFRCKey -Name $DFRCName
|
||||||
|
} catch {
|
||||||
|
if (-not (Test-Path $DFRCKey)) {
|
||||||
|
New-Item -Path $DFRCKey -Force | Out-Null
|
||||||
|
}
|
||||||
|
Set-ItemProperty -Path $DFRCKey -Name $DFRCName -Value 1
|
||||||
|
$DFRCAdded = $True
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
# Form creation
|
# Form creation
|
||||||
$XMLForm = [Windows.Markup.XamlReader]::Load((New-Object System.Xml.XmlNodeReader $XAML))
|
$XMLForm = [Windows.Markup.XamlReader]::Load((New-Object System.Xml.XmlNodeReader $XAML))
|
||||||
@@ -449,8 +500,8 @@ if ($Locale.StartsWith("ar") -or $Locale.StartsWith("fa") -or $Locale.StartsWit
|
|||||||
$XMLForm.FlowDirection = "RightToLeft"
|
$XMLForm.FlowDirection = "RightToLeft"
|
||||||
}
|
}
|
||||||
$WindowsVersionTitle.Text = Get-Translation("Version")
|
$WindowsVersionTitle.Text = Get-Translation("Version")
|
||||||
$Confirm.Content = Get-Translation("Confirm")
|
$Continue.Content = Get-Translation("Continue")
|
||||||
$Back.Content = Get-Translation("Cancel")
|
$Back.Content = Get-Translation("Close")
|
||||||
|
|
||||||
# Populate the Windows versions
|
# Populate the Windows versions
|
||||||
$i = 0
|
$i = 0
|
||||||
@@ -463,16 +514,16 @@ $WindowsVersion.ItemsSource = $array
|
|||||||
$WindowsVersion.DisplayMemberPath = "Version"
|
$WindowsVersion.DisplayMemberPath = "Version"
|
||||||
|
|
||||||
# Button Action
|
# Button Action
|
||||||
$Confirm.add_click({
|
$Continue.add_click({
|
||||||
if ($script:Stage++ -lt 0) {
|
if ($script:Stage++ -lt 0) {
|
||||||
Get-Process -Id $pid | Foreach-Object { $_.CloseMainWindow() | Out-Null }
|
Get-Process -Id $pid | Foreach-Object { $_.CloseMainWindow() | Out-Null }
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
$XMLGrid.Children[2 * $Stage + 1].IsEnabled = $False
|
$XMLGrid.Children[2 * $Stage + 1].IsEnabled = $False
|
||||||
$Confirm.IsEnabled = $False
|
$Continue.IsEnabled = $False
|
||||||
$Back.IsEnabled = $False
|
$Back.IsEnabled = $False
|
||||||
Refresh-Control($Confirm)
|
Refresh-Control($Continue)
|
||||||
Refresh-Control($Back)
|
Refresh-Control($Back)
|
||||||
|
|
||||||
switch ($Stage) {
|
switch ($Stage) {
|
||||||
@@ -481,16 +532,17 @@ $Confirm.add_click({
|
|||||||
$XMLForm.Title = Get-Translation($English[12])
|
$XMLForm.Title = Get-Translation($English[12])
|
||||||
Refresh-Control($XMLForm)
|
Refresh-Control($XMLForm)
|
||||||
|
|
||||||
$url = "https://www.microsoft.com/" + $Locale + "/software-download/"
|
$url = "https://www.microsoft.com/" + $QueryLocale + "/software-download/"
|
||||||
$url += $WindowsVersion.SelectedValue.PageType
|
$url += $WindowsVersion.SelectedValue.PageType
|
||||||
Write-Host Querying $url
|
Write-Host Querying $url
|
||||||
|
|
||||||
if (-not $Testing) {
|
|
||||||
try {
|
try {
|
||||||
$r = Invoke-WebRequest -SessionVariable "Session" $url
|
# Note: We can't use -UseBasicParsing since we need JS to create the session-id
|
||||||
$script:SessionId = $r.ParsedHtml.IHTMLDocument3_GetElementById("session-id").Value
|
# TODO: Use -Headers @{"Cache-Control"="no-cache"}?
|
||||||
|
$r = Invoke-WebRequest -UserAgent $UserAgent -SessionVariable "Session" $url
|
||||||
|
$script:SessionId = $(GetElementById -Request $r -Id "session-id").Value
|
||||||
if (-not $SessionId) {
|
if (-not $SessionId) {
|
||||||
$ErrorMessage = $r.ParsedHtml.IHTMLDocument3_GetElementByID("errorModalMessage").innerHtml
|
$ErrorMessage = $(GetElementById -Request $r -Id "errorModalMessage").InnerText
|
||||||
if ($ErrorMessage) {
|
if ($ErrorMessage) {
|
||||||
Write-Host "$(Get-Translation("Error")): ""$ErrorMessage"""
|
Write-Host "$(Get-Translation("Error")): ""$ErrorMessage"""
|
||||||
}
|
}
|
||||||
@@ -500,7 +552,6 @@ $Confirm.add_click({
|
|||||||
Error($_.Exception.Message)
|
Error($_.Exception.Message)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
$i = 0
|
$i = 0
|
||||||
$array = @()
|
$array = @()
|
||||||
@@ -530,9 +581,7 @@ $Confirm.add_click({
|
|||||||
}
|
}
|
||||||
|
|
||||||
3 { # Product Edition selection => Request and populate Languages
|
3 { # Product Edition selection => Request and populate Languages
|
||||||
|
$url = "https://www.microsoft.com/" + $QueryLocale + "/api/controls/contentinclude/html"
|
||||||
# Get the Product Edition
|
|
||||||
$url = "https://www.microsoft.com/" + $Locale + "/api/controls/contentinclude/html"
|
|
||||||
$url += "?pageId=" + $RequestData["GetLangs"][0]
|
$url += "?pageId=" + $RequestData["GetLangs"][0]
|
||||||
$url += "&host=www.microsoft.com"
|
$url += "&host=www.microsoft.com"
|
||||||
$url += "&segments=software-download," + $WindowsVersion.SelectedValue.PageType
|
$url += "&segments=software-download," + $WindowsVersion.SelectedValue.PageType
|
||||||
@@ -545,16 +594,21 @@ $Confirm.add_click({
|
|||||||
$array = @()
|
$array = @()
|
||||||
$i = 0
|
$i = 0
|
||||||
$SelectedIndex = 0
|
$SelectedIndex = 0
|
||||||
if (-not $Testing) {
|
|
||||||
try {
|
try {
|
||||||
$r = Invoke-WebRequest -WebSession $Session $url
|
$r = Invoke-WebRequest -UserAgent $UserAgent -WebSession $Session $url
|
||||||
foreach ($var in $r.ParsedHtml.IHTMLDocument3_GetElementByID("product-languages")) {
|
# Go through an XML conversion to keep all PowerShells happy...
|
||||||
if ($Debug) {
|
if (-not $($r.AllElements | ? {$_.id -eq "product-languages"})) {
|
||||||
Write-Host $var.value $var.text
|
throw "Unexpected server response"
|
||||||
}
|
}
|
||||||
|
$html = $($r.AllElements | ? {$_.id -eq "product-languages"}).InnerHTML
|
||||||
|
$html = $html.Replace("selected value", "value")
|
||||||
|
$html = $html.Replace("&", "&")
|
||||||
|
$html = "<options>" + $html + "</options>"
|
||||||
|
$xml = [xml]$html
|
||||||
|
foreach ($var in $xml.options.option) {
|
||||||
$json = $var.value | ConvertFrom-Json;
|
$json = $var.value | ConvertFrom-Json;
|
||||||
if ($json) {
|
if ($json) {
|
||||||
$array += @(New-Object PsObject -Property @{ DisplayLanguage = $var.text; Language = $json.language; Id = $json.id })
|
$array += @(New-Object PsObject -Property @{ DisplayLanguage = $var.InnerText; Language = $json.language; Id = $json.id })
|
||||||
if (Select-Language($json.language)) {
|
if (Select-Language($json.language)) {
|
||||||
$SelectedIndex = $i
|
$SelectedIndex = $i
|
||||||
}
|
}
|
||||||
@@ -562,7 +616,7 @@ $Confirm.add_click({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if ($array.Length -eq 0) {
|
if ($array.Length -eq 0) {
|
||||||
$ErrorMessage = $r.ParsedHtml.IHTMLDocument3_GetElementByID("errorModalMessage").innerHtml
|
$ErrorMessage = $(GetElementById -Request $r -Id "errorModalMessage").innerText
|
||||||
if ($ErrorMessage) {
|
if ($ErrorMessage) {
|
||||||
Write-Host "$(Get-Translation("Error")): ""$ErrorMessage"""
|
Write-Host "$(Get-Translation("Error")): ""$ErrorMessage"""
|
||||||
}
|
}
|
||||||
@@ -572,21 +626,12 @@ $Confirm.add_click({
|
|||||||
Error($_.Exception.Message)
|
Error($_.Exception.Message)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
foreach ($var in $(ConvertFrom-Json –InputObject $TestLangs).languages) {
|
|
||||||
$array += @(New-Object PsObject -Property @{ DisplayLanguage = $var.text; Language = $var.language; Id = $var.id })
|
|
||||||
if (Select-Language($var.language)) {
|
|
||||||
$SelectedIndex = $i
|
|
||||||
}
|
|
||||||
$i++
|
|
||||||
}
|
|
||||||
}
|
|
||||||
$script:Language = Add-Entry $Stage "Language" $array "DisplayLanguage"
|
$script:Language = Add-Entry $Stage "Language" $array "DisplayLanguage"
|
||||||
$Language.SelectedIndex = $SelectedIndex
|
$Language.SelectedIndex = $SelectedIndex
|
||||||
}
|
}
|
||||||
|
|
||||||
4 { # Language selection => Request and populate Arch download links
|
4 { # Language selection => Request and populate Arch download links
|
||||||
$url = "https://www.microsoft.com/" + $Locale + "/api/controls/contentinclude/html"
|
$url = "https://www.microsoft.com/" + $QueryLocale + "/api/controls/contentinclude/html"
|
||||||
$url += "?pageId=" + $RequestData["GetLinks"][0]
|
$url += "?pageId=" + $RequestData["GetLinks"][0]
|
||||||
$url += "&host=www.microsoft.com"
|
$url += "&host=www.microsoft.com"
|
||||||
$url += "&segments=software-download," + $WindowsVersion.SelectedValue.PageType
|
$url += "&segments=software-download," + $WindowsVersion.SelectedValue.PageType
|
||||||
@@ -600,39 +645,45 @@ $Confirm.add_click({
|
|||||||
$i = 0
|
$i = 0
|
||||||
$SelectedIndex = 0
|
$SelectedIndex = 0
|
||||||
$array = @()
|
$array = @()
|
||||||
if (-not $Testing) {
|
|
||||||
try {
|
try {
|
||||||
$r = Invoke-WebRequest -WebSession $Session $url
|
$r = Invoke-WebRequest -UserAgent $UserAgent -WebSession $Session $url
|
||||||
foreach ($var in $r.ParsedHtml.IHTMLDocument3_GetElementsByTagName("span") | Where-Object { $_.className -eq "product-download-type" }) {
|
if (-not $($r.AllElements | ? {$_.id -eq "expiration-time"})) {
|
||||||
$Link = $var.ParentNode | Select -Expand href
|
$ErrorMessage = $(GetElementById -Request $r -Id "errorModalMessage").innerText
|
||||||
$Type = $var.innerText
|
if ($ErrorMessage) {
|
||||||
# Maybe Microsoft will provide public ARM/ARM64 retail ISOs one day...
|
Write-Host "$(Get-Translation("Error")): ""$ErrorMessage"""
|
||||||
if ($Type -like "*arm64*") {
|
|
||||||
$Type = "Arm64"
|
|
||||||
if ($ENV:PROCESSOR_ARCHITECTURE -eq "ARM64") {
|
|
||||||
$SelectedIndex = $i
|
|
||||||
}
|
}
|
||||||
} elseif ($Type -like "*arm*") {
|
throw Get-Translation($English[14])
|
||||||
$Type = "Arm"
|
|
||||||
if ($ENV:PROCESSOR_ARCHITECTURE -eq "ARM") {
|
|
||||||
$SelectedIndex = $i
|
|
||||||
}
|
}
|
||||||
} elseif ($Type -like "*x64*") {
|
$html = $($r.AllElements | ? {$_.tagname -eq "input"}).outerHTML
|
||||||
|
# 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(": I", ": ""I")
|
||||||
|
$html = $html.Replace(" }", """ }")
|
||||||
|
$html = "<inputs>" + $html + "</inputs>"
|
||||||
|
$xml = [xml]$html
|
||||||
|
foreach ($var in $xml.inputs.input) {
|
||||||
|
$json = $var.value | ConvertFrom-Json;
|
||||||
|
if ($json) {
|
||||||
|
$Type = $json.DownloadType
|
||||||
|
if ($Type -eq "IsoX64") {
|
||||||
$Type = "x64"
|
$Type = "x64"
|
||||||
if ($ENV:PROCESSOR_ARCHITECTURE -eq "AMD64") {
|
if ($env:PROCESSOR_ARCHITECTURE -eq "AMD64") {
|
||||||
$SelectedIndex = $i
|
$SelectedIndex = $i
|
||||||
}
|
}
|
||||||
} elseif ($Type -like "*x86*") {
|
} elseif ($Type -eq "IsoX86") {
|
||||||
$Type = "x86"
|
$Type = "x86"
|
||||||
if ($ENV:PROCESSOR_ARCHITECTURE -eq "X86") {
|
if ($env:PROCESSOR_ARCHITECTURE -eq "X86") {
|
||||||
$SelectedIndex = $i
|
$SelectedIndex = $i
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
$array += @(New-Object PsObject -Property @{ Type = $Type; Link = $Link })
|
$array += @(New-Object PsObject -Property @{ Type = $Type; Link = $json.Uri })
|
||||||
$i++
|
$i++
|
||||||
}
|
}
|
||||||
|
}
|
||||||
if ($array.Length -eq 0) {
|
if ($array.Length -eq 0) {
|
||||||
$ErrorMessage = $r.ParsedHtml.IHTMLDocument3_GetElementByID("errorModalMessage").innerHtml
|
$ErrorMessage = $(GetElementById -Request $r -Id "errorModalMessage").innerText
|
||||||
if ($ErrorMessage) {
|
if ($ErrorMessage) {
|
||||||
Write-Host "$(Get-Translation("Error")): ""$ErrorMessage"""
|
Write-Host "$(Get-Translation("Error")): ""$ErrorMessage"""
|
||||||
}
|
}
|
||||||
@@ -642,22 +693,14 @@ $Confirm.add_click({
|
|||||||
Error($_.Exception.Message)
|
Error($_.Exception.Message)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
$array += @(New-Object PsObject -Property @{ Type = "x86"; Link = "https://software-download.microsoft.com/sg/Win10_1809Oct_English_x86.iso" })
|
|
||||||
$i++
|
|
||||||
$array += @(New-Object PsObject -Property @{ Type = "x64"; Link = "https://software-download.microsoft.com/sg/Win10_1809Oct_English_x64.iso" })
|
|
||||||
if ($ENV:PROCESSOR_ARCHITECTURE -eq "AMD64") {
|
|
||||||
$SelectedIndex = $i
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$script:Arch = Add-Entry $Stage "Architecture" $array "Type"
|
$script:Arch = Add-Entry $Stage "Architecture" $array "Type"
|
||||||
if (($PipeName -or $Testing) -and $ShowBrowserOption) {
|
if ($PipeName) {
|
||||||
$XMLForm.Height += $dh / 2;
|
$XMLForm.Height += $dh / 2;
|
||||||
$Margin = $Confirm.Margin
|
$Margin = $Continue.Margin
|
||||||
$top = $Margin.Top
|
$top = $Margin.Top
|
||||||
$Margin.Top += $dh /2
|
$Margin.Top += $dh /2
|
||||||
$Confirm.Margin = $Margin
|
$Continue.Margin = $Margin
|
||||||
$Margin = $Back.Margin
|
$Margin = $Back.Margin
|
||||||
$Margin.Top += $dh / 2
|
$Margin.Top += $dh / 2
|
||||||
$Back.Margin = $Margin
|
$Back.Margin = $Margin
|
||||||
@@ -668,7 +711,7 @@ $Confirm.add_click({
|
|||||||
$Check.Visibility = "Visible"
|
$Check.Visibility = "Visible"
|
||||||
}
|
}
|
||||||
$Arch.SelectedIndex = $SelectedIndex
|
$Arch.SelectedIndex = $SelectedIndex
|
||||||
$Confirm.Content = Get-Translation("Download")
|
$Continue.Content = Get-Translation("Download")
|
||||||
}
|
}
|
||||||
|
|
||||||
5 { # Arch selection => Return selected download link
|
5 { # Arch selection => Return selected download link
|
||||||
@@ -682,7 +725,7 @@ $Confirm.add_click({
|
|||||||
$XMLForm.Close()
|
$XMLForm.Close()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
$Confirm.IsEnabled = $True
|
$Continue.IsEnabled = $True
|
||||||
if ($Stage -ge 0) {
|
if ($Stage -ge 0) {
|
||||||
$Back.IsEnabled = $True;
|
$Back.IsEnabled = $True;
|
||||||
}
|
}
|
||||||
@@ -696,44 +739,52 @@ $Back.add_click({
|
|||||||
$XMLGrid.Children.RemoveAt(2 * $Stage + 2)
|
$XMLGrid.Children.RemoveAt(2 * $Stage + 2)
|
||||||
$XMLGrid.Children[2 * $Stage + 1].IsEnabled = $True
|
$XMLGrid.Children[2 * $Stage + 1].IsEnabled = $True
|
||||||
$dh2 = $dh
|
$dh2 = $dh
|
||||||
if ($Stage -eq 4 -and $ShowBrowserOption) {
|
if ($Stage -eq 4 -and $PipeName) {
|
||||||
$Check.Visibility = "Collapsed"
|
$Check.Visibility = "Collapsed"
|
||||||
$dh2 += $dh / 2
|
$dh2 += $dh / 2
|
||||||
}
|
}
|
||||||
$XMLForm.Height -= $dh2;
|
$XMLForm.Height -= $dh2;
|
||||||
$Margin = $Confirm.Margin
|
$Margin = $Continue.Margin
|
||||||
$Margin.Top -= $dh2
|
$Margin.Top -= $dh2
|
||||||
$Confirm.Margin = $Margin
|
$Continue.Margin = $Margin
|
||||||
$Margin = $Back.Margin
|
$Margin = $Back.Margin
|
||||||
$Margin.Top -= $dh2
|
$Margin.Top -= $dh2
|
||||||
$Back.Margin = $Margin
|
$Back.Margin = $Margin
|
||||||
$script:Stage = $Stage - 1
|
$script:Stage = $Stage - 1
|
||||||
if ($Stage -eq 0) {
|
if ($Stage -eq 0) {
|
||||||
$Back.Content = Get-Translation("Cancel")
|
$Back.Content = Get-Translation("Close")
|
||||||
} elseif ($Stage -eq 3) {
|
} elseif ($Stage -eq 3) {
|
||||||
$Confirm.Content = Get-Translation("Confirm")
|
$Continue.Content = Get-Translation("Continue")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
# We need a job in the background to close the obnoxious "Do you want to accept this cookie?" Windows alerts
|
if (-not $PipeName) {
|
||||||
$ClosePrompt = {
|
# We need a job in the background to close the obnoxious "Do you want to accept this cookie?" Windows alerts
|
||||||
|
$ClosePrompt = {
|
||||||
param($PromptTitle)
|
param($PromptTitle)
|
||||||
while ($True) {
|
while ($True) {
|
||||||
Get-Process | Where-Object { $_.MainWindowTitle -match $PromptTitle } | ForEach-Object { $_.CloseMainWindow() }
|
Get-Process | Where-Object { $_.MainWindowTitle -match $PromptTitle } | ForEach-Object { $_.CloseMainWindow() }
|
||||||
Start-Sleep -Milliseconds 100
|
Start-Sleep -Milliseconds 100
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
# Get the localized version of the 'Windows Security Warning' title of the cookie prompt
|
# Get the localized version of the 'Windows Security Warning' title of the cookie prompt
|
||||||
$SecurityWarningTitle = [Gui.Utils]::GetMuiString("urlmon.dll", 2070)
|
$SecurityWarningTitle = [Gui.Utils]::GetMuiString("urlmon.dll", 2070)
|
||||||
if (-not $SecurityWarningTitle) {
|
if (-not $SecurityWarningTitle) {
|
||||||
$SecurityWarningTitle = "Windows Security Warning"
|
$SecurityWarningTitle = "Windows Security Warning"
|
||||||
|
}
|
||||||
|
$Job = Start-Job -ScriptBlock $ClosePrompt -ArgumentList $SecurityWarningTitle
|
||||||
}
|
}
|
||||||
$Job = Start-Job -ScriptBlock $ClosePrompt -ArgumentList $SecurityWarningTitle
|
|
||||||
|
|
||||||
# Display the dialog
|
# Display the dialog
|
||||||
|
$XMLForm.Add_Loaded( { $XMLForm.Activate() } )
|
||||||
$XMLForm.ShowDialog() | Out-Null
|
$XMLForm.ShowDialog() | Out-Null
|
||||||
|
|
||||||
# Clean up & exit
|
# Clean up & exit
|
||||||
Stop-Job -Job $Job
|
if (-not $PipeName) {
|
||||||
|
Stop-Job -Job $Job
|
||||||
|
}
|
||||||
|
if ($DFRCAdded) {
|
||||||
|
Remove-ItemProperty -Path $DFRCKey -Name $DFRCName
|
||||||
|
}
|
||||||
exit $ExitCode
|
exit $ExitCode
|
||||||
|
30
sign.sh
Normal file
30
sign.sh
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# This script creates the RSA-2048 signatures for our downloadable content
|
||||||
|
|
||||||
|
PRIVATE_KEY=/d/Secured/Akeo/Rufus/private.pem
|
||||||
|
PUBLIC_KEY=/d/Secured/Akeo/Rufus/public.pem
|
||||||
|
|
||||||
|
# Create or update a signature
|
||||||
|
sign_file() {
|
||||||
|
if [ -f $FILE.sig ]; then
|
||||||
|
SIZE=$(stat -c%s $FILE.sig)
|
||||||
|
openssl dgst -sha256 -verify $PUBLIC_KEY -signature $FILE.sig $FILE >/dev/null 2>&1
|
||||||
|
if [ ! $? -eq 0 ]; then
|
||||||
|
echo Updating signature for $FILE
|
||||||
|
openssl dgst -sha256 -sign $PRIVATE_KEY -passin pass:$PASSWORD -out $FILE.sig $FILE
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
# No signature => create a new one
|
||||||
|
echo Creating signature for $FILE
|
||||||
|
openssl dgst -sha256 -sign $PRIVATE_KEY -passin pass:$PASSWORD -out $FILE.sig $FILE
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
read -s -p "Enter pass phrase for `realpath $PRIVATE_KEY`: " PASSWORD
|
||||||
|
echo
|
||||||
|
# Confirm that the pass phrase is valid by trying to sign a dummy file
|
||||||
|
openssl dgst -sha256 -sign $PRIVATE_KEY -passin pass:$PASSWORD $PUBLIC_KEY >/dev/null 2>&1 || { echo Invalid pass phrase; exit 1; }
|
||||||
|
|
||||||
|
find . -maxdepth 1 -name "*.ps1" | while read FILE; do sign_file; done
|
||||||
|
# Clear the PASSWORD variable just in case
|
||||||
|
PASSWORD=`head -c 50 /dev/random | base64`
|
Reference in New Issue
Block a user