Source code for bbarchivist.scriptutilstcl

#!/usr/bin/env python3
"""This module contains various utilities for TCL tools."""

import collections  # defaultdict
import os  # path work

import requests  # session
from bbarchivist import argutils  # arguments
from bbarchivist import hashutils  # file hashes
from bbarchivist import networkutils  # network tools
from bbarchivist import networkutilstcl  # tcl network tools
from bbarchivist import utilities  # little things
from bbarchivist import xmlutilstcl  # xml handling

__author__ = "Thurask"
__license__ = "WTFPL v2"
__copyright__ = "2018 Thurask"


[docs]def tclloader_prep(loaderfile, directory=False): """ Prepare directory name and OS version. :param loaderfile: Path to input file/folder. :type loaderfile: str :param directory: If the input file is a folder. Default is False. :type directory: bool """ loaderdir = loaderfile if directory else loaderfile.replace(".zip", "") osver = loaderdir.split("-")[-1] return loaderdir, osver
[docs]def tclloader_filename(loaderdir, osver, loadername=None): """ Prepare platform and filename. :param loaderdir: Path to input folder. :type loaderdir: str :param osver: OS version. :type osver: str :param loadername: Name of final autoloader. Default is auto-generated. :type loadername: str """ platform = os.listdir(os.path.join(loaderdir, "target", "product"))[0] if loadername is None: loadername = "{0}_autoloader_user-all-{1}".format(platform, osver) return loadername, platform
[docs]def tcl_download(downloadurl, filename, filesize, filehash, verify=True): """ Download autoloader file, rename, and verify. :param downloadurl: Download URL. :type downloadurl: str :param filename: Name of autoloader file. :type filename: str :param filesize: Size of autoloader file. :type filesize: str :param filehash: SHA-1 hash of autoloader file. :type filehash: str :param verify: Whether to verify the file after downloading. Default is True. :type verify: bool """ print("FILENAME: {0}".format(filename)) print("LENGTH: {0}".format(utilities.fsizer(filesize))) networkutils.download(downloadurl) print("DOWNLOAD COMPLETE") os.rename(downloadurl.split("/")[-1], filename) if verify: method = hashutils.get_engine("sha1") shahash = hashutils.hashlib_hash(filename, method) if shahash == filehash: print("HASH CHECK OK") else: print(shahash) print("HASH FAILED!")
[docs]def tcl_prd_scan(curef, download=False, mode=4, fvver="AAA000", original=True, export=False, verify=True): """ Scan one PRD and produce download URL and filename. :param curef: PRD of the phone variant to check. :type curef: str :param download: If we'll download the file that this returns. Default is False. :type download: bool :param mode: 4 if downloading autoloaders, 2 if downloading OTA deltas. :type mode: int :param fvver: Initial software version, must be specific if downloading OTA deltas. :type fvver: str :param original: If we'll download the file with its original filename. Default is True. :type original: bool :param export: Whether to export XML response to file. Default is False. :type export: bool :param verify: Whether to verify the file after downloading. Default is True. :type verify: bool """ sess = requests.Session() ctext = networkutilstcl.tcl_check(curef, sess, mode, fvver, export) if ctext is None: raise SystemExit tvver, firmwareid, filename, filesize, filehash = xmlutilstcl.parse_tcl_check(ctext) salt = networkutilstcl.tcl_salt() vkhsh = networkutilstcl.vkhash(curef, tvver, firmwareid, salt, mode, fvver) updatetext = networkutilstcl.tcl_download_request(curef, tvver, firmwareid, salt, vkhsh, sess, mode, fvver, export) downloadurl, encslave = xmlutilstcl.parse_tcl_download_request(updatetext) statcode = networkutils.getcode(downloadurl, sess) filename = tcl_delta_filename(curef, fvver, tvver, filename, original) tcl_prd_print(tvver, downloadurl, filename, statcode, encslave, sess) if statcode == 200 and download: tcl_download(downloadurl, filename, filesize, filehash, verify)
[docs]def tcl_delta_filename(curef, fvver, tvver, filename, original=True): """ Generate compatible filenames for deltas, if needed. :param curef: PRD of the phone variant to check. :type curef: str :param fvver: Initial software version. :type fvver: str :param tvver: Target software version. :type tvver: str :param filename: File name from download URL, passed through if not changing filename. :type filename: str :param original: If we'll download the file with its original filename. Default is True. :type original: bool """ if not original: prdver = curef.split("-")[1] filename = "JSU_PRD-{0}-{1}to{2}.zip".format(prdver, fvver, tvver) return filename
[docs]def tcl_prd_print(tvver, downloadurl, filename, statcode, encslave, session): """ Print output from PRD scanning. :param tvver: Target software version. :type tvver: str :param downloadurl: File to download. :type downloadurl: str :param filename: File name from download URL. :type filename: str :param statcode: Status code of download URL. :type statcode: int :param encslave: Server hosting header script. :type encslave: str :param session: Session object. :type session: requests.Session """ print("OS: {0}".format(tvver)) print("{0}: HTTP {1}".format(filename, statcode)) print(downloadurl) if encslave is not None: address = "/{0}".format(downloadurl.split("/", 3)[3:][0]) print("CHECKING HEADER...") sentinel = networkutilstcl.encrypt_header(address, encslave, session) if sentinel is not None: print(sentinel)
[docs]def tcl_prep_otaver(ota=None): """ Prepare variables for OTA versus full check. :param ota: The starting version if OTA, None if not. Default is None. :type ota: str """ if ota is not None: mode = 2 fvver = ota else: mode = 4 fvver = "AAA000" return mode, fvver
[docs]def tcl_delta_remote(curef): """ Prepare remote version for delta scanning. :param curef: PRD of the phone variant to check. :type curef: str """ remotedict = networkutilstcl.remote_prd_info() fvver = remotedict.get(curef, "AAA000") if fvver == "AAA000": print("NO REMOTE VERSION FOUND!") raise SystemExit return fvver
[docs]def tcl_mainscan_preamble(ota=None): """ Prepare preamble for TCL scanning. :param ota: The starting version if OTA, None if not. Default is None. :type ota: str """ argutils.slim_preamble("TCLSCAN") if ota is not None: print("PRDs with OTA from OS {0}".format(ota.upper()))
[docs]def tcl_mainscan_printer(curef, tvver, ota=None): """ Print output of TCL scanning. :param curef: PRD of the phone variant to check. :type curef: str :param tvver: Target software version. :type tvver: str :param ota: The starting version if OTA, None if not. Default is None. :type ota: str """ if ota is not None: print("{0}: {2} to {1}".format(curef, tvver, ota.upper())) else: print("{0}: {1}".format(curef, tvver))
[docs]def tcl_findprd_prepd_start(prddict): """ Collect list of PRD entries. :param prddict: Device:PRD dictionary. :type prddict: dict(str: list) """ prda = [] for item in prddict.values(): prda.extend(item) return prda
[docs]def tcl_findprd_prepd_middle(prda): """ Convert PRD entries to list of center:end entries. :param prda: List of PRD-xxxxx-yyy entries. :type prda: list(str) """ prds = [x.split(" ")[0].replace("PRD-", "").replace("APBI-PRD", "").replace("-", "") for x in prda] prdx = list({x[0:5]: x[5:]} for x in prds) return prdx
[docs]def tcl_findprd_prepd_end(prdx): """ Convert list of center:end entries to final center:[ends] dict. :param prdx: List of center:end dict entries. :type prdx: list(dict(str: str)) """ prdf = collections.defaultdict(list) for prdc in prdx: for key, value in prdc.items(): prdf[key].append(value) return prdf
[docs]def tcl_findprd_prepdict(prddict): """ Prepare dict of center:[ends] entries. :param prddict: Device:PRD dictionary. :type prddict: dict(str: list) """ prda = tcl_findprd_prepd_start(prddict) prdx = tcl_findprd_prepd_middle(prda) prdfinal = tcl_findprd_prepd_end(prdx) return prdfinal
[docs]def tcl_findprd_checkfilter(prddict, tocheck=None): """ Filter PRD dict if needed. :param prddict: PRD center:[ends] dictionary. :type prddict: collections.defaultdict(str: list) :param tocheck: Specific PRD(s) to check, None if all will be checked. Default is None. :type tocheck: list(str) """ prddict2 = prddict if tocheck is not None: prddict2 = collections.defaultdict(list) for toch in tocheck: toch = toch.replace("PRD-", "").replace("APBI-PRD", "") prddict2[toch] = prddict[toch] return prddict2
[docs]def tcl_findprd_centerscan(center, prddict, session, floor=0, ceiling=999, export=False, noprefix=False, key2mode=False): """ Individual scanning for the center of a PRD. :param center: PRD-center-end. :type center: str :param prddict: PRD center:[ends] dictionary. :type prddict: collections.defaultdict(str: list) :param session: Session object. :type session: requests.Session :param floor: When to start. Default is 0. :type floor: int :param ceiling: When to stop. Default is 999. :type ceiling: int :param export: Whether to export XML response to file. Default is False. :type export: bool :param noprefix: Whether to skip adding "PRD-" prefix. Default is False. :type noprefix: bool :param key2mode: Whether to use new-style prefix. Default is False. :type key2mode: bool """ tails = [int(i) for i in prddict[center]] safes = [g for g in range(floor, ceiling) if g not in tails] print("SCANNING ROOT: {0}{1}".format(center, " "*12)) tcl_findprd_safescan(safes, center, session, export, noprefix, key2mode)
[docs]def tcl_findprd_prepcuref(center, tail, noprefix=False, key2mode=False): """ Prepare candidate PRD. :param center: PRD-center-tail. :type center: str :param tail: PRD-center-tail. :type tail: int :param noprefix: Whether to skip adding "PRD-" prefix. Default is False. :type noprefix: bool :param key2mode: Whether to use new-style prefix. Default is False. :type key2mode: bool """ if key2mode: curef = "APBI-PRD{0}{1:03}".format(center, tail) else: prefix = "" if noprefix else "PRD-" curef = "{2}{0}-{1:03}".format(center, tail, prefix) return curef
[docs]def tcl_findprd_safescan(safes, center, session, export=False, noprefix=False, key2mode=False): """ Scan for PRDs known not to be in database. :param safes: List of ends within given range that aren't in database. :type safes: list(int) :param center: PRD-center-end. :type center: str :param session: Session object. :type session: requests.Session :param export: Whether to export XML response to file. Default is False. :type export: bool :param noprefix: Whether to skip adding "PRD-" prefix. Default is False. :type noprefix: bool :param key2mode: Whether to use new-style prefix. Default is False. :type key2mode: bool """ for j in safes: curef = tcl_findprd_prepcuref(center, j, noprefix, key2mode) print("NOW SCANNING: {0}".format(curef), end="\r") checktext = networkutilstcl.tcl_check(curef, session, export) if checktext is None: continue else: tcl_findprd_safehandle(curef, checktext)
[docs]def tcl_findprd_safehandle(curef, checktext): """ Parse API output and print the relevant bits. :param curef: PRD of the phone variant to check. :type curef: str :param checktext: The XML formatted data returned from the first stage API check. :type checktext: str """ tvver, firmwareid, filename, fsize, fhash = xmlutilstcl.parse_tcl_check(checktext) del firmwareid, filename, fsize, fhash tvver2 = "{0}{1}".format(tvver, " "*12) tcl_mainscan_printer(curef, tvver2)
[docs]def tcl_findprd(prddict, floor=0, ceiling=999, export=False, noprefix=False, key2mode=False): """ Check for new PRDs based on PRD database. :param prddict: PRD center:[ends] dictionary. :type prddict: collections.defaultdict(str: list) :param floor: When to start. Default is 0. :type floor: int :param ceiling: When to stop. Default is 999. :type ceiling: int :param export: Whether to export XML response to file. Default is False. :type export: bool :param noprefix: Whether to skip adding "PRD-" prefix. Default is False. :type noprefix: bool :param key2mode: Whether to use new-style prefix. Default is False. :type key2mode: bool """ sess = requests.Session() for center in sorted(prddict.keys()): tcl_findprd_centerscan(center, prddict, sess, floor, ceiling, export, noprefix, key2mode)