pymobiledevice3 6.2.0__py3-none-any.whl → 7.0.0__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.
- pymobiledevice3/__main__.py +136 -44
- pymobiledevice3/_version.py +2 -2
- pymobiledevice3/bonjour.py +19 -20
- pymobiledevice3/cli/activation.py +24 -22
- pymobiledevice3/cli/afc.py +49 -41
- pymobiledevice3/cli/amfi.py +13 -18
- pymobiledevice3/cli/apps.py +71 -65
- pymobiledevice3/cli/backup.py +134 -93
- pymobiledevice3/cli/bonjour.py +31 -29
- pymobiledevice3/cli/cli_common.py +179 -232
- pymobiledevice3/cli/companion_proxy.py +12 -12
- pymobiledevice3/cli/crash.py +95 -52
- pymobiledevice3/cli/developer/__init__.py +62 -0
- pymobiledevice3/cli/developer/accessibility/__init__.py +65 -0
- pymobiledevice3/cli/developer/accessibility/settings.py +43 -0
- pymobiledevice3/cli/developer/arbitration.py +50 -0
- pymobiledevice3/cli/developer/condition.py +33 -0
- pymobiledevice3/cli/developer/core_device.py +294 -0
- pymobiledevice3/cli/developer/debugserver.py +244 -0
- pymobiledevice3/cli/developer/dvt/__init__.py +387 -0
- pymobiledevice3/cli/developer/dvt/core_profile_session.py +295 -0
- pymobiledevice3/cli/developer/dvt/simulate_location.py +56 -0
- pymobiledevice3/cli/developer/dvt/sysmon/__init__.py +69 -0
- pymobiledevice3/cli/developer/dvt/sysmon/process.py +188 -0
- pymobiledevice3/cli/developer/fetch_symbols.py +108 -0
- pymobiledevice3/cli/developer/simulate_location.py +51 -0
- pymobiledevice3/cli/diagnostics/__init__.py +75 -0
- pymobiledevice3/cli/diagnostics/battery.py +47 -0
- pymobiledevice3/cli/idam.py +18 -22
- pymobiledevice3/cli/lockdown.py +70 -75
- pymobiledevice3/cli/mounter.py +99 -57
- pymobiledevice3/cli/notification.py +38 -26
- pymobiledevice3/cli/pcap.py +36 -20
- pymobiledevice3/cli/power_assertion.py +15 -16
- pymobiledevice3/cli/processes.py +11 -17
- pymobiledevice3/cli/profile.py +120 -75
- pymobiledevice3/cli/provision.py +27 -26
- pymobiledevice3/cli/remote.py +108 -99
- pymobiledevice3/cli/restore.py +134 -129
- pymobiledevice3/cli/springboard.py +50 -50
- pymobiledevice3/cli/syslog.py +138 -74
- pymobiledevice3/cli/usbmux.py +66 -27
- pymobiledevice3/cli/version.py +2 -5
- pymobiledevice3/cli/webinspector.py +149 -103
- pymobiledevice3/remote/remote_service_discovery.py +11 -10
- pymobiledevice3/restore/device.py +28 -4
- pymobiledevice3/service_connection.py +1 -1
- pymobiledevice3/services/mobilebackup2.py +4 -1
- pymobiledevice3/services/screenshot.py +2 -2
- pymobiledevice3/services/web_protocol/automation_session.py +4 -2
- pymobiledevice3/services/web_protocol/cdp_screencast.py +2 -1
- pymobiledevice3/services/web_protocol/element.py +3 -3
- {pymobiledevice3-6.2.0.dist-info → pymobiledevice3-7.0.0.dist-info}/METADATA +3 -2
- {pymobiledevice3-6.2.0.dist-info → pymobiledevice3-7.0.0.dist-info}/RECORD +58 -45
- pymobiledevice3/cli/completions.py +0 -50
- pymobiledevice3/cli/developer.py +0 -1645
- pymobiledevice3/cli/diagnostics.py +0 -110
- {pymobiledevice3-6.2.0.dist-info → pymobiledevice3-7.0.0.dist-info}/WHEEL +0 -0
- {pymobiledevice3-6.2.0.dist-info → pymobiledevice3-7.0.0.dist-info}/entry_points.txt +0 -0
- {pymobiledevice3-6.2.0.dist-info → pymobiledevice3-7.0.0.dist-info}/licenses/LICENSE +0 -0
- {pymobiledevice3-6.2.0.dist-info → pymobiledevice3-7.0.0.dist-info}/top_level.txt +0 -0
pymobiledevice3/cli/lockdown.py
CHANGED
|
@@ -2,13 +2,13 @@ import asyncio
|
|
|
2
2
|
import logging
|
|
3
3
|
import plistlib
|
|
4
4
|
from pathlib import Path
|
|
5
|
-
from typing import Optional
|
|
5
|
+
from typing import Annotated, Literal, Optional
|
|
6
6
|
|
|
7
|
-
import
|
|
7
|
+
import typer
|
|
8
|
+
from typer_injector import InjectingTyper
|
|
8
9
|
|
|
9
|
-
from pymobiledevice3.cli.cli_common import
|
|
10
|
+
from pymobiledevice3.cli.cli_common import NoAutoPairServiceProviderDep, ServiceProviderDep, print_json, sudo_required
|
|
10
11
|
from pymobiledevice3.cli.remote import tunnel_task
|
|
11
|
-
from pymobiledevice3.lockdown import LockdownClient
|
|
12
12
|
from pymobiledevice3.lockdown_service_provider import LockdownServiceProvider
|
|
13
13
|
from pymobiledevice3.remote.common import TunnelProtocol
|
|
14
14
|
from pymobiledevice3.remote.tunnel_service import CoreDeviceTunnelProxy
|
|
@@ -17,131 +17,123 @@ from pymobiledevice3.services.heartbeat import HeartbeatService
|
|
|
17
17
|
logger = logging.getLogger(__name__)
|
|
18
18
|
|
|
19
19
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
@cli.group("lockdown")
|
|
26
|
-
def lockdown_group() -> None:
|
|
27
|
-
"""Pair/Unpair device or access other lockdown services"""
|
|
28
|
-
pass
|
|
20
|
+
cli = InjectingTyper(
|
|
21
|
+
name="lockdown",
|
|
22
|
+
help="Pair/Unpair device or access other lockdown services",
|
|
23
|
+
no_args_is_help=True,
|
|
24
|
+
)
|
|
29
25
|
|
|
30
26
|
|
|
31
|
-
@
|
|
32
|
-
def lockdown_recovery(service_provider:
|
|
27
|
+
@cli.command("recovery")
|
|
28
|
+
def lockdown_recovery(service_provider: ServiceProviderDep) -> None:
|
|
33
29
|
"""enter recovery"""
|
|
34
30
|
print_json(service_provider.enter_recovery())
|
|
35
31
|
|
|
36
32
|
|
|
37
|
-
@
|
|
38
|
-
|
|
39
|
-
def lockdown_service(service_provider: LockdownServiceProvider, service_name):
|
|
33
|
+
@cli.command("service")
|
|
34
|
+
def lockdown_service(service_provider: ServiceProviderDep, service_name: str) -> None:
|
|
40
35
|
"""send-receive raw service messages with a given service name"""
|
|
41
36
|
service_provider.start_lockdown_service(service_name).shell()
|
|
42
37
|
|
|
43
38
|
|
|
44
|
-
@
|
|
45
|
-
|
|
46
|
-
def lockdown_developer_service(service_provider: LockdownServiceProvider, service_name):
|
|
39
|
+
@cli.command("developer-service")
|
|
40
|
+
def lockdown_developer_service(service_provider: ServiceProviderDep, service_name: str) -> None:
|
|
47
41
|
"""send-receive raw service messages with a given developer service name"""
|
|
48
42
|
service_provider.start_lockdown_developer_service(service_name).shell()
|
|
49
43
|
|
|
50
44
|
|
|
51
|
-
@
|
|
52
|
-
def lockdown_info(service_provider:
|
|
45
|
+
@cli.command("info")
|
|
46
|
+
def lockdown_info(service_provider: ServiceProviderDep) -> None:
|
|
53
47
|
"""query all lockdown values"""
|
|
54
48
|
print_json(service_provider.all_values)
|
|
55
49
|
|
|
56
50
|
|
|
57
|
-
@
|
|
58
|
-
|
|
59
|
-
@click.argument("key", required=False)
|
|
60
|
-
def lockdown_get(service_provider: LockdownClient, domain, key):
|
|
51
|
+
@cli.command("get")
|
|
52
|
+
def lockdown_get(service_provider: ServiceProviderDep, domain: Optional[str] = None, key: Optional[str] = None) -> None:
|
|
61
53
|
"""query lockdown values by their domain and key names"""
|
|
62
54
|
print_json(service_provider.get_value(domain=domain, key=key))
|
|
63
55
|
|
|
64
56
|
|
|
65
|
-
@
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
57
|
+
@cli.command("set")
|
|
58
|
+
def lockdown_set(
|
|
59
|
+
service_provider: ServiceProviderDep,
|
|
60
|
+
value: str,
|
|
61
|
+
domain: Optional[str] = None,
|
|
62
|
+
key: Optional[str] = None,
|
|
63
|
+
) -> None:
|
|
70
64
|
"""set a lockdown value using python's eval()"""
|
|
71
65
|
print_json(service_provider.set_value(value=eval(value), domain=domain, key=key))
|
|
72
66
|
|
|
73
67
|
|
|
74
|
-
@
|
|
75
|
-
|
|
76
|
-
@click.argument("key")
|
|
77
|
-
def lockdown_remove(service_provider: LockdownClient, domain, key):
|
|
68
|
+
@cli.command("remove")
|
|
69
|
+
def lockdown_remove(service_provider: ServiceProviderDep, domain: str, key: str) -> None:
|
|
78
70
|
"""remove a domain/key pair"""
|
|
79
71
|
print_json(service_provider.remove_value(domain=domain, key=key))
|
|
80
72
|
|
|
81
73
|
|
|
82
|
-
@
|
|
83
|
-
|
|
84
|
-
def lockdown_unpair(service_provider: LockdownClient, host_id: Optional[str] = None):
|
|
74
|
+
@cli.command("unpair")
|
|
75
|
+
def lockdown_unpair(service_provider: NoAutoPairServiceProviderDep, host_id: Optional[str] = None) -> None:
|
|
85
76
|
"""unpair from connected device"""
|
|
86
77
|
service_provider.unpair(host_id=host_id)
|
|
87
78
|
|
|
88
79
|
|
|
89
|
-
@
|
|
90
|
-
def lockdown_pair(service_provider:
|
|
80
|
+
@cli.command("pair")
|
|
81
|
+
def lockdown_pair(service_provider: NoAutoPairServiceProviderDep) -> None:
|
|
91
82
|
"""pair device"""
|
|
92
83
|
service_provider.pair()
|
|
93
84
|
|
|
94
85
|
|
|
95
|
-
@
|
|
96
|
-
|
|
97
|
-
|
|
86
|
+
@cli.command("pair-supervised")
|
|
87
|
+
def lockdown_pair_supervised(
|
|
88
|
+
service_provider: NoAutoPairServiceProviderDep,
|
|
89
|
+
keybag: Annotated[
|
|
90
|
+
Path,
|
|
91
|
+
typer.Argument(file_okay=True, dir_okay=False, exists=True),
|
|
92
|
+
],
|
|
93
|
+
) -> None:
|
|
98
94
|
"""pair supervised device"""
|
|
99
|
-
service_provider.pair_supervised(
|
|
95
|
+
service_provider.pair_supervised(keybag)
|
|
100
96
|
|
|
101
97
|
|
|
102
|
-
@
|
|
103
|
-
|
|
104
|
-
def lockdown_save_pair_record(service_provider: LockdownClient, output):
|
|
98
|
+
@cli.command("save-pair-record")
|
|
99
|
+
def lockdown_save_pair_record(service_provider: NoAutoPairServiceProviderDep, output: Path) -> None:
|
|
105
100
|
"""save pair record to specified location"""
|
|
106
101
|
if service_provider.pair_record is None:
|
|
107
102
|
logger.error("no pairing record was found")
|
|
108
103
|
return
|
|
109
|
-
plistlib.
|
|
104
|
+
output.write_bytes(plistlib.dumps(service_provider.pair_record))
|
|
110
105
|
|
|
111
106
|
|
|
112
|
-
@
|
|
113
|
-
def lockdown_date(service_provider:
|
|
107
|
+
@cli.command("date")
|
|
108
|
+
def lockdown_date(service_provider: ServiceProviderDep) -> None:
|
|
114
109
|
"""get device date"""
|
|
115
110
|
print(service_provider.date)
|
|
116
111
|
|
|
117
112
|
|
|
118
|
-
@
|
|
119
|
-
def lockdown_heartbeat(service_provider:
|
|
113
|
+
@cli.command("heartbeat")
|
|
114
|
+
def lockdown_heartbeat(service_provider: ServiceProviderDep) -> None:
|
|
120
115
|
"""start heartbeat service"""
|
|
121
116
|
HeartbeatService(service_provider).start()
|
|
122
117
|
|
|
123
118
|
|
|
124
|
-
@
|
|
125
|
-
|
|
126
|
-
def lockdown_language(service_provider: LockdownClient, language: Optional[str]) -> None:
|
|
119
|
+
@cli.command("language")
|
|
120
|
+
def lockdown_language(service_provider: ServiceProviderDep, language: Optional[str] = None) -> None:
|
|
127
121
|
"""Get/Set current language settings"""
|
|
128
122
|
if language is not None:
|
|
129
123
|
service_provider.set_language(language)
|
|
130
124
|
print_json(service_provider.language)
|
|
131
125
|
|
|
132
126
|
|
|
133
|
-
@
|
|
134
|
-
|
|
135
|
-
def lockdown_locale(service_provider: LockdownClient, locale: Optional[str]) -> None:
|
|
127
|
+
@cli.command("locale")
|
|
128
|
+
def lockdown_locale(service_provider: ServiceProviderDep, locale: Optional[str] = None) -> None:
|
|
136
129
|
"""Get/Set current language settings"""
|
|
137
130
|
if locale is not None:
|
|
138
131
|
service_provider.set_locale(locale)
|
|
139
132
|
print_json(service_provider.locale)
|
|
140
133
|
|
|
141
134
|
|
|
142
|
-
@
|
|
143
|
-
|
|
144
|
-
def lockdown_device_name(service_provider: LockdownClient, new_name):
|
|
135
|
+
@cli.command("device-name")
|
|
136
|
+
def lockdown_device_name(service_provider: ServiceProviderDep, new_name: Optional[str] = None) -> None:
|
|
145
137
|
"""get/set current device name"""
|
|
146
138
|
if new_name:
|
|
147
139
|
service_provider.set_value(new_name, key="DeviceName")
|
|
@@ -149,9 +141,10 @@ def lockdown_device_name(service_provider: LockdownClient, new_name):
|
|
|
149
141
|
print(f"{service_provider.get_value(key='DeviceName')}")
|
|
150
142
|
|
|
151
143
|
|
|
152
|
-
@
|
|
153
|
-
|
|
154
|
-
|
|
144
|
+
@cli.command("wifi-connections")
|
|
145
|
+
def lockdown_wifi_connections(
|
|
146
|
+
service_provider: ServiceProviderDep, state: Optional[Literal["on", "off"]] = None
|
|
147
|
+
) -> None:
|
|
155
148
|
"""get/set wifi connections state"""
|
|
156
149
|
if not state:
|
|
157
150
|
# show current state
|
|
@@ -170,21 +163,23 @@ async def async_cli_start_tunnel(service_provider: LockdownServiceProvider, scri
|
|
|
170
163
|
)
|
|
171
164
|
|
|
172
165
|
|
|
173
|
-
@
|
|
174
|
-
@click.option(
|
|
175
|
-
"--script-mode",
|
|
176
|
-
is_flag=True,
|
|
177
|
-
help="Show only HOST and port number to allow easy parsing from external shell scripts",
|
|
178
|
-
)
|
|
166
|
+
@cli.command("start-tunnel")
|
|
179
167
|
@sudo_required
|
|
180
|
-
def cli_start_tunnel(
|
|
168
|
+
def cli_start_tunnel(
|
|
169
|
+
service_provider: ServiceProviderDep,
|
|
170
|
+
script_mode: Annotated[
|
|
171
|
+
bool,
|
|
172
|
+
typer.Option(help="Show only HOST and port number to allow easy parsing from external shell scripts"),
|
|
173
|
+
] = False,
|
|
174
|
+
) -> None:
|
|
181
175
|
"""start tunnel"""
|
|
182
176
|
asyncio.run(async_cli_start_tunnel(service_provider, script_mode), debug=True)
|
|
183
177
|
|
|
184
178
|
|
|
185
|
-
@
|
|
186
|
-
|
|
187
|
-
|
|
179
|
+
@cli.command("assistive-touch")
|
|
180
|
+
def lockdown_assistive_touch(
|
|
181
|
+
service_provider: ServiceProviderDep, state: Optional[Literal["on", "off"]] = None
|
|
182
|
+
) -> None:
|
|
188
183
|
"""get/set assistive touch icon state (visibility)"""
|
|
189
184
|
if not state:
|
|
190
185
|
key = "AssistiveTouchEnabledByiTunes"
|
pymobiledevice3/cli/mounter.py
CHANGED
|
@@ -2,18 +2,19 @@ import asyncio
|
|
|
2
2
|
import logging
|
|
3
3
|
from functools import update_wrapper
|
|
4
4
|
from pathlib import Path
|
|
5
|
+
from typing import Annotated, Optional
|
|
5
6
|
from urllib.error import URLError
|
|
6
7
|
|
|
7
|
-
import
|
|
8
|
+
import typer
|
|
9
|
+
from typer_injector import InjectingTyper
|
|
8
10
|
|
|
9
|
-
from pymobiledevice3.cli.cli_common import
|
|
11
|
+
from pymobiledevice3.cli.cli_common import ServiceProviderDep, print_json
|
|
10
12
|
from pymobiledevice3.exceptions import (
|
|
11
13
|
AlreadyMountedError,
|
|
12
14
|
DeveloperDiskImageNotFoundError,
|
|
13
15
|
NotMountedError,
|
|
14
16
|
UnsupportedCommandError,
|
|
15
17
|
)
|
|
16
|
-
from pymobiledevice3.lockdown import LockdownClient
|
|
17
18
|
from pymobiledevice3.lockdown_service_provider import LockdownServiceProvider
|
|
18
19
|
from pymobiledevice3.services.mobile_image_mounter import (
|
|
19
20
|
DeveloperDiskImageMounter,
|
|
@@ -37,19 +38,15 @@ def catch_errors(func):
|
|
|
37
38
|
return update_wrapper(catch_function, func)
|
|
38
39
|
|
|
39
40
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
@cli.group()
|
|
46
|
-
def mounter() -> None:
|
|
47
|
-
"""Mount/Umount DeveloperDiskImage or query related info"""
|
|
48
|
-
pass
|
|
41
|
+
cli = InjectingTyper(
|
|
42
|
+
name="mounter",
|
|
43
|
+
help="Mount/Umount DeveloperDiskImage or query related info",
|
|
44
|
+
no_args_is_help=True,
|
|
45
|
+
)
|
|
49
46
|
|
|
50
47
|
|
|
51
|
-
@
|
|
52
|
-
def mounter_list(service_provider:
|
|
48
|
+
@cli.command("list")
|
|
49
|
+
def mounter_list(service_provider: ServiceProviderDep) -> None:
|
|
53
50
|
"""list all mounted images"""
|
|
54
51
|
output = []
|
|
55
52
|
|
|
@@ -63,9 +60,8 @@ def mounter_list(service_provider: LockdownClient):
|
|
|
63
60
|
print_json(output)
|
|
64
61
|
|
|
65
62
|
|
|
66
|
-
@
|
|
67
|
-
|
|
68
|
-
def mounter_lookup(service_provider: LockdownClient, image_type):
|
|
63
|
+
@cli.command("lookup")
|
|
64
|
+
def mounter_lookup(service_provider: ServiceProviderDep, image_type: str) -> None:
|
|
69
65
|
"""lookup mounter image type"""
|
|
70
66
|
try:
|
|
71
67
|
signature = MobileImageMounterService(lockdown=service_provider).lookup_image(image_type)
|
|
@@ -74,9 +70,9 @@ def mounter_lookup(service_provider: LockdownClient, image_type):
|
|
|
74
70
|
logger.error(f"Disk image of type: {image_type} is not mounted")
|
|
75
71
|
|
|
76
72
|
|
|
77
|
-
@
|
|
73
|
+
@cli.command("umount-developer")
|
|
78
74
|
@catch_errors
|
|
79
|
-
def mounter_umount_developer(service_provider:
|
|
75
|
+
def mounter_umount_developer(service_provider: ServiceProviderDep) -> None:
|
|
80
76
|
"""unmount Developer image"""
|
|
81
77
|
try:
|
|
82
78
|
DeveloperDiskImageMounter(lockdown=service_provider).umount()
|
|
@@ -85,9 +81,9 @@ def mounter_umount_developer(service_provider: LockdownClient):
|
|
|
85
81
|
logger.error("Developer image isn't currently mounted")
|
|
86
82
|
|
|
87
83
|
|
|
88
|
-
@
|
|
84
|
+
@cli.command("umount-personalized")
|
|
89
85
|
@catch_errors
|
|
90
|
-
def mounter_umount_personalized(service_provider:
|
|
86
|
+
def mounter_umount_personalized(service_provider: ServiceProviderDep) -> None:
|
|
91
87
|
"""unmount Personalized image"""
|
|
92
88
|
try:
|
|
93
89
|
PersonalizedImageMounter(lockdown=service_provider).umount()
|
|
@@ -96,13 +92,29 @@ def mounter_umount_personalized(service_provider: LockdownClient):
|
|
|
96
92
|
logger.error("Personalized image isn't currently mounted")
|
|
97
93
|
|
|
98
94
|
|
|
99
|
-
@
|
|
100
|
-
@click.argument("image", type=click.Path(exists=True, file_okay=True, dir_okay=False))
|
|
101
|
-
@click.argument("signature", type=click.Path(exists=True, file_okay=True, dir_okay=False))
|
|
95
|
+
@cli.command("mount-developer")
|
|
102
96
|
@catch_errors
|
|
103
|
-
def mounter_mount_developer(
|
|
97
|
+
def mounter_mount_developer(
|
|
98
|
+
service_provider: ServiceProviderDep,
|
|
99
|
+
image: Annotated[
|
|
100
|
+
Path,
|
|
101
|
+
typer.Argument(
|
|
102
|
+
exists=True,
|
|
103
|
+
file_okay=True,
|
|
104
|
+
dir_okay=False,
|
|
105
|
+
),
|
|
106
|
+
],
|
|
107
|
+
signature: Annotated[
|
|
108
|
+
Path,
|
|
109
|
+
typer.Argument(
|
|
110
|
+
exists=True,
|
|
111
|
+
file_okay=True,
|
|
112
|
+
dir_okay=False,
|
|
113
|
+
),
|
|
114
|
+
],
|
|
115
|
+
) -> None:
|
|
104
116
|
"""mount developer image"""
|
|
105
|
-
DeveloperDiskImageMounter(lockdown=service_provider).mount(
|
|
117
|
+
DeveloperDiskImageMounter(lockdown=service_provider).mount(image, signature)
|
|
106
118
|
logger.info("Developer image mounted successfully")
|
|
107
119
|
|
|
108
120
|
|
|
@@ -115,16 +127,39 @@ async def mounter_mount_personalized_task(
|
|
|
115
127
|
logger.info("Personalized image mounted successfully")
|
|
116
128
|
|
|
117
129
|
|
|
118
|
-
@
|
|
119
|
-
@click.argument("image", type=click.Path(exists=True, file_okay=True, dir_okay=False))
|
|
120
|
-
@click.argument("trust-cache", type=click.Path(exists=True, file_okay=True, dir_okay=False))
|
|
121
|
-
@click.argument("build-manifest", type=click.Path(exists=True, file_okay=True, dir_okay=False))
|
|
130
|
+
@cli.command("mount-personalized")
|
|
122
131
|
@catch_errors
|
|
123
132
|
def mounter_mount_personalized(
|
|
124
|
-
service_provider:
|
|
133
|
+
service_provider: ServiceProviderDep,
|
|
134
|
+
image: Annotated[
|
|
135
|
+
Path,
|
|
136
|
+
typer.Argument(
|
|
137
|
+
exists=True,
|
|
138
|
+
file_okay=True,
|
|
139
|
+
dir_okay=False,
|
|
140
|
+
),
|
|
141
|
+
],
|
|
142
|
+
trust_cache: Annotated[
|
|
143
|
+
Path,
|
|
144
|
+
typer.Argument(
|
|
145
|
+
exists=True,
|
|
146
|
+
file_okay=True,
|
|
147
|
+
dir_okay=False,
|
|
148
|
+
),
|
|
149
|
+
],
|
|
150
|
+
build_manifest: Annotated[
|
|
151
|
+
Path,
|
|
152
|
+
typer.Argument(
|
|
153
|
+
exists=True,
|
|
154
|
+
file_okay=True,
|
|
155
|
+
dir_okay=False,
|
|
156
|
+
),
|
|
157
|
+
],
|
|
125
158
|
) -> None:
|
|
126
159
|
"""mount personalized image"""
|
|
127
|
-
asyncio.run(
|
|
160
|
+
asyncio.run(
|
|
161
|
+
mounter_mount_personalized_task(service_provider, str(image), str(trust_cache), str(build_manifest)), debug=True
|
|
162
|
+
)
|
|
128
163
|
|
|
129
164
|
|
|
130
165
|
async def mounter_auto_mount_task(service_provider: LockdownServiceProvider, xcode: str, version: str) -> None:
|
|
@@ -144,42 +179,49 @@ async def mounter_auto_mount_task(service_provider: LockdownServiceProvider, xco
|
|
|
144
179
|
)
|
|
145
180
|
|
|
146
181
|
|
|
147
|
-
@
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
182
|
+
@cli.command("auto-mount")
|
|
183
|
+
def mounter_auto_mount(
|
|
184
|
+
service_provider: ServiceProviderDep,
|
|
185
|
+
xcode: Annotated[
|
|
186
|
+
Optional[Path],
|
|
187
|
+
typer.Option(
|
|
188
|
+
"--xcode",
|
|
189
|
+
"-x",
|
|
190
|
+
exists=True,
|
|
191
|
+
file_okay=True,
|
|
192
|
+
dir_okay=False,
|
|
193
|
+
help="Xcode application path used to figure out automatically the DeveloperDiskImage path",
|
|
194
|
+
),
|
|
195
|
+
] = None,
|
|
196
|
+
version: Annotated[
|
|
197
|
+
Optional[str],
|
|
198
|
+
typer.Option(help="Use a different DeveloperDiskImage version from the one retrieved by lockdownconnection"),
|
|
199
|
+
] = None,
|
|
200
|
+
) -> None:
|
|
158
201
|
"""auto-detect correct DeveloperDiskImage and mount it"""
|
|
159
|
-
asyncio.run(mounter_auto_mount_task(service_provider, xcode, version), debug=True)
|
|
202
|
+
asyncio.run(mounter_auto_mount_task(service_provider, str(xcode), version), debug=True)
|
|
160
203
|
|
|
161
204
|
|
|
162
|
-
@
|
|
163
|
-
def mounter_query_developer_mode_status(service_provider:
|
|
205
|
+
@cli.command("query-developer-mode-status")
|
|
206
|
+
def mounter_query_developer_mode_status(service_provider: ServiceProviderDep) -> None:
|
|
164
207
|
"""Query developer mode status"""
|
|
165
208
|
print_json(MobileImageMounterService(lockdown=service_provider).query_developer_mode_status())
|
|
166
209
|
|
|
167
210
|
|
|
168
|
-
@
|
|
169
|
-
|
|
170
|
-
def mounter_query_nonce(service_provider: LockdownClient, image_type: str):
|
|
211
|
+
@cli.command("query-nonce")
|
|
212
|
+
def mounter_query_nonce(service_provider: ServiceProviderDep, image_type: Annotated[str, typer.Option()]) -> None:
|
|
171
213
|
"""Query nonce"""
|
|
172
214
|
print_json(MobileImageMounterService(lockdown=service_provider).query_nonce(image_type))
|
|
173
215
|
|
|
174
216
|
|
|
175
|
-
@
|
|
176
|
-
def mounter_query_personalization_identifiers(service_provider:
|
|
217
|
+
@cli.command("query-personalization-identifiers")
|
|
218
|
+
def mounter_query_personalization_identifiers(service_provider: ServiceProviderDep) -> None:
|
|
177
219
|
"""Query personalization identifiers"""
|
|
178
220
|
print_json(MobileImageMounterService(lockdown=service_provider).query_personalization_identifiers())
|
|
179
221
|
|
|
180
222
|
|
|
181
|
-
@
|
|
182
|
-
def mounter_query_personalization_manifest(service_provider:
|
|
223
|
+
@cli.command("query-personalization-manifest")
|
|
224
|
+
def mounter_query_personalization_manifest(service_provider: ServiceProviderDep) -> None:
|
|
183
225
|
"""Query personalization manifest"""
|
|
184
226
|
result = []
|
|
185
227
|
mounter = MobileImageMounterService(lockdown=service_provider)
|
|
@@ -188,12 +230,12 @@ def mounter_query_personalization_manifest(service_provider: LockdownClient):
|
|
|
188
230
|
print_json(result)
|
|
189
231
|
|
|
190
232
|
|
|
191
|
-
@
|
|
192
|
-
def mounter_roll_personalization_nonce(service_provider:
|
|
233
|
+
@cli.command("roll-personalization-nonce")
|
|
234
|
+
def mounter_roll_personalization_nonce(service_provider: ServiceProviderDep) -> None:
|
|
193
235
|
MobileImageMounterService(lockdown=service_provider).roll_personalization_nonce()
|
|
194
236
|
|
|
195
237
|
|
|
196
|
-
@
|
|
197
|
-
def mounter_roll_cryptex_nonce(service_provider:
|
|
238
|
+
@cli.command("roll-cryptex-nonce")
|
|
239
|
+
def mounter_roll_cryptex_nonce(service_provider: ServiceProviderDep) -> None:
|
|
198
240
|
"""Roll cryptex nonce (will reboot)"""
|
|
199
241
|
MobileImageMounterService(lockdown=service_provider).roll_cryptex_nonce()
|
|
@@ -1,41 +1,48 @@
|
|
|
1
1
|
import logging
|
|
2
|
+
from typing import Annotated
|
|
2
3
|
|
|
3
|
-
import
|
|
4
|
+
import typer
|
|
5
|
+
from typer_injector import InjectingTyper
|
|
4
6
|
|
|
5
|
-
from pymobiledevice3.cli.cli_common import
|
|
6
|
-
from pymobiledevice3.lockdown import LockdownClient
|
|
7
|
+
from pymobiledevice3.cli.cli_common import ServiceProviderDep
|
|
7
8
|
from pymobiledevice3.resources.firmware_notifications import get_notifications
|
|
8
9
|
from pymobiledevice3.services.notification_proxy import NotificationProxyService
|
|
9
10
|
|
|
10
11
|
logger = logging.getLogger(__name__)
|
|
11
12
|
|
|
12
13
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
14
|
+
cli = InjectingTyper(
|
|
15
|
+
name="notifications",
|
|
16
|
+
help="Post or observe Darwin notifications via notification_proxy.",
|
|
17
|
+
no_args_is_help=True,
|
|
18
|
+
)
|
|
16
19
|
|
|
17
20
|
|
|
18
|
-
@cli.
|
|
19
|
-
def
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
"""API for notify_post()."""
|
|
21
|
+
@cli.command()
|
|
22
|
+
def post(
|
|
23
|
+
service_provider: ServiceProviderDep,
|
|
24
|
+
names: list[str],
|
|
25
|
+
insecure: Annotated[
|
|
26
|
+
bool,
|
|
27
|
+
typer.Option(help="Use the insecure relay meant for untrusted clients instead of the trusted channel."),
|
|
28
|
+
],
|
|
29
|
+
) -> None:
|
|
30
|
+
"""Post one or more Darwin notifications (notify_post)."""
|
|
29
31
|
service = NotificationProxyService(lockdown=service_provider, insecure=insecure)
|
|
30
32
|
for name in names:
|
|
31
33
|
service.notify_post(name)
|
|
32
34
|
|
|
33
35
|
|
|
34
|
-
@
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
36
|
+
@cli.command()
|
|
37
|
+
def observe(
|
|
38
|
+
service_provider: ServiceProviderDep,
|
|
39
|
+
names: list[str],
|
|
40
|
+
insecure: Annotated[
|
|
41
|
+
bool,
|
|
42
|
+
typer.Option(help="Use the insecure relay meant for untrusted clients instead of the trusted channel."),
|
|
43
|
+
],
|
|
44
|
+
) -> None:
|
|
45
|
+
"""Subscribe and stream notifications (notify_register_dispatch)."""
|
|
39
46
|
service = NotificationProxyService(lockdown=service_provider, insecure=insecure)
|
|
40
47
|
for name in names:
|
|
41
48
|
service.notify_register_dispatch(name)
|
|
@@ -44,10 +51,15 @@ def observe(service_provider: LockdownClient, names, insecure):
|
|
|
44
51
|
logger.info(event)
|
|
45
52
|
|
|
46
53
|
|
|
47
|
-
@
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
54
|
+
@cli.command("observe-all")
|
|
55
|
+
def observe_all(
|
|
56
|
+
service_provider: ServiceProviderDep,
|
|
57
|
+
insecure: Annotated[
|
|
58
|
+
bool,
|
|
59
|
+
typer.Option(help="Use the insecure relay meant for untrusted clients instead of the trusted channel."),
|
|
60
|
+
],
|
|
61
|
+
) -> None:
|
|
62
|
+
"""Subscribe to all known firmware notifications and stream events."""
|
|
51
63
|
service = NotificationProxyService(lockdown=service_provider, insecure=insecure)
|
|
52
64
|
for notification in get_notifications():
|
|
53
65
|
service.notify_register_dispatch(notification)
|