Ejemplo n.º 1
0
    def monu(self):
        response = None
        while not (response and response == utils.TUIMenu.EXIT_MENU):
            in_between = [("Saved Data: {}" + Colors.RESET.value).format(
                Colors.GREEN.value +
                "Loaded" if self.json_path.exists() else (Colors.RED.value +
                                                          "None"))]

            menu = utils.TUIMenu(f"USBToolBox {shared.VERSION}",
                                 "Select an option: ",
                                 in_between=in_between,
                                 top_level=True)

            menu_options = [
                # ["H", "Print Historical", self.print_historical],
                ["D", "Discover Ports", self.discover_ports],
                ["S", "Select Ports and Build Kext", self.select_ports],
                ["C", "Change Settings", self.change_settings],
            ]
            if self.json_path.exists():
                menu_options.insert(
                    0, ["P", "Delete Saved USB Data", self.remove_historical])
            for i in menu_options:
                menu.add_menu_option(i[1], None, i[2], i[0])

            response = menu.start()
        self.on_quit()
        self.utils.custom_quit()
Ejemplo n.º 2
0
    def change_settings(self):
        def functionify(func):
            return lambda *args, **kwargs: lambda: func(*args, **kwargs)

        @functionify
        def color_status(name, variable):
            return f"{name}: {color('Enabled').green if self.settings[variable] else color('Disabled').red}"

        @functionify
        def toggle_setting(variable):
            self.settings[variable] = not self.settings[variable]

        def combination(name, variable):
            return color_status(name, variable), toggle_setting(variable)

        menu = utils.TUIMenu("Change Settings", "Toggle a setting: ", loop=True)
        for i in [
            ["T", *combination("Show Friendly Types", "show_friendly_types"), ["Show friendly types (ie. 'USB 3 Type A') instead of numbers."]],
            ["N", *combination("Use Native Classes", "use_native"), ["Use native Apple classes (AppleUSBHostMergeProperties) instead of the USBToolBox kext."]],
            ["A", *combination("Add Comments to Map", "add_comments_to_map"), ["Add port comments inside the map."]],
            [
                "C",
                *combination("Bind Companions", "auto_bind_companions"),
                ["Tie companion ports together. If one companion is enabled/disable/port type changed, the other companion will also be affected."],
            ],
        ]:
            menu.add_menu_option(name=i[1], function=i[2], key=i[0], description=i[3] if len(i) == 4 else None)

        menu.start()
        self.dump_settings()
Ejemplo n.º 3
0
 def print_errors(self, errors):
     if not errors:
         return True
     utils.TUIMenu("Selection Validation",
                   "Select an option: ",
                   in_between=errors,
                   loop=True).start()
     return False
Ejemplo n.º 4
0
 def print_types(self):
     in_between = [f"{i}: {i.value}" for i in shared.USBPhysicalPortTypes] + [
         "",
         textwrap.dedent(
             """\
         The difference between connector types 9 and 10 is if you reverse the plug and the devices are connected to the same ports as before, they have a switch (type 9).
         If not, and they are connected to different ports, they do not have a switch (type 10)."""
         ),
         "",
         "For more information and pictures, go to https://github.com/USBToolBox/tool/blob/master/TYPES.md.",
     ]
     utils.TUIMenu("USB Types", "Select an option: ", in_between=in_between).start()
Ejemplo n.º 5
0
    def validate_selections(self):
        errors = []
        if not any(any(p["selected"] for p in c["ports"]) for c in self.controllers_historical):
            utils.TUIMenu("Selection Validation", "Select an option: ", in_between=["No ports are selected! Select some ports."], loop=True).start()
            return False

        for controller in self.controllers_historical:
            for port in controller["ports"]:
                if not port["selected"]:
                    continue
                if port["type"] is None and port["guessed"] is None:
                    errors.append(f"Port {port['selection_index']} is missing a connector type!")

        return self.print_errors(errors)
