pymobiledevice3 5.0.0__py3-none-any.whl → 5.0.2__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of pymobiledevice3 might be problematic. Click here for more details.
- misc/plist_sniffer.py +15 -15
- misc/remotexpc_sniffer.py +29 -28
- pymobiledevice3/__main__.py +128 -102
- pymobiledevice3/_version.py +2 -2
- pymobiledevice3/bonjour.py +26 -49
- pymobiledevice3/ca.py +32 -24
- pymobiledevice3/cli/activation.py +7 -7
- pymobiledevice3/cli/afc.py +19 -19
- pymobiledevice3/cli/amfi.py +4 -4
- pymobiledevice3/cli/apps.py +51 -39
- pymobiledevice3/cli/backup.py +58 -32
- pymobiledevice3/cli/bonjour.py +25 -18
- pymobiledevice3/cli/cli_common.py +112 -81
- pymobiledevice3/cli/companion_proxy.py +4 -4
- pymobiledevice3/cli/completions.py +10 -10
- pymobiledevice3/cli/crash.py +37 -31
- pymobiledevice3/cli/developer.py +602 -520
- pymobiledevice3/cli/diagnostics.py +38 -33
- pymobiledevice3/cli/lockdown.py +79 -74
- pymobiledevice3/cli/mounter.py +85 -68
- pymobiledevice3/cli/notification.py +10 -10
- pymobiledevice3/cli/pcap.py +19 -14
- pymobiledevice3/cli/power_assertion.py +12 -10
- pymobiledevice3/cli/processes.py +10 -10
- pymobiledevice3/cli/profile.py +88 -77
- pymobiledevice3/cli/provision.py +17 -17
- pymobiledevice3/cli/remote.py +186 -110
- pymobiledevice3/cli/restore.py +43 -40
- pymobiledevice3/cli/springboard.py +30 -28
- pymobiledevice3/cli/syslog.py +85 -58
- pymobiledevice3/cli/usbmux.py +21 -20
- pymobiledevice3/cli/version.py +3 -2
- pymobiledevice3/cli/webinspector.py +157 -79
- pymobiledevice3/common.py +1 -1
- pymobiledevice3/exceptions.py +154 -60
- pymobiledevice3/irecv.py +49 -53
- pymobiledevice3/irecv_devices.py +1489 -492
- pymobiledevice3/lockdown.py +394 -241
- pymobiledevice3/lockdown_service_provider.py +5 -7
- pymobiledevice3/osu/os_utils.py +18 -9
- pymobiledevice3/osu/posix_util.py +28 -15
- pymobiledevice3/osu/win_util.py +14 -8
- pymobiledevice3/pair_records.py +19 -19
- pymobiledevice3/remote/common.py +4 -4
- pymobiledevice3/remote/core_device/app_service.py +94 -67
- pymobiledevice3/remote/core_device/core_device_service.py +17 -14
- pymobiledevice3/remote/core_device/device_info.py +5 -5
- pymobiledevice3/remote/core_device/diagnostics_service.py +10 -8
- pymobiledevice3/remote/core_device/file_service.py +47 -33
- pymobiledevice3/remote/remote_service_discovery.py +53 -35
- pymobiledevice3/remote/remotexpc.py +62 -41
- pymobiledevice3/remote/tunnel_service.py +371 -293
- pymobiledevice3/remote/utils.py +12 -11
- pymobiledevice3/remote/xpc_message.py +145 -125
- pymobiledevice3/resources/dsc_uuid_map.py +19 -19
- pymobiledevice3/resources/firmware_notifications.py +16 -16
- pymobiledevice3/restore/asr.py +27 -27
- pymobiledevice3/restore/base_restore.py +90 -47
- pymobiledevice3/restore/consts.py +87 -66
- pymobiledevice3/restore/device.py +11 -11
- pymobiledevice3/restore/fdr.py +46 -46
- pymobiledevice3/restore/ftab.py +19 -19
- pymobiledevice3/restore/img4.py +130 -133
- pymobiledevice3/restore/mbn.py +35 -54
- pymobiledevice3/restore/recovery.py +125 -135
- pymobiledevice3/restore/restore.py +524 -523
- pymobiledevice3/restore/restore_options.py +122 -115
- pymobiledevice3/restore/restored_client.py +25 -22
- pymobiledevice3/restore/tss.py +378 -270
- pymobiledevice3/service_connection.py +50 -46
- pymobiledevice3/services/accessibilityaudit.py +136 -126
- pymobiledevice3/services/afc.py +350 -291
- pymobiledevice3/services/amfi.py +21 -18
- pymobiledevice3/services/companion.py +23 -19
- pymobiledevice3/services/crash_reports.py +60 -46
- pymobiledevice3/services/debugserver_applist.py +3 -3
- pymobiledevice3/services/device_arbitration.py +8 -8
- pymobiledevice3/services/device_link.py +55 -47
- pymobiledevice3/services/diagnostics.py +971 -968
- pymobiledevice3/services/dtfetchsymbols.py +8 -8
- pymobiledevice3/services/dvt/dvt_secure_socket_proxy.py +4 -4
- pymobiledevice3/services/dvt/dvt_testmanaged_proxy.py +4 -4
- pymobiledevice3/services/dvt/instruments/activity_trace_tap.py +85 -74
- pymobiledevice3/services/dvt/instruments/application_listing.py +2 -3
- pymobiledevice3/services/dvt/instruments/condition_inducer.py +7 -6
- pymobiledevice3/services/dvt/instruments/core_profile_session_tap.py +466 -384
- pymobiledevice3/services/dvt/instruments/device_info.py +11 -11
- pymobiledevice3/services/dvt/instruments/energy_monitor.py +1 -1
- pymobiledevice3/services/dvt/instruments/graphics.py +1 -1
- pymobiledevice3/services/dvt/instruments/location_simulation.py +1 -1
- pymobiledevice3/services/dvt/instruments/location_simulation_base.py +10 -10
- pymobiledevice3/services/dvt/instruments/network_monitor.py +17 -17
- pymobiledevice3/services/dvt/instruments/notifications.py +1 -1
- pymobiledevice3/services/dvt/instruments/process_control.py +25 -10
- pymobiledevice3/services/dvt/instruments/screenshot.py +2 -2
- pymobiledevice3/services/dvt/instruments/sysmontap.py +15 -15
- pymobiledevice3/services/dvt/testmanaged/xcuitest.py +40 -50
- pymobiledevice3/services/file_relay.py +10 -10
- pymobiledevice3/services/heartbeat.py +8 -7
- pymobiledevice3/services/house_arrest.py +12 -15
- pymobiledevice3/services/installation_proxy.py +119 -100
- pymobiledevice3/services/lockdown_service.py +12 -5
- pymobiledevice3/services/misagent.py +22 -19
- pymobiledevice3/services/mobile_activation.py +84 -72
- pymobiledevice3/services/mobile_config.py +330 -301
- pymobiledevice3/services/mobile_image_mounter.py +137 -116
- pymobiledevice3/services/mobilebackup2.py +188 -150
- pymobiledevice3/services/notification_proxy.py +11 -11
- pymobiledevice3/services/os_trace.py +69 -51
- pymobiledevice3/services/pcapd.py +306 -306
- pymobiledevice3/services/power_assertion.py +10 -9
- pymobiledevice3/services/preboard.py +4 -4
- pymobiledevice3/services/remote_fetch_symbols.py +16 -14
- pymobiledevice3/services/remote_server.py +176 -146
- pymobiledevice3/services/restore_service.py +16 -16
- pymobiledevice3/services/screenshot.py +13 -10
- pymobiledevice3/services/simulate_location.py +7 -7
- pymobiledevice3/services/springboard.py +15 -15
- pymobiledevice3/services/syslog.py +5 -5
- pymobiledevice3/services/web_protocol/alert.py +3 -3
- pymobiledevice3/services/web_protocol/automation_session.py +180 -176
- pymobiledevice3/services/web_protocol/cdp_screencast.py +44 -36
- pymobiledevice3/services/web_protocol/cdp_server.py +19 -19
- pymobiledevice3/services/web_protocol/cdp_target.py +411 -373
- pymobiledevice3/services/web_protocol/driver.py +47 -45
- pymobiledevice3/services/web_protocol/element.py +74 -63
- pymobiledevice3/services/web_protocol/inspector_session.py +106 -102
- pymobiledevice3/services/web_protocol/selenium_api.py +2 -2
- pymobiledevice3/services/web_protocol/session_protocol.py +15 -10
- pymobiledevice3/services/web_protocol/switch_to.py +11 -12
- pymobiledevice3/services/webinspector.py +127 -116
- pymobiledevice3/tcp_forwarder.py +35 -22
- pymobiledevice3/tunneld/api.py +20 -15
- pymobiledevice3/tunneld/server.py +212 -133
- pymobiledevice3/usbmux.py +183 -138
- pymobiledevice3/utils.py +14 -11
- {pymobiledevice3-5.0.0.dist-info → pymobiledevice3-5.0.2.dist-info}/METADATA +1 -1
- pymobiledevice3-5.0.2.dist-info/RECORD +173 -0
- pymobiledevice3-5.0.0.dist-info/RECORD +0 -173
- {pymobiledevice3-5.0.0.dist-info → pymobiledevice3-5.0.2.dist-info}/WHEEL +0 -0
- {pymobiledevice3-5.0.0.dist-info → pymobiledevice3-5.0.2.dist-info}/entry_points.txt +0 -0
- {pymobiledevice3-5.0.0.dist-info → pymobiledevice3-5.0.2.dist-info}/licenses/LICENSE +0 -0
- {pymobiledevice3-5.0.0.dist-info → pymobiledevice3-5.0.2.dist-info}/top_level.txt +0 -0
|
@@ -17,25 +17,25 @@ def cli() -> None:
|
|
|
17
17
|
|
|
18
18
|
@cli.group()
|
|
19
19
|
def notification() -> None:
|
|
20
|
-
"""
|
|
20
|
+
"""Post/Observe notifications"""
|
|
21
21
|
pass
|
|
22
22
|
|
|
23
23
|
|
|
24
24
|
@notification.command(cls=Command)
|
|
25
|
-
@click.argument(
|
|
26
|
-
@click.option(
|
|
25
|
+
@click.argument("names", nargs=-1)
|
|
26
|
+
@click.option("--insecure", is_flag=True, help="use the insecure relay meant for untrusted clients instead")
|
|
27
27
|
def post(service_provider: LockdownClient, names, insecure):
|
|
28
|
-
"""
|
|
28
|
+
"""API for notify_post()."""
|
|
29
29
|
service = NotificationProxyService(lockdown=service_provider, insecure=insecure)
|
|
30
30
|
for name in names:
|
|
31
31
|
service.notify_post(name)
|
|
32
32
|
|
|
33
33
|
|
|
34
34
|
@notification.command(cls=Command)
|
|
35
|
-
@click.argument(
|
|
36
|
-
@click.option(
|
|
35
|
+
@click.argument("names", nargs=-1)
|
|
36
|
+
@click.option("--insecure", is_flag=True, help="use the insecure relay meant for untrusted clients instead")
|
|
37
37
|
def observe(service_provider: LockdownClient, names, insecure):
|
|
38
|
-
"""
|
|
38
|
+
"""API for notify_register_dispatch()."""
|
|
39
39
|
service = NotificationProxyService(lockdown=service_provider, insecure=insecure)
|
|
40
40
|
for name in names:
|
|
41
41
|
service.notify_register_dispatch(name)
|
|
@@ -44,10 +44,10 @@ def observe(service_provider: LockdownClient, names, insecure):
|
|
|
44
44
|
logger.info(event)
|
|
45
45
|
|
|
46
46
|
|
|
47
|
-
@notification.command(
|
|
48
|
-
@click.option(
|
|
47
|
+
@notification.command("observe-all", cls=Command)
|
|
48
|
+
@click.option("--insecure", is_flag=True, help="use the insecure relay meant for untrusted clients instead")
|
|
49
49
|
def observe_all(service_provider: LockdownClient, insecure):
|
|
50
|
-
"""
|
|
50
|
+
"""attempt to observe all builtin firmware notifications."""
|
|
51
51
|
service = NotificationProxyService(lockdown=service_provider, insecure=insecure)
|
|
52
52
|
for notification in get_notifications():
|
|
53
53
|
service.notify_register_dispatch(notification)
|
pymobiledevice3/cli/pcap.py
CHANGED
|
@@ -17,19 +17,19 @@ def cli() -> None:
|
|
|
17
17
|
def print_packet_header(packet, color: bool) -> None:
|
|
18
18
|
date = datetime.fromtimestamp(packet.seconds + (packet.microseconds / 1000000))
|
|
19
19
|
data = (
|
|
20
|
-
f
|
|
21
|
-
f
|
|
22
|
-
f
|
|
23
|
-
f
|
|
20
|
+
f"{date}: "
|
|
21
|
+
f"Process {packet.comm} ({packet.pid}), "
|
|
22
|
+
f"Interface: {packet.interface_name} ({packet.interface_type.name}), "
|
|
23
|
+
f"Family: {packet.protocol_family.name}"
|
|
24
24
|
)
|
|
25
25
|
if not color:
|
|
26
26
|
print(data)
|
|
27
27
|
else:
|
|
28
|
-
print(highlight(data, lexers.HspecLexer(), formatters.Terminal256Formatter(style=
|
|
28
|
+
print(highlight(data, lexers.HspecLexer(), formatters.Terminal256Formatter(style="native")), end="")
|
|
29
29
|
|
|
30
30
|
|
|
31
31
|
def print_packet(packet, color: Optional[bool] = None):
|
|
32
|
-
"""
|
|
32
|
+
"""Return the packet, so it can be chained in a generator"""
|
|
33
33
|
if color is None:
|
|
34
34
|
color = user_requested_colored_output()
|
|
35
35
|
print_packet_header(packet, color)
|
|
@@ -38,18 +38,23 @@ def print_packet(packet, color: Optional[bool] = None):
|
|
|
38
38
|
|
|
39
39
|
|
|
40
40
|
@cli.command(cls=Command)
|
|
41
|
-
@click.argument(
|
|
42
|
-
@click.option(
|
|
43
|
-
@click.option(
|
|
44
|
-
@click.option(
|
|
45
|
-
def pcap(
|
|
46
|
-
|
|
47
|
-
|
|
41
|
+
@click.argument("out", type=click.File("wb"), required=False)
|
|
42
|
+
@click.option("-c", "--count", type=click.INT, default=-1, help="Number of packets to sniff. Omit to endless sniff.")
|
|
43
|
+
@click.option("--process", default=None, help="Process to filter. Omit for all.")
|
|
44
|
+
@click.option("-i", "--interface", default=None, help="Interface name to filter. Omit for all.")
|
|
45
|
+
def pcap(
|
|
46
|
+
service_provider: LockdownServiceProvider,
|
|
47
|
+
out: Optional[IO],
|
|
48
|
+
count: int,
|
|
49
|
+
process: Optional[str],
|
|
50
|
+
interface: Optional[str],
|
|
51
|
+
) -> None:
|
|
52
|
+
"""Sniff device traffic"""
|
|
48
53
|
service = PcapdService(lockdown=service_provider)
|
|
49
54
|
packets_generator = service.watch(packets_count=count, process=process, interface_name=interface)
|
|
50
55
|
|
|
51
56
|
if out is not None:
|
|
52
|
-
packets_generator_with_print =
|
|
57
|
+
packets_generator_with_print = (print_packet(p) for p in packets_generator)
|
|
53
58
|
service.write_to_pcap(out, packets_generator_with_print)
|
|
54
59
|
return
|
|
55
60
|
|
|
@@ -12,14 +12,16 @@ def cli() -> None:
|
|
|
12
12
|
pass
|
|
13
13
|
|
|
14
14
|
|
|
15
|
-
@cli.command(
|
|
16
|
-
@click.argument(
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
@click.argument(
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
15
|
+
@cli.command("power-assertion", cls=Command)
|
|
16
|
+
@click.argument(
|
|
17
|
+
"assertion_type",
|
|
18
|
+
type=click.Choice(["AMDPowerAssertionTypeWirelessSync", "PreventUserIdleSystemSleep", "PreventSystemSleep"]),
|
|
19
|
+
)
|
|
20
|
+
@click.argument("name")
|
|
21
|
+
@click.argument("timeout", type=click.INT)
|
|
22
|
+
@click.argument("details", required=False)
|
|
23
|
+
def power_assertion(service_provider: LockdownServiceProvider, assertion_type, name, timeout, details) -> None:
|
|
24
|
+
"""Create a power assertion"""
|
|
25
|
+
with PowerAssertionService(service_provider).create_power_assertion(assertion_type, name, timeout, details):
|
|
26
|
+
print("> Hit Ctrl+C to exit")
|
|
25
27
|
time.sleep(timeout)
|
pymobiledevice3/cli/processes.py
CHANGED
|
@@ -16,22 +16,22 @@ def cli() -> None:
|
|
|
16
16
|
|
|
17
17
|
@cli.group()
|
|
18
18
|
def processes() -> None:
|
|
19
|
-
"""
|
|
19
|
+
"""View process list using diagnosticsd API"""
|
|
20
20
|
pass
|
|
21
21
|
|
|
22
22
|
|
|
23
|
-
@processes.command(
|
|
23
|
+
@processes.command("ps", cls=Command)
|
|
24
24
|
def processes_ps(service_provider: LockdownClient):
|
|
25
|
-
"""
|
|
26
|
-
print_json(OsTraceService(lockdown=service_provider).get_pid_list().get(
|
|
25
|
+
"""show process list"""
|
|
26
|
+
print_json(OsTraceService(lockdown=service_provider).get_pid_list().get("Payload"))
|
|
27
27
|
|
|
28
28
|
|
|
29
|
-
@processes.command(
|
|
30
|
-
@click.argument(
|
|
29
|
+
@processes.command("pgrep", cls=Command)
|
|
30
|
+
@click.argument("expression")
|
|
31
31
|
def processes_pgrep(service_provider: LockdownClient, expression):
|
|
32
|
-
"""
|
|
33
|
-
processes_list = OsTraceService(lockdown=service_provider).get_pid_list().get(
|
|
32
|
+
"""try to match processes pid by given expression (like pgrep)"""
|
|
33
|
+
processes_list = OsTraceService(lockdown=service_provider).get_pid_list().get("Payload")
|
|
34
34
|
for pid, process_info in processes_list.items():
|
|
35
|
-
process_name = process_info.get(
|
|
35
|
+
process_name = process_info.get("ProcessName")
|
|
36
36
|
if expression in process_name:
|
|
37
|
-
logger.info(f
|
|
37
|
+
logger.info(f"{pid} {process_name}")
|
pymobiledevice3/cli/profile.py
CHANGED
|
@@ -21,21 +21,21 @@ def cli() -> None:
|
|
|
21
21
|
pass
|
|
22
22
|
|
|
23
23
|
|
|
24
|
-
@cli.group(
|
|
24
|
+
@cli.group("profile")
|
|
25
25
|
def profile_group() -> None:
|
|
26
|
-
"""
|
|
26
|
+
"""Managed installed profiles or install SSL certificates"""
|
|
27
27
|
pass
|
|
28
28
|
|
|
29
29
|
|
|
30
|
-
@profile_group.command(
|
|
30
|
+
@profile_group.command("list", cls=Command)
|
|
31
31
|
def profile_list(service_provider: LockdownClient):
|
|
32
|
-
"""
|
|
32
|
+
"""List installed profiles"""
|
|
33
33
|
print_json(MobileConfigService(lockdown=service_provider).get_profile_list())
|
|
34
34
|
|
|
35
35
|
|
|
36
|
-
@profile_group.command(
|
|
37
|
-
@click.option(
|
|
38
|
-
@click.argument(
|
|
36
|
+
@profile_group.command("install", cls=Command)
|
|
37
|
+
@click.option("--keybag", type=click.Path(file_okay=True, dir_okay=False, exists=True))
|
|
38
|
+
@click.argument("profiles", nargs=-1, type=click.File("rb"))
|
|
39
39
|
def profile_install(service_provider: LockdownServiceProvider, keybag: Optional[str], profiles: list[IO]) -> None:
|
|
40
40
|
"""
|
|
41
41
|
Install given profiles
|
|
@@ -44,101 +44,108 @@ def profile_install(service_provider: LockdownServiceProvider, keybag: Optional[
|
|
|
44
44
|
"""
|
|
45
45
|
service = MobileConfigService(lockdown=service_provider)
|
|
46
46
|
for profile in profiles:
|
|
47
|
-
logger.info(f
|
|
47
|
+
logger.info(f"installing {profile.name}")
|
|
48
48
|
if keybag is not None:
|
|
49
49
|
service.install_profile_silent(Path(keybag), profile.read())
|
|
50
50
|
else:
|
|
51
51
|
service.install_profile(profile.read())
|
|
52
52
|
|
|
53
53
|
|
|
54
|
-
@profile_group.command(
|
|
55
|
-
@click.argument(
|
|
54
|
+
@profile_group.command("cloud-configuration", cls=Command)
|
|
55
|
+
@click.argument("config", type=click.File("rb"), required=False)
|
|
56
56
|
def profile_cloud_configuration(service_provider: LockdownServiceProvider, config: Optional[IO]) -> None:
|
|
57
|
-
"""
|
|
57
|
+
"""Get/Set cloud configuration"""
|
|
58
58
|
if not config:
|
|
59
59
|
print_json(MobileConfigService(lockdown=service_provider).get_cloud_configuration())
|
|
60
60
|
else:
|
|
61
61
|
config_json = plistlib.load(config)
|
|
62
|
-
logger.info(f
|
|
62
|
+
logger.info(f"applying cloud configuration {config_json}")
|
|
63
63
|
MobileConfigService(lockdown=service_provider).set_cloud_configuration(config_json)
|
|
64
|
-
logger.info(
|
|
64
|
+
logger.info("applied cloud configuration")
|
|
65
65
|
|
|
66
66
|
|
|
67
|
-
@profile_group.command(
|
|
68
|
-
@click.argument(
|
|
67
|
+
@profile_group.command("store", cls=Command)
|
|
68
|
+
@click.argument("profiles", nargs=-1, type=click.File("rb"))
|
|
69
69
|
def profile_store(service_provider: LockdownServiceProvider, profiles: list[IO]) -> None:
|
|
70
|
-
"""
|
|
70
|
+
"""Store a profile"""
|
|
71
71
|
service = MobileConfigService(lockdown=service_provider)
|
|
72
72
|
for profile in profiles:
|
|
73
|
-
logger.info(f
|
|
73
|
+
logger.info(f"storing {profile.name}")
|
|
74
74
|
service.store_profile(profile.read())
|
|
75
75
|
|
|
76
76
|
|
|
77
|
-
@profile_group.command(
|
|
78
|
-
@click.argument(
|
|
77
|
+
@profile_group.command("remove", cls=Command)
|
|
78
|
+
@click.argument("name")
|
|
79
79
|
def profile_remove(service_provider: LockdownServiceProvider, name: str) -> None:
|
|
80
|
-
"""
|
|
80
|
+
"""Remove a profile by its name"""
|
|
81
81
|
MobileConfigService(lockdown=service_provider).remove_profile(name)
|
|
82
82
|
|
|
83
83
|
|
|
84
|
-
@profile_group.command(
|
|
85
|
-
@click.argument(
|
|
84
|
+
@profile_group.command("set-wifi-power", cls=Command)
|
|
85
|
+
@click.argument("state", type=click.Choice(["on", "off"]), required=False)
|
|
86
86
|
def profile_set_wifi_power(service_provider: LockdownServiceProvider, state: str) -> None:
|
|
87
|
-
"""
|
|
88
|
-
MobileConfigService(lockdown=service_provider).set_wifi_power_state(state ==
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
@profile_group.command(
|
|
92
|
-
@click.option(
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
"
|
|
99
|
-
|
|
100
|
-
|
|
87
|
+
"""change Wi-Fi power state"""
|
|
88
|
+
MobileConfigService(lockdown=service_provider).set_wifi_power_state(state == "on")
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
@profile_group.command("erase-device", cls=Command)
|
|
92
|
+
@click.option(
|
|
93
|
+
"--preserve-data-plan/--no-preserve-data-plan", default=True, help="Preserves eSIM / data plan after erase"
|
|
94
|
+
)
|
|
95
|
+
@click.option(
|
|
96
|
+
"--disallow-proximity-setup/--no-disallow-proximity-setup",
|
|
97
|
+
default=False,
|
|
98
|
+
help="Disallows to setup the erased device from nearby devices",
|
|
99
|
+
)
|
|
100
|
+
def profile_erase_device(
|
|
101
|
+
service_provider: LockdownServiceProvider, preserve_data_plan: bool, disallow_proximity_setup: bool
|
|
102
|
+
) -> None:
|
|
103
|
+
"""Erase device"""
|
|
104
|
+
logger.info(
|
|
105
|
+
f"Erasing device with preserve_data_plan: {preserve_data_plan}, "
|
|
106
|
+
f"disallow_proximity_setup: {disallow_proximity_setup}"
|
|
107
|
+
)
|
|
101
108
|
MobileConfigService(lockdown=service_provider).erase_device(preserve_data_plan, disallow_proximity_setup)
|
|
102
|
-
logger.info(
|
|
109
|
+
logger.info("Erased device")
|
|
103
110
|
|
|
104
111
|
|
|
105
|
-
@profile_group.command(
|
|
106
|
-
@click.argument(
|
|
107
|
-
@click.argument(
|
|
112
|
+
@profile_group.command("create-keybag")
|
|
113
|
+
@click.argument("keybag", type=click.Path(file_okay=True, dir_okay=False, exists=False))
|
|
114
|
+
@click.argument("organization")
|
|
108
115
|
def profile_create_keybag(keybag: str, organization: str) -> None:
|
|
109
|
-
"""
|
|
116
|
+
"""Create keybag storing certificate and private key"""
|
|
110
117
|
create_keybag_file(Path(keybag), organization)
|
|
111
118
|
|
|
112
119
|
|
|
113
|
-
@profile_group.command(
|
|
114
|
-
@click.argument(
|
|
115
|
-
@click.option(
|
|
120
|
+
@profile_group.command("supervise", cls=Command)
|
|
121
|
+
@click.argument("organization")
|
|
122
|
+
@click.option("--keybag", type=click.Path(file_okay=True, dir_okay=False, exists=True))
|
|
116
123
|
def profile_supervise(service_provider: LockdownServiceProvider, organization: str, keybag: Optional[str]) -> None:
|
|
117
|
-
"""
|
|
118
|
-
if MobileActivationService(service_provider).state ==
|
|
119
|
-
logger.info(
|
|
124
|
+
"""Supervise device"""
|
|
125
|
+
if MobileActivationService(service_provider).state == "Unactivated":
|
|
126
|
+
logger.info("Activating device")
|
|
120
127
|
MobileActivationService(service_provider).activate()
|
|
121
|
-
logger.info(
|
|
122
|
-
logger.info(
|
|
128
|
+
logger.info("Device has been successfully activated")
|
|
129
|
+
logger.info("Supervising device")
|
|
123
130
|
if keybag is None:
|
|
124
131
|
with tempfile.TemporaryDirectory() as temp_dir:
|
|
125
|
-
keybag = Path(temp_dir) /
|
|
132
|
+
keybag = Path(temp_dir) / "keybag"
|
|
126
133
|
create_keybag_file(keybag, organization)
|
|
127
134
|
MobileConfigService(lockdown=service_provider).supervise(organization, keybag)
|
|
128
135
|
else:
|
|
129
136
|
MobileConfigService(lockdown=service_provider).supervise(organization, Path(keybag))
|
|
130
137
|
|
|
131
|
-
logger.info(
|
|
138
|
+
logger.info("Device has been successfully supervised")
|
|
132
139
|
|
|
133
140
|
|
|
134
|
-
@profile_group.command(
|
|
135
|
-
@click.argument(
|
|
136
|
-
@click.argument(
|
|
137
|
-
@click.argument(
|
|
138
|
-
@click.option(
|
|
139
|
-
def profile_install_wifi_profile(
|
|
140
|
-
|
|
141
|
-
|
|
141
|
+
@profile_group.command("install-wifi-profile", cls=Command)
|
|
142
|
+
@click.argument("encryption_type")
|
|
143
|
+
@click.argument("ssid")
|
|
144
|
+
@click.argument("password")
|
|
145
|
+
@click.option("--keybag", type=click.Path(file_okay=True, dir_okay=False, exists=True))
|
|
146
|
+
def profile_install_wifi_profile(
|
|
147
|
+
service_provider: LockdownServiceProvider, encryption_type: str, ssid: str, password: str, keybag: Optional[str]
|
|
148
|
+
) -> None:
|
|
142
149
|
"""
|
|
143
150
|
Install Wi-Fi profile
|
|
144
151
|
|
|
@@ -147,34 +154,38 @@ def profile_install_wifi_profile(service_provider: LockdownServiceProvider, encr
|
|
|
147
154
|
if keybag is not None:
|
|
148
155
|
keybag = Path(keybag)
|
|
149
156
|
MobileConfigService(lockdown=service_provider).install_wifi_profile(
|
|
150
|
-
encryption_type=encryption_type, ssid=ssid, password=password, keybag_file=keybag
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
@
|
|
155
|
-
@click.argument(
|
|
156
|
-
@click.
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
157
|
+
encryption_type=encryption_type, ssid=ssid, password=password, keybag_file=keybag
|
|
158
|
+
)
|
|
159
|
+
|
|
160
|
+
|
|
161
|
+
@profile_group.command("install-http-proxy", cls=Command)
|
|
162
|
+
@click.argument("server")
|
|
163
|
+
@click.argument("port", type=click.IntRange(1, 65535))
|
|
164
|
+
@click.option("--keybag", type=click.Path(file_okay=True, dir_okay=False, exists=True))
|
|
165
|
+
def profile_install_http_proxy(
|
|
166
|
+
service_provider: LockdownServiceProvider, server: str, port: int, keybag: Optional[str]
|
|
167
|
+
) -> None:
|
|
168
|
+
"""Install HTTP Proxy profile"""
|
|
160
169
|
if keybag is not None:
|
|
161
170
|
keybag = Path(keybag)
|
|
162
171
|
MobileConfigService(lockdown=service_provider).install_http_proxy(server, port, keybag_file=keybag)
|
|
163
172
|
|
|
164
173
|
|
|
165
|
-
@profile_group.command(
|
|
174
|
+
@profile_group.command("remove-http-proxy", cls=Command)
|
|
166
175
|
def profile_remove_http_proxy(service_provider: LockdownServiceProvider) -> None:
|
|
167
|
-
"""
|
|
176
|
+
"""Remove HTTP Proxy profile that was previously installed using pymobiledevice3"""
|
|
168
177
|
MobileConfigService(lockdown=service_provider).remove_http_proxy()
|
|
169
178
|
|
|
170
179
|
|
|
171
|
-
@profile_group.command(
|
|
172
|
-
@click.option(
|
|
173
|
-
@click.option(
|
|
180
|
+
@profile_group.command("install-restrictions-profile", cls=Command)
|
|
181
|
+
@click.option("--keybag", type=click.Path(file_okay=True, dir_okay=False, exists=True))
|
|
182
|
+
@click.option("--enforced-software-update-delay", type=click.IntRange(0, 90), default=0)
|
|
174
183
|
def profile_install_restrictions_profile(
|
|
175
|
-
|
|
176
|
-
|
|
184
|
+
service_provider: LockdownServiceProvider, keybag: Optional[str], enforced_software_update_delay: int
|
|
185
|
+
) -> None:
|
|
186
|
+
"""Install restrictions profile (can be used for delayed OTA)"""
|
|
177
187
|
if keybag is not None:
|
|
178
188
|
keybag = Path(keybag)
|
|
179
189
|
MobileConfigService(lockdown=service_provider).install_restrictions_profile(
|
|
180
|
-
enforced_software_update_delay=enforced_software_update_delay, keybag_file=keybag
|
|
190
|
+
enforced_software_update_delay=enforced_software_update_delay, keybag_file=keybag
|
|
191
|
+
)
|
pymobiledevice3/cli/provision.py
CHANGED
|
@@ -17,42 +17,42 @@ def cli() -> None:
|
|
|
17
17
|
|
|
18
18
|
@cli.group()
|
|
19
19
|
def provision() -> None:
|
|
20
|
-
"""
|
|
20
|
+
"""Manage installed provision profiles"""
|
|
21
21
|
pass
|
|
22
22
|
|
|
23
23
|
|
|
24
|
-
@provision.command(
|
|
25
|
-
@click.argument(
|
|
24
|
+
@provision.command("install", cls=Command)
|
|
25
|
+
@click.argument("profile", type=click.File("rb"))
|
|
26
26
|
def provision_install(service_provider: LockdownClient, profile):
|
|
27
|
-
"""
|
|
27
|
+
"""install a provision profile (.mobileprovision file)"""
|
|
28
28
|
MisagentService(lockdown=service_provider).install(profile)
|
|
29
29
|
|
|
30
30
|
|
|
31
|
-
@provision.command(
|
|
32
|
-
@click.argument(
|
|
31
|
+
@provision.command("remove", cls=Command)
|
|
32
|
+
@click.argument("profile_id")
|
|
33
33
|
def provision_remove(service_provider: LockdownClient, profile_id):
|
|
34
|
-
"""
|
|
34
|
+
"""remove a provision profile"""
|
|
35
35
|
MisagentService(lockdown=service_provider).remove(profile_id)
|
|
36
36
|
|
|
37
37
|
|
|
38
|
-
@provision.command(
|
|
38
|
+
@provision.command("clear", cls=Command)
|
|
39
39
|
def provision_clear(service_provider: LockdownClient):
|
|
40
|
-
"""
|
|
40
|
+
"""remove all provision profiles"""
|
|
41
41
|
for profile in MisagentService(lockdown=service_provider).copy_all():
|
|
42
|
-
MisagentService(lockdown=service_provider).remove(profile.plist[
|
|
42
|
+
MisagentService(lockdown=service_provider).remove(profile.plist["UUID"])
|
|
43
43
|
|
|
44
44
|
|
|
45
|
-
@provision.command(
|
|
45
|
+
@provision.command("list", cls=Command)
|
|
46
46
|
def provision_list(service_provider: LockdownClient):
|
|
47
|
-
"""
|
|
47
|
+
"""list installed provision profiles"""
|
|
48
48
|
print_json([p.plist for p in MisagentService(lockdown=service_provider).copy_all()])
|
|
49
49
|
|
|
50
50
|
|
|
51
|
-
@provision.command(
|
|
52
|
-
@click.argument(
|
|
51
|
+
@provision.command("dump", cls=Command)
|
|
52
|
+
@click.argument("out", type=click.Path(file_okay=False, dir_okay=True, exists=True))
|
|
53
53
|
def provision_dump(service_provider: LockdownClient, out):
|
|
54
|
-
"""
|
|
54
|
+
"""dump installed provision profiles to specified location"""
|
|
55
55
|
for profile in MisagentService(lockdown=service_provider).copy_all():
|
|
56
|
-
filename = f
|
|
57
|
-
logger.info(f
|
|
56
|
+
filename = f"{profile.plist['UUID']}.mobileprovision"
|
|
57
|
+
logger.info(f"downloading {filename}")
|
|
58
58
|
(Path(out) / filename).write_bytes(profile.buf)
|