tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

catalog.py (4321B)


      1 # This Source Code Form is subject to the terms of the Mozilla Public
      2 # License, v. 2.0. If a copy of the MPL was not distributed with this
      3 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
      4 
      5 import argparse
      6 import plistlib
      7 import ssl
      8 import sys
      9 from io import BytesIO
     10 from urllib.request import urlopen
     11 from xml.dom import minidom
     12 
     13 import certifi
     14 from mozpack.macpkg import Pbzx, uncpio, unxar
     15 
     16 
     17 def get_english(dict, default=None):
     18    english = dict.get("English")
     19    if english is None:
     20        english = dict.get("en", default)
     21    return english
     22 
     23 
     24 def get_content_at(url):
     25    ssl_context = ssl.create_default_context(cafile=certifi.where())
     26    f = urlopen(url, context=ssl_context)
     27    return f.read()
     28 
     29 
     30 def get_plist_at(url):
     31    return plistlib.loads(get_content_at(url))
     32 
     33 
     34 def show_package_content(url, digest=None, size=None):
     35    package = get_content_at(url)
     36    if size is not None and len(package) != size:
     37        print(f"Package does not match size given in catalog: {url}", file=sys.stderr)
     38        sys.exit(1)
     39    # Ideally we'd check the digest, but it's not md5, sha1 or sha256...
     40    # if digest is not None and hashlib.???(package).hexdigest() != digest:
     41    #     print(f"Package does not match digest given in catalog: {url}", file=sys.stderr)
     42    #     sys.exit(1)
     43    for name, content in unxar(BytesIO(package)):
     44        if name == "Payload":
     45            for path, _, __ in uncpio(Pbzx(content)):
     46                if path:
     47                    print(path.decode("utf-8"))
     48 
     49 
     50 def show_product_info(product, package_id=None):
     51    # An alternative here would be to look at the MetadataURLs in
     52    # product["Packages"], but going with Distributions allows to
     53    # only do one request.
     54    dist = get_english(product.get("Distributions"))
     55    data = get_content_at(dist)
     56    dom = minidom.parseString(data.decode("utf-8"))
     57    for pkg_ref in dom.getElementsByTagName("pkg-ref"):
     58        if pkg_ref.childNodes:
     59            if pkg_ref.hasAttribute("packageIdentifier"):
     60                id = pkg_ref.attributes["packageIdentifier"].value
     61            else:
     62                id = pkg_ref.attributes["id"].value
     63 
     64            if package_id and package_id != id:
     65                continue
     66 
     67            for child in pkg_ref.childNodes:
     68                if child.nodeType != minidom.Node.TEXT_NODE:
     69                    continue
     70                for p in product["Packages"]:
     71                    if p["URL"].endswith("/" + child.data):
     72                        if package_id:
     73                            show_package_content(
     74                                p["URL"], p.get("Digest"), p.get("Size")
     75                            )
     76                        else:
     77                            print(id, p["URL"])
     78 
     79 
     80 def show_products(products, filter=None):
     81    for key, product in products.items():
     82        metadata_url = product.get("ServerMetadataURL", "")
     83        if metadata_url and (not filter or filter in metadata_url):
     84            metadata = get_plist_at(metadata_url)
     85            localization = get_english(metadata.get("localization", {}), {})
     86            title = localization.get("title", None)
     87            version = metadata.get("CFBundleShortVersionString", None)
     88            print(key, title, version)
     89 
     90 
     91 def main():
     92    parser = argparse.ArgumentParser()
     93    parser.add_argument(
     94        "--catalog",
     95        help="URL of the catalog",
     96        default="https://swscan.apple.com/content/catalogs/others/index-15-14-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",
     97    )
     98    parser.add_argument(
     99        "--filter", help="Only show entries with metadata url matching the filter"
    100    )
    101    parser.add_argument(
    102        "what", nargs="?", help="Show packages information about the given entry"
    103    )
    104    args = parser.parse_args()
    105 
    106    data = get_plist_at(args.catalog)
    107    products = data["Products"]
    108 
    109    if args.what:
    110        if args.filter:
    111            print(
    112                "Cannot use --filter when showing verbose information about an entry",
    113                file=sys.stderr,
    114            )
    115            sys.exit(1)
    116        product_id, _, package_id = args.what.partition("/")
    117        show_product_info(products[product_id], package_id)
    118    else:
    119        show_products(products, args.filter)
    120 
    121 
    122 if __name__ == "__main__":
    123    main()