Ejemplo n.º 6
0
    def build_kext(self):
        empty_controllers = [
            c for c in self.controllers_historical
            if not any(p["selected"] for p in c["ports"])
        ]
        response = None
        if empty_controllers:
            empty_menu = utils.TUIMenu(
                "Selection Validation",
                "Select an option: ",
                in_between=[
                    "The following controllers have no enabled ports:", ""
                ] + [controller["name"] for controller in empty_controllers] +
                [
                    "Select whether to ignore these controllers and exclude them from the map, or disable all ports on these controllers."
                ],
                add_quit=False,
                return_number=True,
            )
            empty_menu.add_menu_option("Ignore", key="I")
            empty_menu.add_menu_option("Disable", key="D")
            response = empty_menu.start()

        model_identifier = None
        if self.settings["use_native"]:
            if platform.system() == "Darwin":
                model_identifier = plistlib.loads(
                    subprocess.run(
                        "system_profiler -detailLevel mini -xml SPHardwareDataType"
                        .split(),
                        stdout=subprocess.PIPE,
                        stderr=subprocess.STDOUT).stdout.strip()
                )[0]["_items"][0]["machine_model"]
            else:
                model_menu = utils.TUIOnlyPrint(
                    "Enter Model Identifier",
                    "Enter the model identifier: ",
                    [
                        "You are seeing this as you have selected to use AppleUSBHostController. Model identifier autodetection is unavailable as you are not on macOS.",
                        "Please enter the model identifier of the target system below. You can find it in System Information or with 'system_profiler -detailLevel mini SPHardwareDataType'.",
                    ],
                ).start()
                model_identifier = model_menu.strip()

        ignore = response == "I"

        template = plistlib.load(
            (shared.resource_dir / Path("Info.plist")).open("rb"))

        menu = utils.TUIMenu("Building USBMap", "Select an option: ")
        menu.head()
        print("Generating Info.plist...")
        for controller in self.controllers_historical:
            if not any(i["selected"] for i in controller["ports"]) and ignore:
                continue

            # FIXME: ensure unique
            if controller["identifiers"].get("acpi_path"):
                if self.check_unique(
                        lambda c: c["identifiers"]["acpi_path"].rpartition(
                            ".")[2], lambda c: "acpi_path" in c["identifiers"],
                        controller):
                    personality_name: str = controller["identifiers"][
                        "acpi_path"].rpartition(".")[2]
                else:
                    personality_name: str = controller["identifiers"][
                        "acpi_path"][1:]  # Strip leading \
            elif controller["identifiers"].get("bdf"):
                personality_name: str = ":".join(
                    [str(i) for i in controller["identifiers"]["bdf"]])
            else:
                personality_name: str = controller["name"]

            if self.settings["use_native"]:
                personality = {
                    "CFBundleIdentifier":
                    "com.apple.driver.AppleUSBHostMergeProperties",
                    "IOClass": "AppleUSBHostMergeProperties",
                    "IOProviderClass": "AppleUSBHostController",
                    "IOParentMatch": self.choose_matching_key(controller),
                    "model": model_identifier,
                }

            else:
                personality = {
                    "CFBundleIdentifier": "com.dhinakg.USBToolBox.kext",
                    "IOClass": "USBToolBox",
                    "IOProviderClass": "IOPCIDevice",
                    "IOMatchCategory": "USBToolBox",
                } | self.choose_matching_key(controller)  # type: ignore

            personality["IOProviderMergeProperties"] = {
                "ports": {},
                "port-count": None
            }

            port_name_index = {}
            highest_index = 0

            for port in controller["ports"]:
                if not port["selected"]:
                    continue

                if port["index"] > highest_index:
                    highest_index = port["index"]

                if controller[
                        "class"] == shared.USBControllerTypes.XHCI and port[
                            "class"] == shared.USBDeviceSpeeds.SuperSpeed:
                    prefix = "SS"
                elif controller[
                        "class"] == shared.USBControllerTypes.XHCI and port[
                            "class"] == shared.USBDeviceSpeeds.HighSpeed:
                    prefix = "HS"
                else:
                    prefix = "PRT"

                port_index = port_name_index.setdefault(prefix, 1)
                port_name = prefix + str(port_index).zfill(4 - len(prefix))
                port_name_index[prefix] += 1

                personality["IOProviderMergeProperties"]["ports"][
                    port_name] = {
                        "port":
                        binascii.a2b_hex(
                            hexswap(hex(port["index"])[2:].zfill(8))),
                        "UsbConnector":
                        port["type"] or port["guessed"],
                    }

                if self.settings["add_comments_to_map"] and port["comment"]:
                    personality["IOProviderMergeProperties"]["ports"][
                        port_name]["#comment"] = port["comment"]

            personality["IOProviderMergeProperties"][
                "port-count"] = binascii.a2b_hex(
                    hexswap(hex(highest_index)[2:].zfill(8)))

            template["IOKitPersonalities"][personality_name] = personality

        if not self.settings["use_native"]:
            template["OSBundleLibraries"] = {
                "com.dhinakg.USBToolBox.kext": "1.0.0"
            }

        write_path = shared.current_dir / (Path("USBMap.kext")
                                           if self.settings["use_native"] else
                                           Path("UTBMap.kext"))

        if write_path.exists():
            print("Removing existing kext...")
            shutil.rmtree(write_path)

        print("Writing kext and Info.plist...")
        (write_path / Path("Contents")).mkdir(parents=True)
        plistlib.dump(template,
                      (write_path / Path("Contents/Info.plist")).open("wb"),
                      sort_keys=True)
        print(f"Done. Saved to {write_path.resolve()}.\n")
        menu.print_options()

        menu.select()
        return True
