Skip to content

Instantly share code, notes, and snippets.

@nicolas17
Created September 17, 2022 21:13
Show Gist options
  • Save nicolas17/87b4ec6edfd67daa3dc22d706de19806 to your computer and use it in GitHub Desktop.
Save nicolas17/87b4ec6edfd67daa3dc22d706de19806 to your computer and use it in GitHub Desktop.
#!/usr/bin/python3
# SPDX-FileCopyrightText: 2022 Nicolás Alvarez <nicolas.alvarez@gmail.com>
#
# SPDX-License-Identifier: MIT
import os
import sys
import json
import re
import plistlib
import requests
sess = requests.session()
url = sys.argv[1]
manifest_url = url[:url.rindex('/')] + '/BuildManifest.plist'
print("Loading manifest")
resp = sess.get(manifest_url)
assert resp.status_code == 200
manifest = plistlib.loads(resp.content)
product_types = manifest['SupportedProductTypes']
build = manifest['ProductBuildVersion']
if "iProd99,1" in product_types: product_types.remove("iProd99,1")
if "ADP3,1" in product_types: product_types.remove("ADP3,1")
OS_MAP = [
('iPod', 'iOS'),
('iPhone', 'iOS'),
('iPad', 'iPadOS'),
('AudioAccessory', 'audioOS'),
('AppleTV', 'tvOS'),
('MacBook', 'macOS'),
('Watch', 'watchOS'),
('iBridge', 'bridgeOS'),
]
for product_prefix, os_name in OS_MAP:
if any(prod.startswith(product_prefix) for prod in product_types):
break
else:
raise RuntimeError(f"Couldn't match product types to any known OS: {product_types}")
print(f"Matched product types to: {os_name}")
# JSON files are split into subdirectories per major version;
# figure out which subdir we have to look into
build_major = re.match('^\d+', build).group(0)
for subdir in os.listdir(f"iosFiles/{os_name}"):
if subdir.startswith(build_major+"x"):
version_subdir=subdir
break
else:
raise RuntimeError(f"Couldn't find a subdirectory in {os_name} for build {build} (major {build_major})")
# next, does the JSON file already exist?
json_filename = f"iosFiles/{os_name}/{version_subdir}/{build}.json"
print(f"JSON path is {json_filename}")
if os.path.isfile(json_filename):
# yep; load it
print(f"Reading from {json_filename}")
json_data = json.load(open(json_filename, "r"))
else:
# it doesn't, gotta start from scratch
print("JSON doesn't exist so we're starting from scratch")
json_data = {
"osStr": os_name,
"version": manifest["ProductVersion"] + " (FIXME)",
"build": build,
"released": "yyyy-mm-dd",
"beta": (re.match('.*[a-z]$', build) is not None),
"deviceMap": []
}
for product in sorted(product_types):
if product not in json_data['deviceMap']:
json_data['deviceMap'].append(product)
if 'sources' not in json_data:
json_data['sources'] = []
for source in json_data['sources']:
if set(source['deviceMap']) == set(product_types):
break
else:
print(f"no sources for {sorted(product_types)}, adding")
source = {}
source['deviceMap'] = sorted(product_types)
json_data['sources'].append(source)
source['type'] = 'ipsw'
HTTPS_PREFIX = 'https://updates.cdn-apple.com/'
HTTP_PREFIX = 'http://updates-http.cdn-apple.com/'
if url.startswith(HTTPS_PREFIX):
ipsw_https = url
ipsw_http = url.replace(HTTPS_PREFIX, HTTP_PREFIX)
elif url.startswith(HTTP_PREFIX):
ipsw_http = url
ipsw_https = url.replace(HTTP_PREFIX, HTTPS_PREFIX)
else:
raise RuntimeError(f"Unknown URL prefix on {repr(url)}")
source['links'] = [
{ "url": ipsw_https, "preferred": True, "active": True },
{ "url": ipsw_http, "preferred": False, "active": True }
]
if True:
print("Requesting ipsw URL")
resp = sess.head(ipsw_https)
if resp.status_code == 200:
source['size'] = int(resp.headers['content-length'])
if 'hashes' not in source:
source['hashes'] = {}
if 'x-amz-meta-digest-sh1' in resp.headers:
source['hashes']['sha1'] = resp.headers['x-amz-meta-digest-sh1']
if 'x-amz-meta-digest-sha256' in resp.headers:
source['hashes']['sha2-256'] = resp.headers['x-amz-meta-digest-sha256']
if source['hashes'] == {}:
# we didn't actually get any hashes, delete the empty dict
del source['hashes']
else:
source['links'][0]['active'] = False
source['links'][1]['active'] = False
print(f"Writing to {json_filename}")
with open(json_filename, "w") as f:
json.dump(json_data, f, indent=4)
sess.close()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment