mirror of
https://github.com/dockur/windows.git
synced 2026-08-04 04:57:13 +01:00
feat: Improve Windows image detection and unattended setup (#2037)
This commit is contained in:
+215
-154
@@ -6,6 +6,7 @@ handleCurlError() {
|
||||
local code="$1"
|
||||
local server="$2"
|
||||
local reason="${3:-}"
|
||||
|
||||
local signal
|
||||
|
||||
if [ -n "$reason" ] && (( code <= 125 )); then
|
||||
@@ -29,8 +30,7 @@ handleCurlError() {
|
||||
SEGV | ABRT) error "Curl crashed with signal $signal." ;;
|
||||
"") error "Curl terminated with exit status $code." ;;
|
||||
*) error "Curl terminated due to signal $signal." ;;
|
||||
esac
|
||||
;;
|
||||
esac ;;
|
||||
esac
|
||||
|
||||
return 1
|
||||
@@ -38,10 +38,9 @@ handleCurlError() {
|
||||
|
||||
curlRequest() {
|
||||
|
||||
local output="$1"
|
||||
local server="$2"
|
||||
local agent="$3"
|
||||
shift 3
|
||||
local server="$1"
|
||||
local agent="$2"
|
||||
shift 2
|
||||
|
||||
local log reason response
|
||||
|
||||
@@ -50,6 +49,8 @@ curlRequest() {
|
||||
return 1
|
||||
fi
|
||||
|
||||
# Preserve curl's status under errexit so its stderr can be translated
|
||||
# into a useful error instead of terminating the script immediately.
|
||||
{
|
||||
response=$(LC_ALL=C curl \
|
||||
--silent \
|
||||
@@ -76,10 +77,7 @@ curlRequest() {
|
||||
|
||||
rm -f "$log"
|
||||
|
||||
if [ -n "$output" ]; then
|
||||
printf -v "$output" '%s' "$response"
|
||||
fi
|
||||
|
||||
printf '%s' "$response"
|
||||
return 0
|
||||
}
|
||||
|
||||
@@ -93,13 +91,13 @@ downloadWindowsLink() {
|
||||
local desc="$6"
|
||||
local type="$7"
|
||||
|
||||
local skuId skuJson
|
||||
local linkJson link
|
||||
local ovData ovTime session
|
||||
local ovToken="" ovTicks=""
|
||||
local profile="606624d44113"
|
||||
local skuId skuJson linkJson
|
||||
local link ovData ovTime session
|
||||
|
||||
# uuidgen: For MacOS (installed by default) and other systems (e.g. with no /proc) that don't have a kernel interface for generating random UUIDs
|
||||
# Prefer the Linux kernel UUID source, with uuidgen as a portable fallback
|
||||
# for macOS and systems without /proc.
|
||||
if ! session=$(cat /proc/sys/kernel/random/uuid 2> /dev/null || uuidgen --random); then
|
||||
error "Failed to generate session ID!"
|
||||
return 1
|
||||
@@ -112,32 +110,32 @@ downloadWindowsLink() {
|
||||
return 1
|
||||
fi
|
||||
|
||||
# Microsoft download "protection" requires the sessionId to be whitelisted through vlscppe.microsoft.com/tags
|
||||
# Register the session with Microsoft's anti-abuse endpoint before
|
||||
# requesting SKU or download links.
|
||||
|
||||
local orgId="y6jn8c31"
|
||||
local vlsUrl="https://vlscppe.microsoft.com/tags?org_id=$orgId&session_id=$session"
|
||||
|
||||
enabled "$DEBUG" && echo "Getting Session ID: $session"
|
||||
|
||||
# Permit Session ID
|
||||
curlRequest "" "Microsoft" "$agent" \
|
||||
curlRequest "Microsoft" "$agent" \
|
||||
--output /dev/null \
|
||||
--header "Accept:" \
|
||||
--max-filesize 100K \
|
||||
-- "$vlsUrl" || return 1
|
||||
|
||||
# Microsoft download "protection" also requires an ov-df.microsoft.com request/reply
|
||||
# 1) Request mdt.js to get w and rticks. InstanceId is (currently) constant.
|
||||
# Complete Microsoft's ov-df challenge by retrieving a token and timing
|
||||
# value, then returning both with the current timestamp.
|
||||
|
||||
local instance="560dc9f3-1aa5-4a2f-b63c-9e18f8d0e175"
|
||||
local ovUrl="https://ov-df.microsoft.com/mdt.js?instanceId=$instance&PageId=si&session_id=$session"
|
||||
|
||||
enabled "$DEBUG" && echo -n "Getting OV data: "
|
||||
|
||||
curlRequest ovData "Microsoft" "$agent" \
|
||||
ovData=$(curlRequest "Microsoft" "$agent" \
|
||||
--header "Accept:" \
|
||||
--max-filesize 1M \
|
||||
-- "$ovUrl" || return 1
|
||||
-- "$ovUrl") || return 1
|
||||
|
||||
if [[ $ovData =~ [\?\&]w=([A-Fa-f0-9]+) ]]; then
|
||||
ovToken="${BASH_REMATCH[1]}"
|
||||
@@ -156,14 +154,12 @@ downloadWindowsLink() {
|
||||
|
||||
sleep 0.2
|
||||
|
||||
# 2) Send a reply with session ID, current epoch and previously retrieved w and rticks
|
||||
|
||||
ovTime=$(date +%s%3N)
|
||||
ovUrl="https://ov-df.microsoft.com/?session_id=$session&CustomerId=$instance&PageId=si&w=$ovToken&mdt=$ovTime&rticks=$ovTicks"
|
||||
|
||||
enabled "$DEBUG" && echo "Sending OV reply: $instance"
|
||||
|
||||
curlRequest "" "Microsoft" "$agent" \
|
||||
curlRequest "Microsoft" "$agent" \
|
||||
--output /dev/null \
|
||||
--header "Accept:" \
|
||||
--max-filesize 100K \
|
||||
@@ -173,12 +169,14 @@ downloadWindowsLink() {
|
||||
|
||||
local skuUrl="https://www.microsoft.com/software-download-connector/api/getskuinformationbyproductedition?profile=$profile&ProductEditionId=$productId&SKU=undefined&friendlyFileName=undefined&Locale=en-US&sessionID=$session"
|
||||
|
||||
curlRequest skuJson "Microsoft" "$agent" \
|
||||
skuJson=$(curlRequest "Microsoft" "$agent" \
|
||||
--referer "$url" \
|
||||
--header "Accept:" \
|
||||
--max-filesize 100K \
|
||||
-- "$skuUrl" || return 1
|
||||
-- "$skuUrl") || return 1
|
||||
|
||||
# Guard jq under errexit so malformed API data can be handled as a normal
|
||||
# missing-result error. The same pattern is reused for the link response.
|
||||
{ skuId=$(printf '%s\n' "$skuJson" | jq --arg LANG "$language" -r 'first(.Skus[]? | select(.Language == $LANG) | .Id) // empty') 2>/dev/null; local rc=$?; } || :
|
||||
|
||||
if [ -z "$skuId" ] || [[ "${skuId,,}" == "null" ]] || (( rc != 0 )); then
|
||||
@@ -190,19 +188,18 @@ downloadWindowsLink() {
|
||||
enabled "$DEBUG" && echo "$skuId"
|
||||
enabled "$DEBUG" && echo "Getting ISO download link..."
|
||||
|
||||
# Get ISO download link
|
||||
# If any request is going to be blocked by Microsoft it's always this last one (the previous requests always seem to succeed)
|
||||
# Microsoft normally applies request or IP blocking on this final connector
|
||||
# call rather than during the preceding session setup.
|
||||
|
||||
local linkUrl="https://www.microsoft.com/software-download-connector/api/GetProductDownloadLinksBySku?profile=$profile&ProductEditionId=undefined&SKU=$skuId&friendlyFileName=undefined&Locale=en-US&sessionID=$session"
|
||||
|
||||
curlRequest linkJson "Microsoft" "$agent" \
|
||||
linkJson=$(curlRequest "Microsoft" "$agent" \
|
||||
--referer "$url" \
|
||||
--header "Accept:" \
|
||||
--max-filesize 100K \
|
||||
-- "$linkUrl" || return 1
|
||||
-- "$linkUrl") || return 1
|
||||
|
||||
if ! [ "$linkJson" ]; then
|
||||
# This should only happen if there's been some change to how this API works
|
||||
error "Microsoft servers gave us an empty response to our request for an automated download."
|
||||
return 1
|
||||
fi
|
||||
@@ -235,9 +232,8 @@ downloadWindows() {
|
||||
local lang="$2"
|
||||
local desc="$3"
|
||||
|
||||
local agent language
|
||||
local page productId
|
||||
local type winVer
|
||||
local agent language page
|
||||
local productId type winVer
|
||||
|
||||
agent=$(getAgent)
|
||||
language=$(getLanguage "$lang" "name")
|
||||
@@ -246,22 +242,18 @@ downloadWindows() {
|
||||
"win10x64" )
|
||||
productId="2618"
|
||||
winVer="10"
|
||||
type="1"
|
||||
;;
|
||||
type="1" ;;
|
||||
"win11x64" )
|
||||
productId="3321"
|
||||
winVer="11"
|
||||
type="1"
|
||||
;;
|
||||
type="1" ;;
|
||||
"win11arm64" )
|
||||
productId="3324"
|
||||
winVer="11arm64"
|
||||
type="2"
|
||||
;;
|
||||
type="2" ;;
|
||||
* )
|
||||
error "Invalid VERSION specified, value \"$id\" is not recognized!"
|
||||
return 1
|
||||
;;
|
||||
return 1 ;;
|
||||
esac
|
||||
|
||||
local url="https://www.microsoft.com/en-us/software-download/windows$winVer"
|
||||
@@ -275,14 +267,16 @@ downloadWindows() {
|
||||
|
||||
sleep 1
|
||||
|
||||
# Product edition IDs can change. If the configured ID fails, recover the
|
||||
# current value from Microsoft's public download page and retry once.
|
||||
local msg="retrying using a different method..."
|
||||
info "Microsoft download request failed, $msg"
|
||||
enabled "$DEBUG" && echo "Parsing download page: ${url}"
|
||||
|
||||
curlRequest page "Microsoft" "$agent" \
|
||||
page=$(curlRequest "Microsoft" "$agent" \
|
||||
--header "Accept:" \
|
||||
--max-filesize 1M \
|
||||
-- "$url" || return 1
|
||||
-- "$url") || return 1
|
||||
|
||||
enabled "$DEBUG" && echo -n "Getting Product edition ID: "
|
||||
productId=$(printf '%s' "$page" |
|
||||
@@ -310,6 +304,7 @@ downloadWindowsEval() {
|
||||
local id="$1"
|
||||
local lang="$2"
|
||||
local desc="$3"
|
||||
|
||||
local culture compare type
|
||||
local agent language winVer
|
||||
|
||||
@@ -354,13 +349,12 @@ downloadWindowsEval() {
|
||||
|
||||
enabled "$DEBUG" && echo "Parsing download page: ${url}"
|
||||
|
||||
curlRequest page "Microsoft" "$agent" \
|
||||
page=$(curlRequest "Microsoft" "$agent" \
|
||||
--location \
|
||||
--max-filesize 1M \
|
||||
-- "$url" || return 1
|
||||
-- "$url") || return 1
|
||||
|
||||
if ! [ "$page" ]; then
|
||||
# This should only happen if there's been some change to where this download page is located
|
||||
error "Windows server download page gave us an empty response"
|
||||
return 1
|
||||
fi
|
||||
@@ -375,7 +369,8 @@ downloadWindowsEval() {
|
||||
grep -Eio "https://go\.microsoft\.com/fwlink(/p)?/\?[^\"'<>[:space:]]+" |
|
||||
grep -Ei '(^|[?&])culture='"${culture,,}"'(&|$)' |
|
||||
grep -Ei '(^|[?&])country='"${country,,}"'(&|$)') || {
|
||||
# This should only happen if there's been some change to the download endpoint web address
|
||||
# Distinguish a changed or missing English page from an unavailable
|
||||
# translation for an otherwise supported product.
|
||||
if [[ "${lang,,}" == "en" || "${lang,,}" == "en-"* ]]; then
|
||||
error "Windows server download page gave us no download link!"
|
||||
else
|
||||
@@ -385,6 +380,8 @@ downloadWindowsEval() {
|
||||
return 1
|
||||
}
|
||||
|
||||
# Evaluation pages currently expose several matching fwlinks in a known
|
||||
# product/platform order, so select the entry for the requested variant.
|
||||
case "$type" in
|
||||
"iot" )
|
||||
case "${PLATFORM,,}" in
|
||||
@@ -420,19 +417,21 @@ downloadWindowsEval() {
|
||||
|
||||
[ -z "$link" ] && error "Could not parse download link from page!" && return 1
|
||||
|
||||
# Follow redirect so proceeding log message is useful
|
||||
# This is a request we make that Fido doesn't
|
||||
# Resolve the fwlink now so later logging and platform validation use the
|
||||
# actual ISO URL rather than Microsoft's generic redirect.
|
||||
|
||||
curlRequest link "Microsoft" "$agent" \
|
||||
link=$(curlRequest "Microsoft" "$agent" \
|
||||
--location \
|
||||
--output /dev/null \
|
||||
--write-out "%{url_effective}" \
|
||||
--head \
|
||||
-- "$link" || return 1
|
||||
-- "$link") || return 1
|
||||
|
||||
local lower="${link,,}"
|
||||
local separator='(^|[[:space:]_./-])'
|
||||
|
||||
# Guard against page-order changes resolving to the wrong architecture
|
||||
# before downloading a multi-gigabyte image.
|
||||
case "${PLATFORM,,}" in
|
||||
"x64" )
|
||||
if [[ "$lower" =~ ${separator}(arm64|a64) ]]; then
|
||||
@@ -450,6 +449,8 @@ downloadWindowsEval() {
|
||||
fi ;;
|
||||
esac
|
||||
|
||||
# During debug verification, compare the resolved filename with the static
|
||||
# catalog entry to expose unexpected changes on Microsoft's page.
|
||||
if enabled "$DEBUG" && enabled "$VERIFY" && [[ "${lang,,}" == "en"* ]]; then
|
||||
|
||||
compare=$(getMido "$id" "$lang" "")
|
||||
@@ -494,14 +495,11 @@ getMidoDetected() {
|
||||
# Derive the normal answer-file identity from the requested download route.
|
||||
case "$default" in
|
||||
*"-enterprise-ltsc-eval" )
|
||||
default="${default%-enterprise-ltsc-eval}-ltsc"
|
||||
;;
|
||||
default="${default%-enterprise-ltsc-eval}-ltsc" ;;
|
||||
*"-enterprise-iot-eval" )
|
||||
default="${default%-enterprise-iot-eval}-iot"
|
||||
;;
|
||||
default="${default%-enterprise-iot-eval}-iot" ;;
|
||||
*"-eval" )
|
||||
default="${default%-eval}"
|
||||
;;
|
||||
default="${default%-eval}" ;;
|
||||
esac
|
||||
|
||||
# Preserve a genuinely different DETECTED override.
|
||||
@@ -513,17 +511,13 @@ getMidoDetected() {
|
||||
# Select the answer-file identity for the source that actually succeeded.
|
||||
case "$source" in
|
||||
*"-enterprise-ltsc-eval" )
|
||||
detected="${source%-enterprise-ltsc-eval}-ltsc-eval"
|
||||
;;
|
||||
detected="${source%-enterprise-ltsc-eval}-ltsc-eval" ;;
|
||||
*"-enterprise-iot-eval" )
|
||||
detected="${source%-enterprise-iot-eval}-iot-eval"
|
||||
;;
|
||||
detected="${source%-enterprise-iot-eval}-iot-eval" ;;
|
||||
*"-eval" )
|
||||
detected="$source"
|
||||
;;
|
||||
detected="$source" ;;
|
||||
* )
|
||||
detected="${current:-$default}"
|
||||
;;
|
||||
detected="${current:-$default}" ;;
|
||||
esac
|
||||
|
||||
echo "$detected"
|
||||
@@ -535,21 +529,21 @@ downloadWindowsLtsc() {
|
||||
local id="$1"
|
||||
local lang="$2"
|
||||
local desc="$3"
|
||||
|
||||
local alternate alternate_desc
|
||||
|
||||
case "${id,,}" in
|
||||
"win11${PLATFORM,,}-enterprise-iot-eval" )
|
||||
alternate="win11${PLATFORM,,}-enterprise-ltsc-eval"
|
||||
;;
|
||||
alternate="win11${PLATFORM,,}-enterprise-ltsc-eval" ;;
|
||||
"win11${PLATFORM,,}-enterprise-ltsc-eval" )
|
||||
alternate="win11${PLATFORM,,}-enterprise-iot-eval"
|
||||
;;
|
||||
alternate="win11${PLATFORM,,}-enterprise-iot-eval" ;;
|
||||
* )
|
||||
error "Invalid VERSION specified, value \"$id\" is not recognized!"
|
||||
return 1
|
||||
;;
|
||||
return 1 ;;
|
||||
esac
|
||||
|
||||
# IoT and LTSC share related evaluation sources and may become unavailable
|
||||
# independently, so use the sibling edition as a compatibility fallback.
|
||||
if downloadWindowsEval "$id" "$lang" "$desc" > /dev/null 2>&1; then
|
||||
MIDO_SOURCE="$id"
|
||||
return 0
|
||||
@@ -574,6 +568,7 @@ getWindows() {
|
||||
local lang="$2"
|
||||
local desc="$3"
|
||||
local web_desc="$4"
|
||||
|
||||
local language edition
|
||||
|
||||
MIDO_SOURCE=""
|
||||
@@ -586,6 +581,8 @@ getWindows() {
|
||||
local web_msg="Requesting $web_desc from the Microsoft servers..."
|
||||
info "$msg" && html "$web_msg"
|
||||
|
||||
# These sources are only published in English, so avoid trying download
|
||||
# routes that cannot satisfy the requested language.
|
||||
case "${version,,}" in
|
||||
"win2008r2"* | \
|
||||
"win81${PLATFORM,,}"* | \
|
||||
@@ -598,6 +595,8 @@ getWindows() {
|
||||
fi ;;
|
||||
esac
|
||||
|
||||
# ARM64 downloads exist only for the explicitly supported Windows 11
|
||||
# routes; all other catalog entries remain x64-only.
|
||||
case "${version,,}" in
|
||||
"win10x64" ) ;;
|
||||
"win11${PLATFORM,,}" ) ;;
|
||||
@@ -610,6 +609,8 @@ getWindows() {
|
||||
fi ;;
|
||||
esac
|
||||
|
||||
# Prefer live Microsoft download routes. Unsupported or failed live routes
|
||||
# fall through to the configured static catalog below.
|
||||
case "${version,,}" in
|
||||
"win10x64" | "win11${PLATFORM,,}" )
|
||||
|
||||
@@ -621,8 +622,7 @@ getWindows() {
|
||||
"win11${PLATFORM,,}-enterprise-iot-eval" | \
|
||||
"win11${PLATFORM,,}-enterprise-ltsc-eval" )
|
||||
|
||||
downloadWindowsLtsc "$version" "$lang" "$edition" && return 0
|
||||
;;
|
||||
downloadWindowsLtsc "$version" "$lang" "$edition" && return 0 ;;
|
||||
|
||||
"win11${PLATFORM,,}-enterprise"* )
|
||||
|
||||
@@ -643,10 +643,11 @@ getWindows() {
|
||||
|
||||
* )
|
||||
error "Invalid VERSION specified, value \"$version\" is not recognized!"
|
||||
return 1
|
||||
;;
|
||||
return 1 ;;
|
||||
esac
|
||||
|
||||
# Static catalog URLs are the last resort after live Microsoft methods are
|
||||
# unavailable or have failed.
|
||||
MIDO_URL=$(getMido "$version" "$lang" "")
|
||||
[ -z "$MIDO_URL" ] && return 1
|
||||
|
||||
@@ -665,11 +666,10 @@ getBuild() {
|
||||
|
||||
local id="$1"
|
||||
local ret="$2"
|
||||
local url=""
|
||||
local name=""
|
||||
local build="$3"
|
||||
local edition=""
|
||||
|
||||
local file="catalog.xml"
|
||||
local url="" name="" edition=""
|
||||
|
||||
case "${id,,}" in
|
||||
"win11${PLATFORM,,}" )
|
||||
@@ -695,10 +695,9 @@ getCatalog() {
|
||||
|
||||
local id="$1"
|
||||
local ret="$2"
|
||||
local url=""
|
||||
local name=""
|
||||
local edition=""
|
||||
|
||||
local file="catalog.cab"
|
||||
local url="" name="" edition=""
|
||||
|
||||
if [[ "${id,,}" == "win11"* ]] && ! isCompatible; then
|
||||
# ARMv8.0 cannot run Windows 11 builds 24H2 and up.
|
||||
@@ -728,19 +727,108 @@ getCatalog() {
|
||||
"url" ) echo "$url" ;;
|
||||
"file" ) echo "$file" ;;
|
||||
"name" ) echo "$name" ;;
|
||||
"edition" ) echo '[Edition="'"${edition}"'"]' ;;
|
||||
"edition" ) echo "$edition" ;;
|
||||
*) echo "";;
|
||||
esac
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
getXmlTag() {
|
||||
parseESD() {
|
||||
|
||||
local tag="$1"
|
||||
local file="$2"
|
||||
local xml="$1"
|
||||
local version="$2"
|
||||
local lang="$3"
|
||||
local desc="$4"
|
||||
local edition="$5"
|
||||
local culture="$6"
|
||||
|
||||
xmllint --nonet --xpath "//$tag" "$file" 2>/dev/null | sed -E -e "s/<[\/]?$tag>//g" || true
|
||||
local xmlFile="${xml##*/}"
|
||||
local file_path file_sum file_size file_edition
|
||||
local file_culture file_match=0 language_match=0
|
||||
local records architecture language separator=$'\x1f'
|
||||
|
||||
ESD=""
|
||||
ESD_SUM=""
|
||||
ESD_SIZE=""
|
||||
|
||||
# Microsoft catalogs have used different XML namespaces. Match elements by
|
||||
# local name and flatten the catalog once so selection needs no temporary XML.
|
||||
if ! records=$(xmlstarlet sel \
|
||||
-T -t \
|
||||
-m "//*[local-name()='File']" \
|
||||
-v "normalize-space(*[local-name()='Architecture'])" \
|
||||
-o "$separator" \
|
||||
-v "normalize-space(*[local-name()='Edition'])" \
|
||||
-o "$separator" \
|
||||
-v "normalize-space(*[local-name()='LanguageCode'])" \
|
||||
-o "$separator" \
|
||||
-v "normalize-space(*[local-name()='FilePath'])" \
|
||||
-o "$separator" \
|
||||
-v "normalize-space(*[local-name()='Sha1'])" \
|
||||
-o "$separator" \
|
||||
-v "normalize-space(*[local-name()='Size'])" \
|
||||
-n \
|
||||
"$xml" 2>/dev/null); then
|
||||
|
||||
error "Failed to parse $xmlFile!"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# Track product/platform and language matches separately so failures can
|
||||
# distinguish an unavailable edition from an unavailable translation.
|
||||
while IFS="$separator" read -r \
|
||||
architecture file_edition file_culture \
|
||||
file_path file_sum file_size; do
|
||||
|
||||
[ -n "$architecture$file_path$file_sum$file_size$file_culture$file_edition" ] || continue
|
||||
|
||||
[ "${architecture,,}" = "${PLATFORM,,}" ] || continue
|
||||
|
||||
if [ -n "$edition" ] &&
|
||||
[ "${file_edition,,}" != "${edition,,}" ]; then
|
||||
continue
|
||||
fi
|
||||
|
||||
file_match=1
|
||||
|
||||
[ "${file_culture,,}" = "${culture,,}" ] || continue
|
||||
|
||||
language_match=1
|
||||
ESD="$file_path"
|
||||
ESD_SUM="$file_sum"
|
||||
ESD_SIZE="$file_size"
|
||||
break
|
||||
|
||||
done <<< "$records"
|
||||
|
||||
if (( ! file_match )); then
|
||||
desc=$(printEdition "$version" "$desc" "Y")
|
||||
error "No download link available for $desc!"
|
||||
return 1
|
||||
fi
|
||||
|
||||
if (( ! language_match )); then
|
||||
desc=$(printEdition "$version" "$desc" "Y")
|
||||
language=$(getLanguage "$lang" "desc")
|
||||
error "No download in the $language language available for $desc!"
|
||||
return 1
|
||||
fi
|
||||
|
||||
if [ -z "$ESD" ]; then
|
||||
error "Failed to find ESD URL in $xmlFile!"
|
||||
return 1
|
||||
fi
|
||||
|
||||
if [ -z "$ESD_SUM" ]; then
|
||||
error "Failed to find ESD checksum in $xmlFile!"
|
||||
return 1
|
||||
fi
|
||||
|
||||
if [ -z "$ESD_SIZE" ]; then
|
||||
error "Failed to find ESD filesize in $xmlFile!"
|
||||
return 1
|
||||
fi
|
||||
|
||||
return 0
|
||||
}
|
||||
@@ -751,12 +839,10 @@ getESD() {
|
||||
local version="$2"
|
||||
local lang="$3"
|
||||
local desc="$4"
|
||||
local file result culture
|
||||
local language edition catalog
|
||||
|
||||
local file culture log
|
||||
local edition catalog rc=0
|
||||
local xmlFile="products.xml"
|
||||
local esdFile="esd_edition.xml"
|
||||
local filterFile="products_filter.xml"
|
||||
local log
|
||||
|
||||
file=$(getCatalog "$version" "file")
|
||||
catalog=$(getCatalog "$version" "url")
|
||||
@@ -783,10 +869,12 @@ getESD() {
|
||||
return 1
|
||||
fi
|
||||
|
||||
# Preserve wget's status under errexit so its log can provide the actual
|
||||
# server or filesystem failure reason.
|
||||
{
|
||||
LC_ALL=C wget "$catalog" -O "$dir/$file" --no-verbose --timeout=30 \
|
||||
--no-http-keep-alive --output-file="$log"
|
||||
local rc=$?
|
||||
rc=$?
|
||||
} || :
|
||||
|
||||
if (( rc != 0 )); then
|
||||
@@ -813,6 +901,8 @@ getESD() {
|
||||
|
||||
rm -f "$log"
|
||||
|
||||
# Normal catalogs arrive as CAB archives, while pinned build catalogs are
|
||||
# already XML and only need the common filename.
|
||||
if [[ "$file" == *".xml" ]]; then
|
||||
|
||||
if ! mv -f "$dir/$file" "$dir/$xmlFile"; then
|
||||
@@ -832,61 +922,13 @@ getESD() {
|
||||
|
||||
fi
|
||||
|
||||
if [ ! -f "$dir/$xmlFile" ] || [ ! -s "$dir/$xmlFile" ]; then
|
||||
if [ ! -s "$dir/$xmlFile" ]; then
|
||||
error "Failed to find $xmlFile in $file!"
|
||||
return 1
|
||||
fi
|
||||
|
||||
local query='//File[Architecture="'${PLATFORM,,}'"]'"${edition}"''
|
||||
result=$(xmllint --nonet --xpath "${query}" "$dir/$xmlFile" 2>/dev/null || true)
|
||||
|
||||
if [ -z "$result" ]; then
|
||||
|
||||
query='//File[Architecture="'${PLATFORM^^}'"]'"${edition}"''
|
||||
result=$(xmllint --nonet --xpath "${query}" "$dir/$xmlFile" 2>/dev/null || true)
|
||||
|
||||
if [ -z "$result" ]; then
|
||||
desc=$(printEdition "$version" "$desc" "Y")
|
||||
language=$(getLanguage "$lang" "desc")
|
||||
error "No download link available for $desc!"
|
||||
return 1
|
||||
fi
|
||||
|
||||
fi
|
||||
|
||||
echo -e '<Catalog>' > "$dir/$filterFile"
|
||||
echo "$result" >> "$dir/$filterFile"
|
||||
echo -e '</Catalog>'>> "$dir/$filterFile"
|
||||
|
||||
result=$(xmllint --nonet --xpath "//File[LanguageCode=\"${culture,,}\"]" "$dir/$filterFile" 2>/dev/null || true)
|
||||
|
||||
if [ -z "$result" ]; then
|
||||
desc=$(printEdition "$version" "$desc" "Y")
|
||||
language=$(getLanguage "$lang" "desc")
|
||||
error "No download in the $language language available for $desc!"
|
||||
return 1
|
||||
fi
|
||||
|
||||
echo "$result" > "$dir/$esdFile"
|
||||
|
||||
ESD=$(getXmlTag "FilePath" "$dir/$esdFile")
|
||||
|
||||
if [ -z "$ESD" ]; then
|
||||
error "Failed to find ESD URL in $esdFile!"
|
||||
return 1
|
||||
fi
|
||||
|
||||
ESD_SUM=$(getXmlTag "Sha1" "$dir/$esdFile")
|
||||
|
||||
if [ -z "$ESD_SUM" ]; then
|
||||
error "Failed to find ESD checksum in $esdFile!"
|
||||
return 1
|
||||
fi
|
||||
|
||||
ESD_SIZE=$(getXmlTag "Size" "$dir/$esdFile")
|
||||
|
||||
if [ -z "$ESD_SIZE" ]; then
|
||||
error "Failed to find ESD filesize in $esdFile!"
|
||||
if ! parseESD \
|
||||
"$dir/$xmlFile" "$version" "$lang" "$desc" "$edition" "$culture"; then
|
||||
return 1
|
||||
fi
|
||||
|
||||
@@ -898,11 +940,12 @@ isCompressed() {
|
||||
|
||||
local url="${1%%\?*}"
|
||||
|
||||
# The ReactOS latest-build endpoint returns an archive without a filename
|
||||
# extension, so recognize its path explicitly.
|
||||
case "${url,,}" in
|
||||
*.7z | *.zip | *.rar | *.tar | *.cab | *.cpio | \
|
||||
*.lzh | *.lha | *.xar | */latest-x86-gcc-lin-rel )
|
||||
return 0
|
||||
;;
|
||||
return 0 ;;
|
||||
esac
|
||||
|
||||
return 1
|
||||
@@ -926,6 +969,9 @@ verifyFile() {
|
||||
|
||||
[ -z "$check" ] && return 0
|
||||
enabled "$VERIFY" || return 0
|
||||
|
||||
# Microsoft ESD catalogs publish SHA1, while current mirror metadata normally
|
||||
# uses SHA256; the digest length identifies which algorithm is required.
|
||||
[[ "${#check}" == "40" ]] && algo="SHA1"
|
||||
|
||||
local msg="Verifying downloaded ISO..."
|
||||
@@ -975,10 +1021,13 @@ downloadFile() {
|
||||
local desc="$4"
|
||||
local web_desc="$5"
|
||||
local connections="${6:-1}"
|
||||
|
||||
local domain dots
|
||||
local msg="Downloading $web_desc"
|
||||
local console_msg="Downloading $desc"
|
||||
local domain dots
|
||||
|
||||
# Keep mirror messages concise by reducing subdomains to the final two
|
||||
# labels, while Microsoft downloads retain the generic description.
|
||||
domain=$(echo "$url" | awk -F/ '{print $3}')
|
||||
dots=$(echo "$domain" | tr -cd '.' | wc -c)
|
||||
(( dots > 1 )) && domain=$(expr "$domain" : '.*\.\(.*\..*\)')
|
||||
@@ -1007,8 +1056,11 @@ tryDownload() {
|
||||
local desc="$6"
|
||||
local seconds="$7"
|
||||
local web_desc="$8"
|
||||
|
||||
local total minimum="104857600"
|
||||
|
||||
# Compressed archives can legitimately be much smaller than the ISO they
|
||||
# contain, so use a lower sanity threshold until extraction.
|
||||
if isCompressed "$url"; then
|
||||
minimum="10485760"
|
||||
fi
|
||||
@@ -1059,8 +1111,8 @@ fallbackEnglish() {
|
||||
local lang="$3"
|
||||
local desc="$4"
|
||||
local web_desc="$5"
|
||||
local culture web_msg
|
||||
|
||||
local culture web_msg
|
||||
local msg="No working download method was found for $desc, falling back to English..."
|
||||
info "$msg"
|
||||
|
||||
@@ -1086,12 +1138,11 @@ downloadImage() {
|
||||
local iso="$1"
|
||||
local version="$2"
|
||||
local lang="$3"
|
||||
|
||||
local requested="$version"
|
||||
local tried="n"
|
||||
local success="n"
|
||||
local seconds="5"
|
||||
local detected="$DETECTED"
|
||||
local url sum size base desc web_desc language i
|
||||
local detected="$DETECTED"
|
||||
local tried="n" success="n" seconds="5"
|
||||
local i url sum size base language desc web_desc
|
||||
|
||||
if [[ "${version,,}" == "http"* ]]; then
|
||||
|
||||
@@ -1127,6 +1178,8 @@ downloadImage() {
|
||||
desc+=" in $language"
|
||||
fi
|
||||
|
||||
# Prefer a live Microsoft URL and retry link generation once before moving
|
||||
# on to ESD catalogs or mirrors.
|
||||
if isMido "$version" "$lang"; then
|
||||
|
||||
tried="y"
|
||||
@@ -1167,7 +1220,13 @@ downloadImage() {
|
||||
fi
|
||||
fi
|
||||
|
||||
if switchEdition version; then
|
||||
# Some editions share another download route. Update the effective version
|
||||
# before looking up ESD catalogs and mirrors.
|
||||
if version=$(switchEdition "$version"); then
|
||||
|
||||
if ! enabled "${DETECTED_ORG:-}"; then
|
||||
DETECTED="${SUGGEST:-$version}"
|
||||
fi
|
||||
|
||||
desc=$(printVariant "$DETECTED" "" "Y")
|
||||
web_desc=$(printVariant "$DETECTED" "")
|
||||
@@ -1196,6 +1255,8 @@ downloadImage() {
|
||||
|
||||
if [[ "$success" == "y" ]]; then
|
||||
|
||||
# Standalone ESD media requires a different extraction path, so expose
|
||||
# its real extension through the active ISO variable.
|
||||
ISO="${ISO%.*}.esd"
|
||||
|
||||
if tryDownload "$ISO" "$ESD" "$ESD_SUM" "$ESD_SIZE" "$lang" "$desc" "$seconds" "$web_desc"; then
|
||||
|
||||
Reference in New Issue
Block a user