mirror of
https://github.com/dortania/OpenCore-Legacy-Patcher.git
synced 2026-04-18 05:34:42 +10:00
kdk_handler: Enumerate KDKs from Apple developer portal
This commit is contained in:
@@ -1,19 +1,37 @@
|
|||||||
# Kernel Debug Kit downloader
|
# Kernel Debug Kit downloader
|
||||||
|
|
||||||
|
import datetime
|
||||||
|
from typing import cast
|
||||||
|
import urllib.parse
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
import requests, urllib
|
|
||||||
|
import packaging.version
|
||||||
|
import requests
|
||||||
|
|
||||||
from resources import utilities
|
from resources import utilities
|
||||||
|
from resources.constants import Constants
|
||||||
|
|
||||||
SESSION = requests.Session()
|
SESSION = requests.Session()
|
||||||
|
|
||||||
|
|
||||||
class kernel_debug_kit_handler:
|
class kernel_debug_kit_handler:
|
||||||
|
def __init__(self, constants: Constants):
|
||||||
|
|
||||||
def __init__(self, constants):
|
|
||||||
self.constants = constants
|
self.constants = constants
|
||||||
|
|
||||||
|
def get_available_kdks(self):
|
||||||
|
KDK_API_LINK = "https://kdk-api.dhinak.net/v1"
|
||||||
|
|
||||||
def get_closest_match(self, host_version: str, host_build: str):
|
print("- Fetching available KDKs")
|
||||||
|
|
||||||
|
results = SESSION.get(KDK_API_LINK, headers={"User-Agent": f"OCLP/{self.constants.patcher_version}"})
|
||||||
|
|
||||||
|
if results.status_code != 200:
|
||||||
|
print(" - Could not fetch KDK list")
|
||||||
|
return None
|
||||||
|
|
||||||
|
return results.json().sort(key=lambda x: (packaging.version.parse(x["version"]), datetime.datetime.fromisoformat(x["date"])), reversed=True)
|
||||||
|
|
||||||
|
def get_closest_match_legacy(self, host_version: str, host_build: str):
|
||||||
# Get the closest match to the provided version
|
# Get the closest match to the provided version
|
||||||
# KDKs are generally a few days late, so we'll rely on N-1 matching
|
# KDKs are generally a few days late, so we'll rely on N-1 matching
|
||||||
|
|
||||||
@@ -21,12 +39,7 @@ class kernel_debug_kit_handler:
|
|||||||
|
|
||||||
OS_DATABASE_LINK = "https://api.appledb.dev/main.json"
|
OS_DATABASE_LINK = "https://api.appledb.dev/main.json"
|
||||||
|
|
||||||
newest_build = {
|
parsed_host_version = cast(packaging.version.Version, packaging.version.parse(host_version))
|
||||||
"name": "",
|
|
||||||
"version": "",
|
|
||||||
"build": "",
|
|
||||||
"date": "",
|
|
||||||
}
|
|
||||||
|
|
||||||
print(f"- Checking closest match for: {host_version} build {host_build}")
|
print(f"- Checking closest match for: {host_version} build {host_build}")
|
||||||
|
|
||||||
@@ -36,65 +49,27 @@ class kernel_debug_kit_handler:
|
|||||||
print(" - Could not fetch database")
|
print(" - Could not fetch database")
|
||||||
return None, ""
|
return None, ""
|
||||||
|
|
||||||
results = results.json()
|
macos_builds = [i for i in results.json()["ios"] if i["osType"] == "macOS"]
|
||||||
|
macos_builds.sort(key=lambda x: (packaging.version.parse(x["version"]), datetime.datetime.fromisoformat(x["released"])), reverse=True)
|
||||||
|
|
||||||
# Iterate through, find build that is closest to the host version
|
# Iterate through, find build that is closest to the host version
|
||||||
# Use date to determine which is closest
|
# Use date to determine which is closest
|
||||||
if "ios" in results:
|
for build_info in macos_builds:
|
||||||
for variant in results["ios"]:
|
if build_info["osType"] == "macOS":
|
||||||
if variant["osStr"] == "macOS":
|
version = cast(packaging.version.Version, packaging.version.parse(build_info["version"]))
|
||||||
|
if version == parsed_host_version:
|
||||||
name = variant["version"]
|
# Skip, as we want the next closest match
|
||||||
version = name.split(" ")[0]
|
continue
|
||||||
build = variant["build"]
|
elif version <= parsed_host_version and version.major == parsed_host_version.major and version.minor == parsed_host_version.minor:
|
||||||
date = variant["released"]
|
# The KDK list is already sorted by date then version, so the first match is the closest
|
||||||
|
print(f"- Closest match: {build_info['version']} build {build_info['build']}")
|
||||||
if version != host_version:
|
return self.generate_kdk_link(build_info["version"], build_info["build"]), build_info["build"]
|
||||||
# Check if this is a security update (ie. 13.0.1)
|
|
||||||
version_split = version.split(".")
|
|
||||||
host_version_split = host_version.split(".")
|
|
||||||
if len(version_split) >= 2 and len(host_version_split) >= 2:
|
|
||||||
if not (version_split[0] == host_version_split[0] and version_split[1] == host_version_split[1]):
|
|
||||||
continue
|
|
||||||
else:
|
|
||||||
continue
|
|
||||||
|
|
||||||
if build == host_build:
|
|
||||||
# Skip, as we want the next closest match
|
|
||||||
continue
|
|
||||||
|
|
||||||
# Check if this is the newest build
|
|
||||||
if newest_build["date"] == "":
|
|
||||||
newest_build = {
|
|
||||||
"name": name,
|
|
||||||
"version": version,
|
|
||||||
"build": build,
|
|
||||||
"date": date,
|
|
||||||
}
|
|
||||||
|
|
||||||
else:
|
|
||||||
if date > newest_build["date"]:
|
|
||||||
newest_build = {
|
|
||||||
"name": name,
|
|
||||||
"version": version,
|
|
||||||
"build": build,
|
|
||||||
"date": date,
|
|
||||||
}
|
|
||||||
|
|
||||||
if newest_build["date"] != "":
|
|
||||||
print(f"- Closest match: {newest_build['version']} build {newest_build['build']}")
|
|
||||||
return self.generate_kdk_link(newest_build["version"], newest_build["build"])
|
|
||||||
|
|
||||||
print(" - Could not find a match")
|
print(" - Could not find a match")
|
||||||
return None, ""
|
return None, ""
|
||||||
|
|
||||||
|
|
||||||
def generate_kdk_link(self, version: str, build: str):
|
def generate_kdk_link(self, version: str, build: str):
|
||||||
# Note: cannot do lazy matching as we don't store old version/build numbers nor can we enumerate KDKs from the portal
|
return f"https://download.developer.apple.com/macOS/Kernel_Debug_Kit_{version}_build_{build}/Kernel_Debug_Kit_{version}_build_{build}.dmg"
|
||||||
URL_TEMPLATE = f"https://download.developer.apple.com/macOS/Kernel_Debug_Kit_{version}_build_{build}/Kernel_Debug_Kit_{version}_build_{build}.dmg"
|
|
||||||
|
|
||||||
return URL_TEMPLATE, build
|
|
||||||
|
|
||||||
|
|
||||||
def verify_apple_developer_portal(self, link):
|
def verify_apple_developer_portal(self, link):
|
||||||
# Determine whether Apple Developer Portal is up
|
# Determine whether Apple Developer Portal is up
|
||||||
@@ -105,7 +80,7 @@ class kernel_debug_kit_handler:
|
|||||||
# 1: Portal is up but file is not available
|
# 1: Portal is up but file is not available
|
||||||
# 2: Portal is down
|
# 2: Portal is down
|
||||||
|
|
||||||
TOKEN_URL_BASE = "https://developerservices2.apple.com/services/download?path="
|
TOKEN_URL_BASE = "https://developerservices2.apple.com/services/download"
|
||||||
remote_path = urllib.parse.urlparse(link).path
|
remote_path = urllib.parse.urlparse(link).path
|
||||||
token_url = urllib.parse.urlunparse(urllib.parse.urlparse(TOKEN_URL_BASE)._replace(query=urllib.parse.urlencode({"path": remote_path})))
|
token_url = urllib.parse.urlunparse(urllib.parse.urlparse(TOKEN_URL_BASE)._replace(query=urllib.parse.urlencode({"path": remote_path})))
|
||||||
|
|
||||||
@@ -126,7 +101,6 @@ class kernel_debug_kit_handler:
|
|||||||
return 2
|
return 2
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
|
|
||||||
def download_kdk(self, version: str, build: str):
|
def download_kdk(self, version: str, build: str):
|
||||||
error_msg = ""
|
error_msg = ""
|
||||||
detected_build = build
|
detected_build = build
|
||||||
@@ -135,37 +109,60 @@ class kernel_debug_kit_handler:
|
|||||||
print(" - KDK is already installed")
|
print(" - KDK is already installed")
|
||||||
return True, error_msg, detected_build
|
return True, error_msg, detected_build
|
||||||
|
|
||||||
# Note: cannot do lazy matching as we don't store old version/build numbers nor can we enumerate KDKs from the portal
|
download_link = None
|
||||||
URL_TEMPLATE, detected_build = self.generate_kdk_link(version, build)
|
closest_match_download_link = None
|
||||||
|
closest_build = ""
|
||||||
|
|
||||||
|
kdk_list = self.get_available_kdks()
|
||||||
|
|
||||||
|
parsed_version = cast(packaging.version.Version, packaging.version.parse(version))
|
||||||
|
|
||||||
|
if kdk_list:
|
||||||
|
for kdk in kdk_list:
|
||||||
|
kdk_version = cast(packaging.version.Version, packaging.version.parse(kdk["version"]))
|
||||||
|
if kdk["build"] == build:
|
||||||
|
download_link = kdk["download_url"]
|
||||||
|
elif not closest_match_download_link and kdk_version <= parsed_version and kdk_version.major == parsed_version.major and kdk_version.minor == parsed_version.minor:
|
||||||
|
# The KDK list is already sorted by date then version, so the first match is the closest
|
||||||
|
closest_match_download_link = kdk["download_url"]
|
||||||
|
closest_build = kdk["build"]
|
||||||
|
else:
|
||||||
|
print(" - Could not fetch KDK list, falling back to brute force")
|
||||||
|
download_link = self.generate_kdk_link(version, build)
|
||||||
|
closest_match_download_link, closest_build = self.get_closest_match_legacy(version, build)
|
||||||
|
|
||||||
print(f"- Downloading Apple KDK for macOS {version} build {build}")
|
print(f"- Downloading Apple KDK for macOS {version} build {build}")
|
||||||
result = self.verify_apple_developer_portal(URL_TEMPLATE)
|
# download_link is None if no matching KDK is found, so we'll fall back to the closest match
|
||||||
if result == 2:
|
result = self.verify_apple_developer_portal(download_link) if download_link else 1
|
||||||
error_msg = "Could not contact Apple download servers"
|
if result == 0:
|
||||||
return False, error_msg, ""
|
|
||||||
elif result == 0:
|
|
||||||
print(" - Downloading KDK")
|
print(" - Downloading KDK")
|
||||||
elif result == 1:
|
elif result == 1:
|
||||||
print(" - Could not find KDK, finding closest match")
|
print(" - Could not find KDK, finding closest match")
|
||||||
URL_TEMPLATE, detected_build = self.get_closest_match(version, build)
|
|
||||||
|
|
||||||
if self.is_kdk_installed(detected_build) is True:
|
if self.is_kdk_installed(closest_build) is True:
|
||||||
return True, error_msg, detected_build
|
return True, error_msg, closest_build
|
||||||
if URL_TEMPLATE is None:
|
|
||||||
|
if closest_match_download_link is None:
|
||||||
error_msg = "Could not find KDK for host, nor closest match"
|
error_msg = "Could not find KDK for host, nor closest match"
|
||||||
return False, error_msg, ""
|
return False, error_msg, ""
|
||||||
result = self.verify_apple_developer_portal(URL_TEMPLATE)
|
|
||||||
if result == 2:
|
result = self.verify_apple_developer_portal(closest_match_download_link)
|
||||||
error_msg = "Could not contact Apple download servers"
|
|
||||||
return False, error_msg, ""
|
if result == 0:
|
||||||
elif result == 0:
|
|
||||||
print(" - Downloading KDK")
|
print(" - Downloading KDK")
|
||||||
|
download_link = closest_match_download_link
|
||||||
elif result == 1:
|
elif result == 1:
|
||||||
print(" - Could not find KDK")
|
print(" - Could not find KDK")
|
||||||
error_msg = "Could not find KDK for host on Apple's servers, nor closest match"
|
error_msg = "Could not find KDK for host on Apple's servers, nor closest match"
|
||||||
return False, error_msg, ""
|
return False, error_msg, ""
|
||||||
|
elif result == 2:
|
||||||
|
error_msg = "Could not contact Apple download servers"
|
||||||
|
return False, error_msg, ""
|
||||||
|
elif result == 2:
|
||||||
|
error_msg = "Could not contact Apple download servers"
|
||||||
|
return False, error_msg, ""
|
||||||
|
|
||||||
if utilities.download_apple_developer_portal(URL_TEMPLATE, self.constants.kdk_download_path):
|
if utilities.download_apple_developer_portal(download_link, self.constants.kdk_download_path):
|
||||||
return True, error_msg, detected_build
|
return True, error_msg, detected_build
|
||||||
error_msg = "Failed to download KDK"
|
error_msg = "Failed to download KDK"
|
||||||
return False, error_msg, ""
|
return False, error_msg, ""
|
||||||
@@ -176,4 +173,4 @@ class kernel_debug_kit_handler:
|
|||||||
if file.is_dir():
|
if file.is_dir():
|
||||||
if file.name.endswith(f"{build}.kdk"):
|
if file.name.endswith(f"{build}.kdk"):
|
||||||
return True
|
return True
|
||||||
return False
|
return False
|
||||||
|
|||||||
Reference in New Issue
Block a user