Ejemplo n.º 7
0
    def select_ports(self):
        if not self.controllers_historical:
            utils.TUIMenu("Select Ports and Build Kext",
                          "Select an option: ",
                          in_between=["No ports! Use the discovery mode."],
                          loop=True).start()
            return

        selection_index = 1
        by_port = []
        for controller in self.controllers_historical:
            controller["selected_count"] = 0
            for port in controller["ports"]:
                if "selected" not in port:
                    port["selected"] = bool(port["devices"])
                    port["selected"] = port["selected"] or (
                        bool(self.get_companion_port(port)["devices"])
                        if self.get_companion_port(port) else False)
                controller["selected_count"] += 1 if port["selected"] else 0
                port["selection_index"] = selection_index
                selection_index += 1
                by_port.append(port)

        while True:
            self.dump_historical()
            for controller in self.controllers_historical:
                controller["selected_count"] = sum(
                    1 if port["selected"] else 0
                    for port in controller["ports"])

            utils.header("Select Ports and Build Kext")
            print()
            for controller in self.controllers_historical:
                port_count_str = f"{controller['selected_count']}/{len(controller['ports'])}"
                port_count_str = color(port_count_str).red if controller[
                    "selected_count"] > 15 else color(port_count_str).green
                print(
                    self.controller_to_str(controller) +
                    f" | {port_count_str} ports")
                for port in controller["ports"]:
                    port_info = f"[{'#' if port['selected'] else ' '}]  {port['selection_index']}.{(len(str(selection_index)) - len(str(port['selection_index'])) + 1) * ' ' }" + self.port_to_str(
                        port)
                    companion = self.get_companion_port(port)
                    if companion:
                        port_info += f" | Companion to {companion['selection_index']}"
                    if port["selected"]:
                        print(color(port_info).green.bold)
                    else:
                        print(port_info)
                    if port["comment"]:
                        print(
                            len(f"[{'#' if port['selected'] else ' '}]  {port['selection_index']}.{(len(str(selection_index)) - len(str(port['selection_index'])) + 1) * ' ' }"
                                ) * " " + color(port["comment"]).blue.bold)
                    for device in port["devices"]:
                        self.print_devices(device,
                                           indentation="      " +
                                           len(str(selection_index)) * " " * 2)
                print()

            print(
                f"Binding companions is currently {color('on').green if self.settings['auto_bind_companions'] else color('off').red}.\n"
            )

            print(
                textwrap.dedent(f"""\
                K. Build {'USBMap' if self.settings['use_native'] else 'UTBMap'}.kext
                A. Select All
                N. Select None
                P. Enable All Populated Ports
                D. Disable All Empty Ports
                T. Show Types

                B. Back

                - Select ports to toggle with comma-delimited lists (eg. 1,2,3,4,5)
                - Change types using this formula T:1,2,3,4,5:t where t is the type
                - Set custom names using this formula C:1:Name - Name = None to clear"""
                                ))

            output = input("Select an option: ")
            if not output:
                continue
            elif output.upper() == "B":
                break
            elif output.upper() == "K":
                if not self.validate_selections():
                    continue
                self.build_kext()
                continue
            elif output.upper() in ("N", "A"):
                for port in by_port:
                    port["selected"] = output.upper() == "A"
            elif output.upper() == "P":
                for port in by_port:
                    if port["devices"] or (
                            self.get_companion_port(port)["devices"]
                            if self.get_companion_port(port) else False):
                        port["selected"] = True
            elif output.upper() == "D":
                for port in by_port:
                    if not port["devices"] and not (
                            self.get_companion_port(port)["devices"]
                            if self.get_companion_port(port) else False):
                        port["selected"] = False
            elif output.upper() == "T":
                self.print_types()
                continue
            elif output[0].upper() == "T":
                # We should have a type
                if len(output.split(":")) != 3:
                    continue
                try:
                    port_nums, port_type = output.split(":")[1:]
                    port_nums = port_nums.replace(" ", "").split(",")
                    port_type = shared.USBPhysicalPortTypes(int(port_type))

                    for port_num in list(port_nums):
                        if port_num not in port_nums:
                            continue

                        port_num = int(port_num) - 1

                        if port_num not in range(len(by_port)):
                            continue

                        companion = self.get_companion_port(by_port[port_num])
                        if self.settings["auto_bind_companions"] and companion:
                            companion["type"] = port_type
                            if str(companion["selection_index"]) in port_nums:
                                port_nums.remove(
                                    str(companion["selection_index"]))
                        by_port[port_num]["type"] = port_type
                except ValueError:
                    continue
            elif output[0].upper() == "C":
                # We should have a new name
                if len(output.split(":")) < 2:
                    continue
                try:
                    port_nums = output.split(":")[1].replace(" ",
                                                             "").split(",")
                    port_comment = output.split(":", 2)[2:]

                    for port_num in list(port_nums):
                        if port_num not in port_nums:
                            continue

                        port_num = int(port_num) - 1

                        if port_num not in range(len(by_port)):
                            continue

                        by_port[port_num]["comment"] = port_comment[
                            0] if port_comment else None
                except ValueError:
                    continue
            else:
                try:
                    port_nums = output.replace(" ", "").split(",")

                    for port_num in list(port_nums):
                        if port_num not in port_nums:
                            continue

                        port_num = int(port_num) - 1

                        if port_num not in range(len(by_port)):
                            continue

                        companion = self.get_companion_port(by_port[port_num])
                        if self.settings["auto_bind_companions"] and companion:
                            companion[
                                "selected"] = not by_port[port_num]["selected"]
                            if str(companion["selection_index"]) in port_nums:
                                port_nums.remove(
                                    str(companion["selection_index"]))
                        by_port[port_num][
                            "selected"] = not by_port[port_num]["selected"]
                except ValueError:
                    continue
Ejemplo n.º 8
0
 def print_historical(self):
     utils.TUIMenu("Print Historical (DEBUG)",
                   "Select an option: ",
                   in_between=lambda: self.print_controllers(
                       self.controllers_historical),
                   loop=True).start()