diff options
Diffstat (limited to 'build/macosx/catalog.py')
-rw-r--r-- | build/macosx/catalog.py | 123 |
1 files changed, 123 insertions, 0 deletions
diff --git a/build/macosx/catalog.py b/build/macosx/catalog.py new file mode 100644 index 0000000000..e3d937ffaf --- /dev/null +++ b/build/macosx/catalog.py @@ -0,0 +1,123 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +import argparse +import plistlib +import ssl +import sys +from io import BytesIO +from urllib.request import urlopen +from xml.dom import minidom + +import certifi +from mozpack.macpkg import Pbzx, uncpio, unxar + + +def get_english(dict, default=None): + english = dict.get("English") + if english is None: + english = dict.get("en", default) + return english + + +def get_content_at(url): + ssl_context = ssl.create_default_context(cafile=certifi.where()) + f = urlopen(url, context=ssl_context) + return f.read() + + +def get_plist_at(url): + return plistlib.loads(get_content_at(url)) + + +def show_package_content(url, digest=None, size=None): + package = get_content_at(url) + if size is not None and len(package) != size: + print(f"Package does not match size given in catalog: {url}", file=sys.stderr) + sys.exit(1) + # Ideally we'd check the digest, but it's not md5, sha1 or sha256... + # if digest is not None and hashlib.???(package).hexdigest() != digest: + # print(f"Package does not match digest given in catalog: {url}", file=sys.stderr) + # sys.exit(1) + for name, content in unxar(BytesIO(package)): + if name == "Payload": + for path, _, __ in uncpio(Pbzx(content)): + if path: + print(path.decode("utf-8")) + + +def show_product_info(product, package_id=None): + # An alternative here would be to look at the MetadataURLs in + # product["Packages"], but going with Distributions allows to + # only do one request. + dist = get_english(product.get("Distributions")) + data = get_content_at(dist) + dom = minidom.parseString(data.decode("utf-8")) + for pkg_ref in dom.getElementsByTagName("pkg-ref"): + if pkg_ref.childNodes: + if pkg_ref.hasAttribute("packageIdentifier"): + id = pkg_ref.attributes["packageIdentifier"].value + else: + id = pkg_ref.attributes["id"].value + + if package_id and package_id != id: + continue + + for child in pkg_ref.childNodes: + if child.nodeType != minidom.Node.TEXT_NODE: + continue + for p in product["Packages"]: + if p["URL"].endswith("/" + child.data): + if package_id: + show_package_content( + p["URL"], p.get("Digest"), p.get("Size") + ) + else: + print(id, p["URL"]) + + +def show_products(products, filter=None): + for key, product in products.items(): + metadata_url = product.get("ServerMetadataURL", "") + if metadata_url and (not filter or filter in metadata_url): + metadata = get_plist_at(metadata_url) + localization = get_english(metadata.get("localization", {}), {}) + title = localization.get("title", None) + version = metadata.get("CFBundleShortVersionString", None) + print(key, title, version) + + +def main(): + parser = argparse.ArgumentParser() + parser.add_argument( + "--catalog", + help="URL of the catalog", + default="https://swscan.apple.com/content/catalogs/others/index-13-12-10.16-10.15-10.14-10.13-10.12-10.11-10.10-10.9-mountainlion-lion-snowleopard-leopard.merged-1.sucatalog", + ) + parser.add_argument( + "--filter", help="Only show entries with metadata url matching the filter" + ) + parser.add_argument( + "what", nargs="?", help="Show packages information about the given entry" + ) + args = parser.parse_args() + + data = get_plist_at(args.catalog) + products = data["Products"] + + if args.what: + if args.filter: + print( + "Cannot use --filter when showing verbose information about an entry", + file=sys.stderr, + ) + sys.exit(1) + product_id, _, package_id = args.what.partition("/") + show_product_info(products[product_id], package_id) + else: + show_products(products, args.filter) + + +if __name__ == "__main__": + main() |