# Copyright (C) 2022, Mykola Grymalyuk # Copyright (c) 2023 Jazzzny import wx import wx.html2 import requests import markdown2 import logging import plistlib import subprocess import webbrowser import hashlib from pathlib import Path from resources import utilities, updates, global_settings, network_handler, constants from resources.sys_patch import sys_patch_detect from resources.wx_gui import gui_entry, gui_support from data import css_data class AutomaticSysPatch: """ Library of functions for launch agent, including automatic patching """ def __init__(self, global_constants: constants.Constants): self.constants: constants.Constants = global_constants def start_auto_patch(self): """ Initiates automatic patching Auto Patching's main purpose is to try and tell the user they're missing root patches New users may not realize OS updates remove our patches, so we try and run when nessasary Conditions for running: - Verify running GUI (TUI users can write their own scripts) - Verify the Snapshot Seal is intact (if not, assume user is running patches) - Verify this model needs patching (if not, assume user upgraded hardware and OCLP was not removed) - Verify there are no updates for OCLP (ensure we have the latest patch sets) If all these tests pass, start Root Patcher """ logging.info("- Starting Automatic Patching") if self.constants.wxpython_variant is False: logging.info("- Auto Patch option is not supported on TUI, please use GUI") return dict = updates.CheckBinaryUpdates(self.constants).check_binary_updates() if dict: version = dict["Version"] logging.info(f"- Found new version: {version}") app = wx.App() mainframe = wx.Frame(None, -1, "OpenCore Legacy Patcher") ID_GITHUB = wx.NewId() ID_UPDATE = wx.NewId() url = "https://api.github.com/repos/dortania/OpenCore-Legacy-Patcher/releases/latest" response = requests.get(url).json() try: changelog = response["body"].split("## Asset Information")[0] except: #if user constantly checks for updates, github will rate limit them changelog = """## Unable to fetch changelog Please check the Github page for more information about this release.""" html_markdown = markdown2.markdown(changelog, extras=["tables"]) html_css = css_data.updater_css frame = wx.Dialog(None, -1, title="", size=(650, 500)) frame.SetMinSize((650, 500)) frame.SetWindowStyle(wx.STAY_ON_TOP) panel = wx.Panel(frame) sizer = wx.BoxSizer(wx.VERTICAL) sizer.AddSpacer(10) self.title_text = wx.StaticText(panel, label="A new version of OpenCore Legacy Patcher is available!") self.description = wx.StaticText(panel, label=f"OpenCore Legacy Patcher {version} is now available - You have {self.constants.patcher_version}{' (Nightly)' if not self.constants.commit_info[0].startswith('refs/tags') else ''}. Would you like to update?") self.title_text.SetFont(gui_support.font_factory(19, wx.FONTWEIGHT_BOLD)) self.description.SetFont(gui_support.font_factory(13, wx.FONTWEIGHT_NORMAL)) self.web_view = wx.html2.WebView.New(panel, style=wx.BORDER_SUNKEN) html_code = f''' {html_markdown.replace(" bool: # Get kext list in /Library/Extensions that have the 'GPUCompanionBundles' property # This is used to determine if we need to run the RSRMonitor logging.info("- Checking if RSRMonitor is needed") cryptex_path = f"/System/Volumes/Preboot/{utilities.get_preboot_uuid()}/cryptex1/current/OS.dmg" if not Path(cryptex_path).exists(): logging.info("- No OS.dmg, skipping RSRMonitor") return False kexts = [] for kext in Path("/Library/Extensions").glob("*.kext"): if not Path(f"{kext}/Contents/Info.plist").exists(): continue try: kext_plist = plistlib.load(open(f"{kext}/Contents/Info.plist", "rb")) except Exception as e: logging.info(f" - Failed to load plist for {kext.name}: {e}") continue if "GPUCompanionBundles" not in kext_plist: continue logging.info(f" - Found kext with GPUCompanionBundles: {kext.name}") kexts.append(kext.name) # If we have no kexts, we don't need to run the RSRMonitor if not kexts: logging.info("- No kexts found with GPUCompanionBundles, skipping RSRMonitor") return False # Load the RSRMonitor plist rsr_monitor_plist = plistlib.load(open(self.constants.rsr_monitor_launch_daemon_path, "rb")) arguments = ["/bin/rm", "-Rfv"] arguments += [f"/Library/Extensions/{kext}" for kext in kexts] # Add the arguments to the RSRMonitor plist rsr_monitor_plist["ProgramArguments"] = arguments # Next add monitoring for '/System/Volumes/Preboot/{UUID}/cryptex1/OS.dmg' logging.info(f" - Adding monitor: {cryptex_path}") rsr_monitor_plist["WatchPaths"] = [ cryptex_path, ] # Write the RSRMonitor plist plistlib.dump(rsr_monitor_plist, Path(self.constants.rsr_monitor_launch_daemon_path).open("wb")) return True