package utils import ( "fmt" "os" "github.com/AlecAivazis/survey/v2" "github.com/AlecAivazis/survey/v2/terminal" "github.com/apex/log" "github.com/blacktop/ipsw/pkg/usb" "github.com/blacktop/ipsw/pkg/usb/lockdownd" "github.com/blacktop/ipsw/pkg/usb/mount" ) func PickDevice() (*lockdownd.DeviceValues, error) { var deets []*lockdownd.DeviceValues conn, err := usb.NewConn() if err != nil { return nil, fmt.Errorf("failed to connect to usbmuxd: %w", err) } defer conn.Close() devices, err := conn.ListDevices() if err != nil { return nil, fmt.Errorf("failed to list devices: %w", err) } if len(devices) == 0 { return nil, fmt.Errorf("no devices found") } for _, device := range devices { ldc, err := lockdownd.NewClient(device.SerialNumber) if err != nil { return nil, err } deet, err := ldc.GetValues() if err != nil { return nil, err } deets = append(deets, deet) ldc.Close() } if len(deets) == 1 { return deets[0], nil } selected := make(map[string]*lockdownd.DeviceValues, len(deets)) for _, d := range deets { selected[fmt.Sprintf("%s_%s_%s", d.ProductType, d.HardwareModel, d.BuildVersion)] = d } if len(selected) == 1 { return deets[0], nil // can happen if device setup to connect via network } var choices []string for s := range selected { choices = append(choices, s) } var picked string prompt := &survey.Select{ Message: "Select what iDevice to connect to:", Options: choices, } if err := survey.AskOne(prompt, &picked); err == terminal.InterruptErr { log.Warn("Exiting...") os.Exit(0) } return selected[picked], nil } func PickDevices() ([]*lockdownd.DeviceValues, error) { var deets []*lockdownd.DeviceValues conn, err := usb.NewConn() if err != nil { return nil, fmt.Errorf("failed to connect to usbmuxd: %w", err) } defer conn.Close() devices, err := conn.ListDevices() if err != nil { return nil, fmt.Errorf("failed to list devices: %w", err) } if len(devices) == 0 { return nil, fmt.Errorf("no devices found") } for _, device := range devices { ldc, err := lockdownd.NewClient(device.SerialNumber) if err != nil { return nil, err } deet, err := ldc.GetValues() if err != nil { return nil, err } deets = append(deets, deet) ldc.Close() } if len(deets) == 1 { return deets, nil } else { var choices []string for _, d := range deets { choices = append(choices, fmt.Sprintf("%s_%s_%s", d.ProductType, d.HardwareModel, d.BuildVersion)) } selected := []int{} prompt := &survey.MultiSelect{ Message: "Select what iDevices to connect to:", Options: choices, } if err := survey.AskOne(prompt, &selected); err == terminal.InterruptErr { log.Warn("Exiting...") os.Exit(0) } // filter based on selection var picked []*lockdownd.DeviceValues for _, idx := range selected { picked = append(picked, deets[idx]) } return picked, nil } } func IsDeveloperModeEnabled(udid string) (bool, error) { cli, err := lockdownd.NewClient(udid) if err != nil { return false, fmt.Errorf("failed to connect to lockdownd: %w", err) } defer cli.Close() return cli.DeveloperModeEnabled() } func IsDeveloperImageMounted(udid string) error { cli, err := mount.NewClient(udid) if err != nil { return fmt.Errorf("failed to connect to mobile_image_mounter: %w", err) } defer cli.Close() images, err := cli.ListImages() if err != nil { return fmt.Errorf("failed to list images: %w", err) } if len(images) == 0 { return fmt.Errorf("mount the Developer image with the `ipsw idev img mount` command or by opening Xcode") } return nil }