def _read_conditions(self): """Read any existing conditions in the conditional items property list file.""" result = dict() if os.path.exists(self._conditions_file): result = plist.readPlist(path=self._conditions_file) return result
def _read_remote_plist(self): """Gets the property list.""" result = None _basename = os.path.basename(self._plist_url_path) _tmp_file = os.path.join(self._tmp_dir, _basename) _bad_wolf_fixes = bad_wolf.BAD_WOLF_PKGS.get(_basename, None) _bwd = None _req = curl_requests.CURL(url=self._plist_url_path) # NOTE 2019-11-04: Seems that using the 'resume' capability in cURL does not # work here now for some reason, so don't resume. if _req.status in config.HTTP_OK_STATUS: _req.get(url=self._plist_url_path, output=_tmp_file, resume=False) else: _req.get(url=self._plist_failover_url_path, output=_tmp_file, resume=False) _root = plist.readPlist(_tmp_file) if _root: result = set() # Apply 'Bad Wolf' pathches for _pkg in _root['Packages']: _new_pkg = _root['Packages'][_pkg].copy() # Work on copy # Create a new key called 'PackageName' that # contains the value '_pkg' for use with content packs. _new_pkg['PackageName'] = _pkg if _bad_wolf_fixes: _bwd = _bad_wolf_fixes.get( _pkg, None) # A dictionary from '_bad_wolf_fixes' # Merge new/existing keys from matching '_bwd' if _bwd: _new_pkg.update(_bwd) _pkg_obj = package.LoopPackage(**_new_pkg) # pylint: disable=no-member # Only add/process packages that are _not_ 'BadWolfIgnore = True' if not _pkg_obj.BadWolfIgnore: result.add(_pkg_obj) # pylint: enable=no-member # Now process option packs _opt_packs = option_packs.OptionPack(source=_root, release=_basename) self.option_packs = _opt_packs.option_packs misc.clean_up(file_path=_tmp_file) return result
def _get_app_info(self): """Returns the contents of the 'Contents/Info.plist' of the app.""" result = None _info_file_path = os.path.join(self._file_path, config.CONTENTS_PATH, 'Info.plist') if os.path.exists(_info_file_path): result = plist.readPlist(_info_file_path) return result
def get_system_info(self): self.installed_version = "Not Installed!" self.os_build_number = self.r.run( {"args": ["sw_vers", "-buildVersion"]})[0].strip() self.os_number = self.r.run({"args": ["sw_vers", "-productVersion"]})[0].strip() if self.wd_loc: info_plist = plist.readPlist(self.wd_loc + "/Contents/Info.plist") self.installed_version = info_plist["CFBundleGetInfoString"].split( " ")[-1].replace("(", "").replace(")", "")
def patch_menu(self): self.u.head("Web Driver Patch") print(" ") os.chdir(os.path.dirname(os.path.realpath(__file__))) if not self.wd_loc: print( "NVDAStartupWeb.kext was not found in either /L/E or /S/L/E!\n" ) print("Please make sure you have the Web Drivers installed.") self.u.grab("", timeout=5) return info_plist = plist.readPlist(self.wd_loc + "/Contents/Info.plist") current_build = info_plist.get("IOKitPersonalities", {}).get("NVDAStartup", {}).get("NVDARequiredOS", None) print("OS Build Number: {}".format(self.os_build_number)) print("WD Target Build: {}".format(current_build)) print(" ") print("C. Set to Current Build Number") print("I. Input New Build Number") can_restore = False if os.path.exists(self.wd_loc + "/Contents/Info.plist.bak"): print("R. Restore Backup") print("D. Delete Backup") can_restore = True print(" ") print("M. Main Menu") print("Q. Quit") print(" ") menu = self.grab("Please make a selection: ") if not len(menu): self.patch_menu() return if menu[:1].lower() == "q": self.custom_quit() elif menu[:1].lower() == "c": self.set_build(self.os_build_number) elif menu[:1].lower() == "i": self.custom_build() elif menu[:1].lower() == "r" and can_restore: self.restore_backup() elif menu[:1].lower() == "d" and can_restore: self.delete_backup() elif menu[:1].lower() == "m": return self.patch_menu() return
def _get_packages(self): """Returns a set of all packages (as object instances). Also patches any 'issues' that resolve known issues with Apple's audiocontentdownload mirrored files.""" result = None if self.plist_file_path: _basename = os.path.basename(self.plist_file_path) _bad_wolf_fixes = bad_wolf.BAD_WOLF_PKGS.get(_basename, None) _bwd = None _root = plist.readPlist(self.plist_file_path) if _root: result = set() # Apply 'Bad Wolf' pathches for _pkg in _root['Packages']: _new_pkg = _root['Packages'][_pkg].copy() # Work on copy # Create a new key called 'PackageName' that # contains the value '_pkg' for use with content packs. _new_pkg['PackageName'] = _pkg if _bad_wolf_fixes: _bwd = _bad_wolf_fixes.get( _pkg, None) # A dictionary from '_bad_wolf_fixes' # Merge new/existing keys from matching '_bwd' if _bwd: _new_pkg.update(_bwd) _pkg_obj = package.LoopPackage(**_new_pkg) # pylint: disable=no-member # Only add/process packages that are _not_ 'BadWolfIgnore = True' if not _pkg_obj.BadWolfIgnore: result.add(_pkg_obj) # pylint: enable=no-member # Now process option packs self.option_packs = option_packs.OptionPack( source=_root, release=_basename).option_packs return result
def custom_build(self): self.u.head("Custom Build") print(" ") print("") if not self.wd_loc: print( "NVDAStartupWeb.kext was not found in either /L/E or /S/L/E!\n" ) print("Please make sure you have the Web Drivers installed.") self.u.grab("", timeout=5) return info_plist = plist.readPlist(self.wd_loc + "/Contents/Info.plist") current_build = info_plist.get("IOKitPersonalities", {}).get("NVDAStartup", {}).get("NVDARequiredOS", None) print("OS Build Number: {}".format(self.os_build_number)) print("WD Target Build: {}".format(current_build)) print(" ") print("P. Patch Menu") print(" ") print("M. Main Menu") print("Q. Quit") print(" ") menu = self.grab("Please enter a new build number: ") if not len(menu): self.custom_build() return if menu.lower() == "q": self.custom_quit() elif menu.lower() == "m": self.main() elif menu.lower() == "p": return # We have a build number self.set_build(menu) self.main() return
def config_menu(self): self.u.head("Config.plist Patch Menu") print(" ") os.chdir(os.path.dirname(os.path.realpath(__file__))) if not self.wd_loc: print( "NVDAStartupWeb.kext was not found in either /L/E or /S/L/E!\n" ) print("Please make sure you have the Web Drivers installed.") self.u.grab("", timeout=5) return info_plist = plist.readPlist(self.wd_loc + "/Contents/Info.plist") current_build = info_plist.get("IOKitPersonalities", {}).get("NVDAStartup", {}).get("NVDARequiredOS", None) print("OS Build Number: {}".format(self.os_build_number)) print("WD Target Build: {}".format(current_build)) print(" ") print("C. Current Build Number") print(" ") print("M. Main Menu") print("Q. Quit") print(" ") menu = self.grab( "Please make a selection (or type a custom build number): ") if not len(menu): self.config_menu() return if menu[:1].lower() == "m": return elif menu[:1].lower() == "q": self.custom_quit() elif menu[:1].lower() == "c": self.type_menu(self.os_build_number) else: self.type_menu(menu) self.config_menu() return
def differences(file_a, file_b, detailed_info=False): """Compares the package details in 'file_a' against 'file_b' to determine what files exist in 'file_b' but not in 'file_a'. This will also display packages _removed_. This function sorts the files into smallest to largest order. So if 'garageband1021.plist' is compared to 'garageband1011.plist', the output will be based on packages that are in 'garageband1021.plist' but not in 'garageband1011.plist'. In otherwords, '1021' is the _right_ file, '1011' is the _left_ file.""" sorted_files = sorted([f for f in [file_a, file_b]]) file_a = sorted_files[0] base_a = os.path.basename(file_a) file_b = sorted_files[1] base_b = os.path.basename(file_b) _supported = [_v for _v in config.SUPPORTED_PLISTS.values()] _supported.sort() if not all(_file.endswith('.plist') for _file in [file_a, file_b]): print('Files must both be property list files.') sys.exit(1) if not all(_file in _supported for _file in [base_a, base_b]): print('Files must be from {}'.format(_supported)) sys.exit(1) # Sort the two files so if the order of 'file_a' 'file_b' is # 'garageband1021.plist' 'garageband1011.plist' it becomes # 'garageband1011.plist' 'garageband1021.plist' _tmp_dir = os.path.join(tempfile.gettempdir(), config.BUNDLE_ID) if not os.path.exists(file_a): _fa_fallback = os.path.join(config.AUDIOCONTENT_FAILOVER_URL, 'lp10_ms3_content_2016', base_a) _fa_url = misc.plist_url_path(base_a) file_a = os.path.join(_tmp_dir, base_a) _req = curl_requests.CURL(url=_fa_url) if _req.status in config.HTTP_OK_STATUS: _req.get(url=_fa_url, output=file_a) else: _req.get(url=_fa_fallback, output=file_a) file_a_plist = plist.readPlist(plist_path=file_a)['Packages'] misc.clean_up(file_a) elif os.path.exists(file_a): file_a_plist = plist.readPlist(plist_path=file_a)['Packages'] if not os.path.exists(file_b): _fb_fallback = os.path.join(config.AUDIOCONTENT_FAILOVER_URL, 'lp10_ms3_content_2016', base_b) _fb_url = misc.plist_url_path(base_b) file_b = os.path.join(_tmp_dir, base_b) _req = curl_requests.CURL(url=_fb_url) if _req.status in config.HTTP_OK_STATUS: _req.get(url=_fb_url, output=file_b) else: _req.get(url=_fb_fallback, output=file_b) file_b_plist = plist.readPlist(plist_path=file_b)['Packages'] misc.clean_up(file_b) elif os.path.exists(file_b): file_a_plist = plist.readPlist(plist_path=file_a)['Packages'] # Build a set of package names. file_a_packages = set([ os.path.basename(file_a_plist[_pkg]['DownloadName']) for _pkg in file_a_plist ]) file_b_packages = set([ os.path.basename(file_b_plist[_pkg]['DownloadName']) for _pkg in file_b_plist ]) # Get the new/removed files by using 'set_b.difference(set_a)' new_files = file_b_packages.difference(file_a_packages) rem_files = file_a_packages.difference(file_b_packages) cmn_files = file_b_packages.intersection(file_a_packages) if not detailed_info: if new_files: print('{} new packages in {} when compared to {}'.format( len(new_files), base_b, base_a)) if rem_files: print('{} packages removed from {} compared to {}'.format( len(rem_files), base_a, base_b)) if cmn_files: print('{} packages common between {} and {}'.format( len(cmn_files), base_a, base_b)) # Exit success because nothing else to do. sys.exit(0)
def type_menu(self, build): self.u.head("Config.plist Patch: {}".format(build)) print(" ") os.chdir(os.path.dirname(os.path.realpath(__file__))) if not self.wd_loc: print( "NVDAStartupWeb.kext was not found in either /L/E or /S/L/E!\n" ) print("Please make sure you have the Web Drivers installed.") self.u.grab("", timeout=5) return info_plist = plist.readPlist(self.wd_loc + "/Contents/Info.plist") current_build = info_plist.get("IOKitPersonalities", {}).get("NVDAStartup", {}).get("NVDARequiredOS", None) if build == current_build: print( "Both builds are the same - this defeats the purpose of the patch." ) self.u.grab("", timeout=5) return print("B. Show Base64 Values") print("H. Show Hex Values") print("P. Show Plist Patch") print(" ") print("M. Main Menu") print("C. Config.plist Patch Menu") print("Q. Quit") print(" ") menu = self.grab("Please make a selection: ") if not len(menu): self.config_menu() return display_text = data_type = "" if menu[:1].lower() == "m": self.main() return elif menu[:1].lower() == "q": self.custom_quit() elif menu[:1].lower() == "b": data_type = "Base64" display_text += "Name: NVDAStartupWeb\n" display_text += "Disabled: False\n" display_text += "Find: {}\n".format( self.get_base(current_build)) display_text += "Replace: {}\n".format(self.get_base(build)) display_text += "InfoPlist: True" elif menu[:1].lower() == "h": data_type = "Hex" display_text += "Name: NVDAStartupWeb\n" display_text += "Disabled: False\n" display_text += "Find: {}\n".format( self.get_hex(current_build)) display_text += "Replace: {}\n".format(self.get_hex(build)) display_text += "InfoPlist: True" elif menu[:1].lower() == "p": # Get some plist wizardry data_type = "Plist" plist_dict = { "Name": "NVDAStartupWeb", "Comment": "Nvidia {} to {}".format(current_build, build), "InfoPlistPatch": True, "Disabled": False, "Find": self.get_base(current_build), "Replace": self.get_base(build) } plist_string = plist.dumps(plist_dict).decode("utf-8") # Trim the plist display_text = "\n".join(plist_string.split("\n")[3:-2]) if len(display_text): self.u.head("Config.plist Patch: {}".format(build)) print(" ") print("Your {} Data:\n".format(data_type)) print(display_text) print(" ") self.grab("Press [enter] to return...") self.type_menu(build) return
def patch_pkg(self, package, temp, build=None): self.u.head("Patching Install Package") print(" ") script_path = os.path.dirname(os.path.realpath(__file__)) print("Expanding package...\n") stat = self.r.run( {"args": ["pkgutil", "--expand", package, temp + "/package"]}) if not stat[2] == 0: print("Something went wrong!\n") print(stat[1]) return new_dist = "" print("Patching Distribution...\n") with open(temp + "/package/Distribution") as f: for line in f: if "if (!validatesoftware())" in line.lower(): continue if "if (!validatehardware())" in line.lower(): continue if "return false;" in line: line = line.replace("return false;", "return true;") new_dist += line with open(temp + "/package/Distribution", "w") as f: f.write(new_dist) if build: # We have a build - let's do stuff print("Patching Kext for {}...".format(build)) os.chdir(temp + "/package") os.chdir( self.r.run({ "args": "ls | grep -i nvwebdrivers", "shell": True })[0].strip()) self.r.run({"args": ["mkdir", "temp"]}) os.chdir("temp") print(" Extracting Payload...") self.r.run({"args": ["tar", "xvf", "../Payload"]}) info_path = None if os.path.exists("./NVDAStartupWeb.kext/Contents/Info.plist"): info_path = "./NVDAStartupWeb.kext/Contents/Info.plist" elif os.path.exists( "./Library/Extensions/NVDAStartupWeb.kext/Contents/Info.plist" ): info_path = "./Library/Extensions/NVDAStartupWeb.kext/Contents/Info.plist" elif os.path.exists("./NVDAStartup.kext/Contents/Info.plist"): # Old stuff... info_path = "./NVDAStartup.kext/Contents/Info.plist" if not info_path: print("Couldn't find Info.plist to patch!") return print(" Patching Info.plist for {}...".format(build)) # Got the info.plist - load it and patch it info_plist = plist.readPlist(os.path.realpath(info_path)) info_plist["IOKitPersonalities"]["NVDAStartup"][ "NVDARequiredOS"] = build # Write the changes plist.writePlist(info_plist, os.path.realpath(info_path)) # Remove the old Payload and BOM print(" Removing old Payload and BOM...") self.r.run({"args": ["rm", "../Payload"]}) self.r.run({"args": ["rm", "../BOM"]}) # Repacking Payload print(" Setting ownership...") stat = self.r.run({"args": "sudo chown -R 0:0 ./*", "shell": True}) if not stat[2] == 0: print("Something went wrong!\n") print(stat[1]) return print(" Repacking Payload...") stat = self.r.run({ "args": "sudo find . | sudo cpio -o --format odc | gzip -c > ../Payload", "shell": True }) if not stat[2] == 0: print("Something went wrong!\n") print(stat[1]) return # Generate BOM print(" Generating BOM...") stat = self.r.run({ "args": ["mkbom", "../../", "../BOM"], "sudo": True }) if not stat[2] == 0: print("Something went wrong!\n") print(stat[1]) return # Clean up the temp folder print(" Cleaning up...\n") os.chdir("../") stat = self.r.run({"args": ["rm", "-rf", "temp"], "sudo": True}) if not stat[2] == 0: print("Something went wrong!\n") print(stat[1]) return self.check_dir("Patched") print("Repacking...\n") os.chdir(os.path.dirname(os.path.realpath(__file__))) os.chdir("../Web Drivers/Patched/") suffix = " (Patched).pkg" if build == None else " ({}).pkg".format( build) self.r.run({ "args": [ "pkgutil", "--flatten", temp + "/package", os.getcwd() + "/" + os.path.basename(package)[:-4] + suffix ] }) print("Done.") self.r.run({"args": ["open", os.getcwd()]}) self.u.grab("", timeout=5)
def set_build(self, build_number): if not self.sip_checked: res = self.check_sip() if res == None or res == True: # Likely on Yosemite? self.sip_checked = True else: return self.u.head("Setting NVDARequiredOS to {}".format(build_number)) print(" ") os.chdir(os.path.dirname(os.path.realpath(__file__))) # Start our command list c = [] info_plist = plist.readPlist(self.wd_loc + "/Contents/Info.plist") if not os.path.exists(self.wd_loc + "/Contents/Info.plist.bak"): # Create a backup self.r.run({ "args": [ "cp", self.wd_loc + "/Contents/Info.plist", self.wd_loc + "/Contents/Info.plist.bak" ], "sudo": True, "message": "Creating backup...\n" }) # plist.writePlist(info_plist, self.wd_loc + "/Contents/Info.plist.bak") # Change the build number and write to the main plist print("Patching plist for build \"{}\"...\n".format(build_number)) info_plist["IOKitPersonalities"]["NVDAStartup"][ "NVDARequiredOS"] = build_number # Make a temp folder for our plist temp_folder = tempfile.mkdtemp() # Write the changes plist.writePlist(info_plist, temp_folder + "/Info.plist") # Build and run commands c = [{ "args": [ "mv", "-f", temp_folder + "/Info.plist", self.wd_loc + "/Contents/Info.plist" ], "sudo": True }, { "args": ["chown", "0:0", self.wd_loc + "/Contents/Info.plist"], "sudo": True, "message": "Updating ownership and permissions...\n" }, { "args": ["chmod", "755", self.wd_loc + "/Contents/Info.plist"], "sudo": True }, { "args": ["kextcache", "-i", "/"], "sudo": True, "stream": True, "message": "Rebuilding kext cache...\n" }, { "args": ["kextcache", "-u", "/"], "sudo": True, "stream": True, }] self.r.run(c, True) # Remove temp if os.path.exists(temp_folder): shutil.rmtree(temp_folder) print(" ") print("Done.") self.u.grab("", timeout=5) return
def main(self): self._check_info() self.get_system_info() self.u.head("Web Driver Toolkit") print(" ") os.chdir(os.path.dirname(os.path.realpath(__file__))) print("OS Version: {} - {}".format(self.os_number, self.os_build_number)) print("WD Version: " + self.installed_version) if self.wd_loc: info_plist = plist.readPlist(self.wd_loc + "/Contents/Info.plist") current_build = info_plist.get("IOKitPersonalities", {}).get( "NVDAStartup", {}).get("NVDARequiredOS", None) print("WD Target Build: {}".format(current_build)) if not "updates" in self.web_drivers: newest_version = "Manifest not available!" else: newest_version = "None for this build number!" for update in self.web_drivers.get("updates", []): if update["OS"].lower() == self.os_build_number.lower(): newest_version = update["version"] break if self.installed_version.lower() == newest_version.lower(): print("Newest: " + newest_version + " (Current)") else: print("Newest: " + newest_version) try: nv = self.r.run({ "args": "nvram -p | grep -i nvda_drv | cut -d$'\t' -f 2", "shell": True })[0].strip("\n") except: nv = "" aptio_loaded = "Unknown" aptio = next((x for x in bdmesg.bdmesg().split("\n") if "aptiomemoryfix" in x.lower()), None) if aptio: aptio_loaded = "Loaded" if "success" in aptio.lower( ) else "Not Loaded" if nv in ["1", "1%00"]: print("Status: Enabled (AptioMemoryFix {})".format( aptio_loaded)) else: print( "Status: Disabled - or Unknown (AptioMemoryFix {})". format(aptio_loaded)) print(" ") patch = False if self.wd_loc: print("P. Patch Menu") patch = True print("I. Patch Install Package") print("D. Download For Current") print("B. Download By Build Number") print("S. Search By Build/OS Number") print("R. Remove Web Drivers") print("U. Update Manifest") print("C. Config.plist Patch Menu") print("F. Flush Kext Cache") print("N. Attempt to {} nvram_drv=1 in NVRAM".format( "unset" if nv in ["1", "1%00"] else "set")) print("") print("Q. Quit") print(" ") menu = self.grab( "Please make a selection (just press enter to reload): ") if not len(menu): return if menu[:1].lower() == "q": self.custom_quit() elif menu[:1].lower() == "p" and patch: self.patch_menu() elif menu[:1].lower() == "d": self.download_for_build(self.os_build_number) elif menu[:1].lower() == "b": self.build_list() elif menu[:1].lower() == "s": self.build_search() elif menu[:1].lower() == "i": self.patch_installer_build() elif menu[:1].lower() == "u": self.get_manifest() elif menu[:1].lower() == "r": self.remove_drivers() elif menu[:1].lower() == "c": self.config_menu() elif menu[:1].lower() == "f": self.flush_cache(True) elif menu[:1].lower() == "n": if nv in ["1", "1%00"]: # Unset self.u.head("Unsetting nvda_drv=1") print("") print("Running:\n\nsudo nvram -d nvda_drv") self.r.run({ "args": ["sudo", "nvram", "-d", "nvda_drv"], "stream": True }) else: # Set self.u.head("Setting nvda_drv=1") print("") print("Running:\n\nsudo nvram nvda_drv=1") self.r.run({ "args": ["sudo", "nvram", "nvda_drv=1"], "stream": True }) print("") print("Done.") self.u.grab("", timeout=5) return