mido.sh 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576
  1. #!/usr/bin/env bash
  2. set -Eeuo pipefail
  3. handle_curl_error() {
  4. local error_code="$1"
  5. case "$error_code" in
  6. 6) error "Failed to resolve Microsoft servers! Is there an Internet connection?" ;;
  7. 7) error "Failed to contact Microsoft servers! Is there an Internet connection or is the server down?" ;;
  8. 8) error "Microsoft servers returned a malformed HTTP response!" ;;
  9. 22) error "Microsoft servers returned a failing HTTP status code!" ;;
  10. 23) error "Failed at writing Windows media to disk! Out of disk space or permission error?" ;;
  11. 26) error "Ran out of memory during download!" ;;
  12. 28) error "Connection timed out to Microsoft server!" ;;
  13. 35) error "SSL connection error from Microsoft server!" ;;
  14. 36) error "Failed to continue earlier download!" ;;
  15. 52) error "Received no data from the Microsoft server!" ;;
  16. 63) error "Microsoft servers returned an unexpectedly large response!" ;;
  17. # POSIX defines exit statuses 1-125 as usable by us
  18. # https://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_08_02
  19. $((error_code <= 125)))
  20. # Must be some other server or network error (possibly with this specific request/file)
  21. # This is when accounting for all possible errors in the curl manual assuming a correctly formed curl command and an HTTP(S) request, using only the curl features we're using, and a sane build
  22. error "Miscellaneous server or network error, reason: $error_code"
  23. ;;
  24. 126 | 127 ) error "Curl command not found!" ;;
  25. # Exit statuses are undefined by POSIX beyond this point
  26. *)
  27. case "$(kill -l "$error_code")" in
  28. # Signals defined to exist by POSIX:
  29. # https://pubs.opengroup.org/onlinepubs/009695399/basedefs/signal.h.html
  30. INT) error "Curl was interrupted!" ;;
  31. # There could be other signals but these are most common
  32. SEGV | ABRT ) error "Curl crashed! Failed exploitation attempt? Please report any core dumps to curl developers." ;;
  33. *) error "Curl terminated due to a fatal signal!" ;;
  34. esac
  35. esac
  36. return 1
  37. }
  38. download_windows() {
  39. local id="$1"
  40. local lang="$2"
  41. local sku_id=""
  42. local language=""
  43. local session_id=""
  44. local browser_version=""
  45. local windows_version=""
  46. local iso_download_link=""
  47. local product_edition_id=""
  48. local iso_download_link_html=""
  49. local iso_download_page_html=""
  50. local language_skuid_table_html=""
  51. case "${id,,}" in
  52. "win11${PLATFORM,,}" ) windows_version="11" ;;
  53. "win10${PLATFORM,,}" ) windows_version="10" ;;
  54. "win81${PLATFORM,,}" ) windows_version="8" ;;
  55. * ) error "Invalid VERSION specified, value \"$id\" is not recognized!" && return 1 ;;
  56. esac
  57. language=$(getLanguage "$lang" "name")
  58. local url="https://www.microsoft.com/en-us/software-download/windows$windows_version"
  59. case "$windows_version" in
  60. 8 | 10) url="${url}ISO";;
  61. esac
  62. # Determine approximate latest Firefox release
  63. browser_version="$((124 + ($(date +%s) - 1710892800) / 2419200))"
  64. local user_agent="Mozilla/5.0 (X11; Linux x86_64; rv:${browser_version}.0) Gecko/20100101 Firefox/${browser_version}.0"
  65. # 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
  66. session_id="$(cat /proc/sys/kernel/random/uuid 2> /dev/null || uuidgen --random)"
  67. # Get product edition ID for latest release of given Windows version
  68. # Product edition ID: This specifies both the Windows release (e.g. 22H2) and edition ("multi-edition" is default, either Home/Pro/Edu/etc., we select "Pro" in the answer files) in one number
  69. # This is the *only* request we make that Fido doesn't. Fido manually maintains a list of all the Windows release/edition product edition IDs in its script (see: $WindowsVersions array). This is helpful for downloading older releases (e.g. Windows 10 1909, 21H1, etc.) but we always want to get the newest release which is why we get this value dynamically
  70. # Also, keeping a "$WindowsVersions" array like Fido does would be way too much of a maintenance burden
  71. # Remove "Accept" header that curl sends by default
  72. [[ "$DEBUG" == [Yy1]* ]] && echo " - Parsing download page: ${url}"
  73. iso_download_page_html="$(curl --silent --max-time 30 --user-agent "$user_agent" --header "Accept:" --max-filesize 1M --fail --proto =https --tlsv1.2 --http1.1 -- "$url")" || {
  74. handle_curl_error $?
  75. return $?
  76. }
  77. [[ "$DEBUG" == [Yy1]* ]] && echo -n "Getting Product edition ID: "
  78. # tr: Filter for only numerics to prevent HTTP parameter injection
  79. # head -c was recently added to POSIX: https://austingroupbugs.net/view.php?id=407
  80. product_edition_id="$(echo "$iso_download_page_html" | grep -Eo '<option value="[0-9]+">Windows' | cut -d '"' -f 2 | head -n 1 | tr -cd '0-9' | head -c 16)"
  81. [[ "$DEBUG" == [Yy1]* ]] && echo "$product_edition_id"
  82. [[ "$DEBUG" == [Yy1]* ]] && echo "Permit Session ID: $session_id"
  83. # Permit Session ID
  84. # "org_id" is always the same value
  85. curl --silent --max-time 30 --output /dev/null --user-agent "$user_agent" --header "Accept:" --max-filesize 100K --fail --proto =https --tlsv1.2 --http1.1 -- "https://vlscppe.microsoft.com/tags?org_id=y6jn8c31&session_id=$session_id" || {
  86. # This should only happen if there's been some change to how this API works
  87. handle_curl_error $?
  88. return $?
  89. }
  90. # Extract everything after the last slash
  91. local url_segment_parameter="${url##*/}"
  92. [[ "$DEBUG" == [Yy1]* ]] && echo -n "Getting language SKU ID: "
  93. # Get language -> skuID association table
  94. # SKU ID: This specifies the language of the ISO. We always use "English (United States)", however, the SKU for this changes with each Windows release
  95. # We must make this request so our next one will be allowed
  96. # --data "" is required otherwise no "Content-Length" header will be sent causing HTTP response "411 Length Required"
  97. language_skuid_table_html="$(curl --silent --max-time 30 --request POST --user-agent "$user_agent" --data "" --header "Accept:" --max-filesize 10K --fail --proto =https --tlsv1.2 --http1.1 -- "https://www.microsoft.com/en-US/api/controls/contentinclude/html?pageId=a8f8f489-4c7f-463a-9ca6-5cff94d8d041&host=www.microsoft.com&segments=software-download,$url_segment_parameter&query=&action=getskuinformationbyproductedition&sessionId=$session_id&productEditionId=$product_edition_id&sdVersion=2")" || {
  98. handle_curl_error $?
  99. return $?
  100. }
  101. # tr: Filter for only alphanumerics or "-" to prevent HTTP parameter injection
  102. sku_id="$(echo "$language_skuid_table_html" | grep -m 1 ">${language}<" | sed 's/&quot;//g' | cut -d ',' -f 1 | cut -d ':' -f 2 | tr -cd '[:alnum:]-' | head -c 16)"
  103. if [ -z "$sku_id" ]; then
  104. language=$(getLanguage "$lang" "desc")
  105. error "No download for the $language language available!"
  106. return 1
  107. fi
  108. [[ "$DEBUG" == [Yy1]* ]] && echo "$sku_id"
  109. [[ "$DEBUG" == [Yy1]* ]] && echo "Getting ISO download link..."
  110. # Get ISO download link
  111. # If any request is going to be blocked by Microsoft it's always this last one (the previous requests always seem to succeed)
  112. # --referer: Required by Microsoft servers to allow request
  113. iso_download_link_html="$(curl --silent --max-time 30 --request POST --user-agent "$user_agent" --data "" --referer "$url" --header "Accept:" --max-filesize 100K --fail --proto =https --tlsv1.2 --http1.1 -- "https://www.microsoft.com/en-US/api/controls/contentinclude/html?pageId=6e2a1789-ef16-4f27-a296-74ef7ef5d96b&host=www.microsoft.com&segments=software-download,$url_segment_parameter&query=&action=GetProductDownloadLinksBySku&sessionId=$session_id&skuId=$sku_id&language=English&sdVersion=2")"
  114. if ! [ "$iso_download_link_html" ]; then
  115. # This should only happen if there's been some change to how this API works
  116. error "Microsoft servers gave us an empty response to our request for an automated download."
  117. return 1
  118. fi
  119. if echo "$iso_download_link_html" | grep -q "We are unable to complete your request at this time."; then
  120. error "Microsoft blocked the automated download request based on your IP address."
  121. return 1
  122. fi
  123. # Filter for 64-bit ISO download URL
  124. # sed: HTML decode "&" character
  125. # tr: Filter for only alphanumerics or punctuation
  126. iso_download_link="$(echo "$iso_download_link_html" | grep -o "https://software.download.prss.microsoft.com.*IsoX64" | cut -d '"' -f 1 | sed 's/&amp;/\&/g' | tr -cd '[:alnum:][:punct:]')"
  127. if ! [ "$iso_download_link" ]; then
  128. # This should only happen if there's been some change to the download endpoint web address
  129. error "Microsoft servers gave us no download link to our request for an automated download!"
  130. return 1
  131. fi
  132. MIDO_URL="$iso_download_link"
  133. return 0
  134. }
  135. download_windows_eval() {
  136. local id="$1"
  137. local lang="$2"
  138. local culture=""
  139. local language=""
  140. local windows_version=""
  141. local enterprise_type=""
  142. case "${id,,}" in
  143. "win11${PLATFORM,,}-enterprise-eval" )
  144. windows_version="windows-11-enterprise"
  145. enterprise_type="enterprise" ;;
  146. "win10${PLATFORM,,}-enterprise-eval" )
  147. windows_version="windows-10-enterprise"
  148. enterprise_type="enterprise" ;;
  149. "win10${PLATFORM,,}-enterprise-ltsc-eval" )
  150. windows_version="windows-10-enterprise"
  151. enterprise_type="ltsc" ;;
  152. "win2022-eval" )
  153. windows_version="windows-server-2022"
  154. enterprise_type="server" ;;
  155. "win2019-eval" )
  156. windows_version="windows-server-2019"
  157. enterprise_type="server" ;;
  158. "win2016-eval" )
  159. windows_version="windows-server-2016"
  160. enterprise_type="server" ;;
  161. "win2012r2-eval" )
  162. windows_version="windows-server-2012-r2"
  163. enterprise_type="server" ;;
  164. * )
  165. error "Invalid VERSION specified, value \"$id\" is not recognized!" && return 1 ;;
  166. esac
  167. culture=$(getLanguage "$lang" "culture")
  168. local country="${culture#*-}"
  169. local iso_download_page_html=""
  170. local url="https://www.microsoft.com/en-us/evalcenter/download-$windows_version"
  171. [[ "$DEBUG" == [Yy1]* ]] && echo "Parsing download page: ${url}"
  172. iso_download_page_html="$(curl --silent --max-time 30 --location --max-filesize 1M --fail --proto =https --tlsv1.2 --http1.1 -- "$url")" || {
  173. handle_curl_error $?
  174. return $?
  175. }
  176. if ! [ "$iso_download_page_html" ]; then
  177. # This should only happen if there's been some change to where this download page is located
  178. error "Windows server download page gave us an empty response"
  179. return 1
  180. fi
  181. [[ "$DEBUG" == [Yy1]* ]] && echo "Getting download link.."
  182. iso_download_links="$(echo "$iso_download_page_html" | grep -o "https://go.microsoft.com/fwlink/p/?LinkID=[0-9]\+&clcid=0x[0-9a-z]\+&culture=${culture,,}&country=${country^^}")" || {
  183. # This should only happen if there's been some change to the download endpoint web address
  184. if [[ "${lang,,}" == "en" ]] || [[ "${lang,,}" == "en-"* ]]; then
  185. error "Windows server download page gave us no download link!"
  186. else
  187. language=$(getLanguage "$lang" "desc")
  188. error "No download for the $language language available!"
  189. fi
  190. return 1
  191. }
  192. # Limit untrusted size for input validation
  193. iso_download_links="$(echo "$iso_download_links" | head -c 1024)"
  194. case "$enterprise_type" in
  195. # Select x64 download link
  196. "enterprise") iso_download_link=$(echo "$iso_download_links" | head -n 2 | tail -n 1) ;;
  197. # Select x64 LTSC download link
  198. "ltsc") iso_download_link=$(echo "$iso_download_links" | head -n 4 | tail -n 1) ;;
  199. *) iso_download_link="$iso_download_links" ;;
  200. esac
  201. # Follow redirect so proceeding log message is useful
  202. # This is a request we make this Fido doesn't
  203. # We don't need to set "--max-filesize" here because this is a HEAD request and the output is to /dev/null anyway
  204. iso_download_link="$(curl --silent --max-time 30 --location --output /dev/null --silent --write-out "%{url_effective}" --head --fail --proto =https --tlsv1.2 --http1.1 -- "$iso_download_link")" || {
  205. # This should only happen if the Microsoft servers are down
  206. handle_curl_error $?
  207. return $?
  208. }
  209. MIDO_URL="$iso_download_link"
  210. return 0
  211. }
  212. getWindows() {
  213. local version="$1"
  214. local lang="$2"
  215. local desc="$3"
  216. local language
  217. local msg="Requesting $desc from Microsoft server..."
  218. info "$msg" && html "$msg"
  219. case "${version,,}" in
  220. "win81${PLATFORM,,}" | "win10${PLATFORM,,}" | "win11${PLATFORM,,}" )
  221. download_windows "$version" "$lang" && return 0
  222. ;;
  223. "win11${PLATFORM,,}-enterprise-eval" )
  224. download_windows_eval "$version" "$lang" && return 0
  225. ;;
  226. "win10${PLATFORM,,}-enterprise-eval" | "win10${PLATFORM,,}-enterprise-ltsc-eval" )
  227. download_windows_eval "$version" "$lang" && return 0
  228. ;;
  229. "win2022-eval" | "win2019-eval" | "win2016-eval" | "win2012r2-eval" )
  230. download_windows_eval "$version" "$lang" && return 0
  231. ;;
  232. "win81${PLATFORM,,}-enterprise-eval" )
  233. if [[ "${lang,,}" == "en" ]] || [[ "${lang,,}" == "en-"* ]]; then
  234. MIDO_URL="https://download.microsoft.com/download/B/9/9/B999286E-0A47-406D-8B3D-5B5AD7373A4A/9600.17050.WINBLUE_REFRESH.140317-1640_X64FRE_ENTERPRISE_EVAL_EN-US-IR3_CENA_X64FREE_EN-US_DV9.ISO" && return 0
  235. fi
  236. language=$(getLanguage "$lang" "desc")
  237. error "No download for the $language language available!"
  238. ;;
  239. "win2008r2" )
  240. if [[ "${lang,,}" == "en" ]] || [[ "${lang,,}" == "en-"* ]]; then
  241. MIDO_URL="https://download.microsoft.com/download/4/1/D/41DEA7E0-B30D-4012-A1E3-F24DC03BA1BB/7601.17514.101119-1850_x64fre_server_eval_en-us-GRMSXEVAL_EN_DVD.iso" && return 0
  242. fi
  243. language=$(getLanguage "$lang" "desc")
  244. error "No download for the $language language available!"
  245. ;;
  246. * ) error "Invalid VERSION specified, value \"$version\" is not recognized!" ;;
  247. esac
  248. MIDO_URL=""
  249. return 1
  250. }
  251. getCatalog() {
  252. local id="$1"
  253. local ret="$2"
  254. local url=""
  255. local name=""
  256. local edition=""
  257. case "${id,,}" in
  258. "win11${PLATFORM,,}" )
  259. edition="Professional"
  260. name="Windows 11 Pro"
  261. url="https://go.microsoft.com/fwlink?linkid=2156292" ;;
  262. "win10${PLATFORM,,}" )
  263. edition="Professional"
  264. name="Windows 10 Pro"
  265. url="https://go.microsoft.com/fwlink/?LinkId=841361" ;;
  266. "win11${PLATFORM,,}-enterprise" | "win11${PLATFORM,,}-enterprise-eval")
  267. edition="Enterprise"
  268. name="Windows 11 Enterprise"
  269. url="https://go.microsoft.com/fwlink?linkid=2156292" ;;
  270. "win10${PLATFORM,,}-enterprise" | "win10${PLATFORM,,}-enterprise-eval" )
  271. edition="Enterprise"
  272. name="Windows 10 Enterprise"
  273. url="https://go.microsoft.com/fwlink/?LinkId=841361" ;;
  274. esac
  275. case "${ret,,}" in
  276. "url" ) echo "$url" ;;
  277. "name" ) echo "$name" ;;
  278. "edition" ) echo "$edition" ;;
  279. *) echo "";;
  280. esac
  281. return 0
  282. }
  283. getESD() {
  284. local dir="$1"
  285. local version="$2"
  286. local lang="$3"
  287. local desc="$4"
  288. local culture
  289. local language
  290. local editionName
  291. local winCatalog size
  292. culture=$(getLanguage "$lang" "culture")
  293. winCatalog=$(getCatalog "$version" "url")
  294. editionName=$(getCatalog "$version" "edition")
  295. if [ -z "$winCatalog" ] || [ -z "$editionName" ]; then
  296. error "Invalid VERSION specified, value \"$version\" is not recognized!" && return 1
  297. fi
  298. local msg="Downloading product information from Microsoft server..."
  299. info "$msg" && html "$msg"
  300. rm -rf "$dir"
  301. mkdir -p "$dir"
  302. local wFile="catalog.cab"
  303. local xFile="products.xml"
  304. local eFile="esd_edition.xml"
  305. local fFile="products_filter.xml"
  306. { wget "$winCatalog" -O "$dir/$wFile" -q --timeout=30; rc=$?; } || :
  307. (( rc == 4 )) && error "Failed to download $winCatalog , network failure!" && return 1
  308. (( rc != 0 )) && error "Failed to download $winCatalog , reason: $rc" && return 1
  309. cd "$dir"
  310. if ! cabextract "$wFile" > /dev/null; then
  311. cd /run
  312. error "Failed to extract $wFile!" && return 1
  313. fi
  314. cd /run
  315. if [ ! -s "$dir/$xFile" ]; then
  316. error "Failed to find $xFile in $wFile!" && return 1
  317. fi
  318. local edQuery='//File[Architecture="'${PLATFORM}'"][Edition="'${editionName}'"]'
  319. echo -e '<Catalog>' > "$dir/$fFile"
  320. xmllint --nonet --xpath "${edQuery}" "$dir/$xFile" >> "$dir/$fFile" 2>/dev/null
  321. echo -e '</Catalog>'>> "$dir/$fFile"
  322. xmllint --nonet --xpath "//File[LanguageCode=\"${culture,,}\"]" "$dir/$fFile" >"$dir/$eFile"
  323. size=$(stat -c%s "$dir/$eFile")
  324. if ((size<20)); then
  325. language=$(getLanguage "$lang" "desc")
  326. error "the $language language is not supported by this download method!" && return 1
  327. fi
  328. local tag="FilePath"
  329. ESD=$(xmllint --nonet --xpath "//$tag" "$dir/$eFile" | sed -E -e "s/<[\/]?$tag>//g")
  330. if [ -z "$ESD" ]; then
  331. error "Failed to find ESD URL in $eFile!" && return 1
  332. fi
  333. tag="Sha1"
  334. ESD_SUM=$(xmllint --nonet --xpath "//$tag" "$dir/$eFile" | sed -E -e "s/<[\/]?$tag>//g")
  335. tag="Size"
  336. ESD_SIZE=$(xmllint --nonet --xpath "//$tag" "$dir/$eFile" | sed -E -e "s/<[\/]?$tag>//g")
  337. rm -rf "$dir"
  338. return 0
  339. }
  340. verifyFile() {
  341. local iso="$1"
  342. local size="$2"
  343. local total="$3"
  344. local check="$4"
  345. if [ -n "$size" ] && [[ "$total" != "$size" ]] && [[ "$size" != "0" ]]; then
  346. warn "The downloaded file has an unexpected size: $total bytes, while expected value was: $size bytes. Please report this at $SUPPORT/issues"
  347. fi
  348. local hash=""
  349. local algo="SHA256"
  350. [ -z "$check" ] && return 0
  351. [[ "$VERIFY" != [Yy1]* ]] && return 0
  352. [[ "${#check}" == "40" ]] && algo="SHA1"
  353. local msg="Verifying downloaded ISO..."
  354. info "$msg" && html "$msg"
  355. if [[ "${algo,,}" != "sha256" ]]; then
  356. hash=$(sha1sum "$iso" | cut -f1 -d' ')
  357. else
  358. hash=$(sha256sum "$iso" | cut -f1 -d' ')
  359. fi
  360. if [[ "$hash" == "$check" ]]; then
  361. info "Succesfully verified ISO!" && return 0
  362. fi
  363. error "The downloaded file has an invalid $algo checksum: $hash , while expected value was: $check. Please report this at $SUPPORT/issues"
  364. rm -f "$iso"
  365. return 1
  366. }
  367. downloadFile() {
  368. local iso="$1"
  369. local url="$2"
  370. local sum="$3"
  371. local size="$4"
  372. local lang="$5"
  373. local desc="$6"
  374. local rc total progress domain dots
  375. rm -f "$iso"
  376. # Check if running with interactive TTY or redirected to docker log
  377. if [ -t 1 ]; then
  378. progress="--progress=bar:noscroll"
  379. else
  380. progress="--progress=dot:giga"
  381. fi
  382. local msg="Downloading $desc..."
  383. html "$msg"
  384. domain=$(echo "$url" | awk -F/ '{print $3}')
  385. dots=$(echo "$domain" | tr -cd '.' | wc -c)
  386. (( dots > 1 )) && domain=$(expr "$domain" : '.*\.\(.*\..*\)')
  387. if [ -n "$domain" ] && [[ "${domain,,}" != *"microsoft.com" ]]; then
  388. msg="Downloading $desc from $domain..."
  389. fi
  390. info "$msg"
  391. /run/progress.sh "$iso" "$size" "Downloading $desc ([P])..." &
  392. { wget "$url" -O "$iso" -q --timeout=30 --show-progress "$progress"; rc=$?; } || :
  393. fKill "progress.sh"
  394. if (( rc == 0 )) && [ -f "$iso" ]; then
  395. total=$(stat -c%s "$iso")
  396. if [ "$total" -gt 100000000 ]; then
  397. ! verifyFile "$iso" "$size" "$total" "$sum" && return 1
  398. html "Download finished successfully..." && return 0
  399. fi
  400. fi
  401. if (( rc != 4 )); then
  402. error "Failed to download $url , reason: $rc"
  403. else
  404. error "Failed to download $url , network failure!"
  405. fi
  406. rm -f "$iso"
  407. return 1
  408. }
  409. downloadImage() {
  410. local iso="$1"
  411. local version="$2"
  412. local lang="$3"
  413. local tried="n"
  414. local url sum size base desc language
  415. if [[ "${version,,}" == "http"* ]]; then
  416. base=$(basename "$iso")
  417. desc=$(fromFile "$base")
  418. downloadFile "$iso" "$version" "" "" "" "$desc" && return 0
  419. return 1
  420. fi
  421. if ! validVersion "$version" "en"; then
  422. error "Invalid VERSION specified, value \"$version\" is not recognized!" && return 1
  423. fi
  424. desc=$(printVersion "$version" "")
  425. if [[ "${lang,,}" != "en" ]] && [[ "${lang,,}" != "en-"* ]]; then
  426. language=$(getLanguage "$lang" "desc")
  427. if ! validVersion "$version" "$lang"; then
  428. desc=$(printEdition "$version" "$desc")
  429. error "The $language language version of $desc is not available, please switch to English." && return 1
  430. fi
  431. desc="$desc in $language"
  432. fi
  433. if isMido "$version" "$lang"; then
  434. tried="y"
  435. if getWindows "$version" "$lang" "$desc"; then
  436. size=$(getMido "$version" "$lang" "size" )
  437. sum=$(getMido "$version" "$lang" "sum")
  438. downloadFile "$iso" "$MIDO_URL" "$sum" "$size" "$lang" "$desc" && return 0
  439. fi
  440. fi
  441. switchEdition "$version"
  442. if isESD "$version" "$lang"; then
  443. if [[ "$tried" != "n" ]]; then
  444. info "Failed to download $desc, will try a diferent method now..."
  445. fi
  446. tried="y"
  447. if getESD "$TMP/esd" "$version" "$lang" "$desc"; then
  448. ISO="${ISO%.*}.esd"
  449. downloadFile "$ISO" "$ESD" "$ESD_SUM" "$ESD_SIZE" "$lang" "$desc" && return 0
  450. ISO="$iso"
  451. fi
  452. fi
  453. for ((i=1;i<=MIRRORS;i++)); do
  454. url=$(getLink "$i" "$version" "$lang")
  455. if [ -n "$url" ]; then
  456. if [[ "$tried" != "n" ]]; then
  457. info "Failed to download $desc, will try another mirror now..."
  458. fi
  459. tried="y"
  460. size=$(getSize "$i" "$version" "$lang")
  461. sum=$(getHash "$i" "$version" "$lang")
  462. downloadFile "$iso" "$url" "$sum" "$size" "$lang" "$desc" && return 0
  463. fi
  464. done
  465. return 1
  466. }
  467. return 0