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
|
@@ -14,16 +14,16 @@ from pymobiledevice3.lockdown_service_provider import LockdownServiceProvider
|
|
|
14
14
|
from pymobiledevice3.services.afc import AfcService
|
|
15
15
|
from pymobiledevice3.services.lockdown_service import LockdownService
|
|
16
16
|
|
|
17
|
-
GET_APPS_ADDITIONAL_INFO = {
|
|
17
|
+
GET_APPS_ADDITIONAL_INFO = {"ReturnAttributes": ["CFBundleIdentifier", "StaticDiskUsage", "DynamicDiskUsage"]}
|
|
18
18
|
|
|
19
|
-
TEMP_REMOTE_BASEDIR =
|
|
20
|
-
TEMP_REMOTE_IPA_FILE = f
|
|
21
|
-
TEMP_REMOTE_IPCC_FOLDER = f
|
|
19
|
+
TEMP_REMOTE_BASEDIR = "/PublicStaging"
|
|
20
|
+
TEMP_REMOTE_IPA_FILE = f"{TEMP_REMOTE_BASEDIR}/pymobiledevice3.ipa"
|
|
21
|
+
TEMP_REMOTE_IPCC_FOLDER = f"{TEMP_REMOTE_BASEDIR}/pymobiledevice3.ipcc"
|
|
22
22
|
|
|
23
23
|
|
|
24
24
|
class ZipFileType(Enum):
|
|
25
|
-
IPCC =
|
|
26
|
-
IPA =
|
|
25
|
+
IPCC = "ipcc"
|
|
26
|
+
IPA = "ipa"
|
|
27
27
|
|
|
28
28
|
def is_ipcc(self) -> bool:
|
|
29
29
|
return self == ZipFileType.IPCC
|
|
@@ -33,42 +33,41 @@ class ZipFileType(Enum):
|
|
|
33
33
|
|
|
34
34
|
|
|
35
35
|
def create_ipa_contents_from_directory(directory: str) -> bytes:
|
|
36
|
-
payload_prefix =
|
|
36
|
+
payload_prefix = "Payload/" + os.path.basename(directory)
|
|
37
37
|
with TemporaryDirectory() as temp_dir:
|
|
38
|
-
zip_path = Path(temp_dir) /
|
|
39
|
-
with ZipFile(zip_path,
|
|
40
|
-
for root,
|
|
38
|
+
zip_path = Path(temp_dir) / "ipa"
|
|
39
|
+
with ZipFile(zip_path, "w", ZIP_DEFLATED) as zip_file:
|
|
40
|
+
for root, _dirs, files in os.walk(directory):
|
|
41
41
|
for file in files:
|
|
42
42
|
full_path = Path(root) / file
|
|
43
43
|
full_path.touch()
|
|
44
|
-
zip_file.write(full_path,
|
|
45
|
-
arcname=f'{payload_prefix}/{os.path.relpath(full_path, directory)}')
|
|
44
|
+
zip_file.write(full_path, arcname=f"{payload_prefix}/{os.path.relpath(full_path, directory)}")
|
|
46
45
|
return zip_path.read_bytes()
|
|
47
46
|
|
|
48
47
|
|
|
49
48
|
def classify_zip_file(zip_bytes: bytes) -> ZipFileType:
|
|
50
|
-
"""
|
|
49
|
+
"""checks the zipped bytes if it's a .ipcc or .ipa"""
|
|
51
50
|
try:
|
|
52
|
-
with ZipFile(BytesIO(zip_bytes),
|
|
51
|
+
with ZipFile(BytesIO(zip_bytes), "r") as zip_file:
|
|
53
52
|
# sometimes packages at first index don't have enough infos to check
|
|
54
|
-
dirs = zip_file.namelist()[1].split(
|
|
53
|
+
dirs = zip_file.namelist()[1].split("/")
|
|
55
54
|
|
|
56
|
-
if dirs[0] !=
|
|
57
|
-
raise AppInstallError(
|
|
58
|
-
if dirs[1].endswith(
|
|
55
|
+
if dirs[0] != "Payload":
|
|
56
|
+
raise AppInstallError("package does not have a payload")
|
|
57
|
+
if dirs[1].endswith(".app"):
|
|
59
58
|
return ZipFileType.IPA
|
|
60
|
-
elif dirs[1].endswith(
|
|
59
|
+
elif dirs[1].endswith(".bundle"):
|
|
61
60
|
return ZipFileType.IPCC
|
|
62
61
|
else:
|
|
63
|
-
raise AppInstallError(
|
|
62
|
+
raise AppInstallError("package does not have the appropriate folders structure")
|
|
64
63
|
|
|
65
|
-
except BadZipFile:
|
|
66
|
-
raise AppInstallError(
|
|
64
|
+
except BadZipFile as e:
|
|
65
|
+
raise AppInstallError("Invalid bytes package") from e
|
|
67
66
|
|
|
68
67
|
|
|
69
68
|
class InstallationProxyService(LockdownService):
|
|
70
|
-
SERVICE_NAME =
|
|
71
|
-
RSD_SERVICE_NAME =
|
|
69
|
+
SERVICE_NAME = "com.apple.mobile.installation_proxy"
|
|
70
|
+
RSD_SERVICE_NAME = "com.apple.mobile.installation_proxy.shim.remote"
|
|
72
71
|
|
|
73
72
|
def __init__(self, lockdown: LockdownServiceProvider):
|
|
74
73
|
if isinstance(lockdown, LockdownClient):
|
|
@@ -76,69 +75,84 @@ class InstallationProxyService(LockdownService):
|
|
|
76
75
|
else:
|
|
77
76
|
super().__init__(lockdown, self.RSD_SERVICE_NAME)
|
|
78
77
|
|
|
79
|
-
def _watch_completion(self, handler: Callable = None, ipcc: bool = False, *args) -> None:
|
|
78
|
+
def _watch_completion(self, handler: Optional[Callable] = None, ipcc: bool = False, *args) -> None:
|
|
80
79
|
while True:
|
|
81
80
|
response = self.service.recv_plist()
|
|
82
81
|
if not response:
|
|
83
82
|
break
|
|
84
|
-
error = response.get(
|
|
83
|
+
error = response.get("Error")
|
|
85
84
|
if error:
|
|
86
|
-
raise AppInstallError(f
|
|
87
|
-
completion = response.get(
|
|
85
|
+
raise AppInstallError(f"{error}: {response.get('ErrorDescription')}")
|
|
86
|
+
completion = response.get("PercentComplete")
|
|
88
87
|
if completion:
|
|
89
88
|
if handler:
|
|
90
|
-
self.logger.debug(
|
|
89
|
+
self.logger.debug("calling handler")
|
|
91
90
|
handler(completion, *args)
|
|
92
|
-
self.logger.info(f
|
|
93
|
-
if response.get(
|
|
91
|
+
self.logger.info(f"{response.get('PercentComplete')}% Complete")
|
|
92
|
+
if response.get("Status") == "Complete":
|
|
94
93
|
if ipcc:
|
|
95
94
|
# there is no progress when installing a .ipcc file,
|
|
96
95
|
# so we just put a simple message indicating it's done
|
|
97
|
-
self.logger.info(
|
|
96
|
+
self.logger.info("Installation succeed.")
|
|
98
97
|
return
|
|
99
98
|
raise AppInstallError()
|
|
100
99
|
|
|
101
|
-
def send_cmd_for_bundle_identifier(
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
100
|
+
def send_cmd_for_bundle_identifier(
|
|
101
|
+
self,
|
|
102
|
+
bundle_identifier: str,
|
|
103
|
+
cmd: str = "Archive",
|
|
104
|
+
options: Optional[dict] = None,
|
|
105
|
+
handler: Optional[dict] = None,
|
|
106
|
+
*args,
|
|
107
|
+
) -> None:
|
|
108
|
+
"""send a low-level command to installation relay"""
|
|
109
|
+
cmd = {"Command": cmd, "ApplicationIdentifier": bundle_identifier}
|
|
107
110
|
|
|
108
111
|
if options is None:
|
|
109
112
|
options = {}
|
|
110
113
|
|
|
111
|
-
cmd.update({
|
|
114
|
+
cmd.update({"ClientOptions": options})
|
|
112
115
|
self.service.send_plist(cmd)
|
|
113
116
|
self._watch_completion(handler, *args)
|
|
114
117
|
|
|
115
|
-
def install(
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
""
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
"""
|
|
118
|
+
def install(
|
|
119
|
+
self, package_path: str, options: Optional[dict] = None, handler: Optional[Callable] = None, *args
|
|
120
|
+
) -> None:
|
|
121
|
+
"""install given ipa/ipcc from device path"""
|
|
122
|
+
self.install_from_local(package_path, "Install", options, handler, args)
|
|
123
|
+
|
|
124
|
+
def upgrade(self, ipa_path: str, options: Optional[dict] = None, handler: Optional[Callable] = None, *args) -> None:
|
|
125
|
+
"""upgrade given ipa from device path"""
|
|
126
|
+
self.install_from_local(ipa_path, "Upgrade", options, handler, args)
|
|
127
|
+
|
|
128
|
+
def restore(
|
|
129
|
+
self, bundle_identifier: str, options: Optional[dict] = None, handler: Optional[Callable] = None, *args
|
|
130
|
+
) -> None:
|
|
131
|
+
"""no longer supported on newer iOS versions"""
|
|
132
|
+
self.send_cmd_for_bundle_identifier(bundle_identifier, "Restore", options, handler, args)
|
|
133
|
+
|
|
134
|
+
def uninstall(
|
|
135
|
+
self, bundle_identifier: str, options: Optional[dict] = None, handler: Optional[Callable] = None, *args
|
|
136
|
+
) -> None:
|
|
137
|
+
"""uninstall given bundle_identifier"""
|
|
138
|
+
self.send_cmd_for_bundle_identifier(bundle_identifier, "Uninstall", options, handler, args)
|
|
139
|
+
|
|
140
|
+
def install_from_bytes(
|
|
141
|
+
self,
|
|
142
|
+
package_bytes: bytes,
|
|
143
|
+
cmd: str = "Install",
|
|
144
|
+
options: Optional[dict] = None,
|
|
145
|
+
handler: Optional[Callable] = None,
|
|
146
|
+
*args,
|
|
147
|
+
) -> None:
|
|
148
|
+
"""upload given ipa/ipcc bytes object onto device and install it"""
|
|
135
149
|
ipcc_mode = classify_zip_file(package_bytes).is_ipcc()
|
|
136
150
|
|
|
137
151
|
if options is None:
|
|
138
152
|
options = {}
|
|
139
153
|
|
|
140
154
|
if ipcc_mode:
|
|
141
|
-
options[
|
|
155
|
+
options["PackageType"] = "CarrierBundle"
|
|
142
156
|
|
|
143
157
|
with AfcService(self.lockdown) as afc:
|
|
144
158
|
if not ipcc_mode:
|
|
@@ -148,17 +162,24 @@ class InstallationProxyService(LockdownService):
|
|
|
148
162
|
|
|
149
163
|
self.send_package(cmd, options, handler, ipcc_mode, *args)
|
|
150
164
|
|
|
151
|
-
@str_to_path(
|
|
152
|
-
def install_from_local(
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
165
|
+
@str_to_path("package_path")
|
|
166
|
+
def install_from_local(
|
|
167
|
+
self,
|
|
168
|
+
package_path: Path,
|
|
169
|
+
cmd: str = "Install",
|
|
170
|
+
options: Optional[dict] = None,
|
|
171
|
+
handler: Optional[Callable] = None,
|
|
172
|
+
developer: bool = False,
|
|
173
|
+
*args,
|
|
174
|
+
) -> None:
|
|
175
|
+
"""upload given ipa/ipcc onto device and install it"""
|
|
176
|
+
ipcc_mode = package_path.suffix == ".ipcc"
|
|
156
177
|
|
|
157
178
|
if options is None:
|
|
158
179
|
options = {}
|
|
159
180
|
|
|
160
181
|
if ipcc_mode:
|
|
161
|
-
options[
|
|
182
|
+
options["PackageType"] = "CarrierBundle"
|
|
162
183
|
else:
|
|
163
184
|
if package_path.is_dir():
|
|
164
185
|
# treat as app, convert into an ipa
|
|
@@ -168,7 +189,7 @@ class InstallationProxyService(LockdownService):
|
|
|
168
189
|
ipa_contents = package_path.read_bytes()
|
|
169
190
|
|
|
170
191
|
if developer:
|
|
171
|
-
options[
|
|
192
|
+
options["PackageType"] = "Developer"
|
|
172
193
|
|
|
173
194
|
with AfcService(self.lockdown) as afc:
|
|
174
195
|
if not ipcc_mode:
|
|
@@ -182,19 +203,16 @@ class InstallationProxyService(LockdownService):
|
|
|
182
203
|
|
|
183
204
|
def send_package(self, cmd: str, options: Optional[dict], handler: Callable, ipcc_mode: bool = False, *args):
|
|
184
205
|
self.service.send_plist({
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
TEMP_REMOTE_IPCC_FOLDER if ipcc_mode
|
|
189
|
-
else TEMP_REMOTE_IPA_FILE
|
|
190
|
-
)
|
|
206
|
+
"Command": cmd,
|
|
207
|
+
"ClientOptions": options,
|
|
208
|
+
"PackagePath": (TEMP_REMOTE_IPCC_FOLDER if ipcc_mode else TEMP_REMOTE_IPA_FILE),
|
|
191
209
|
})
|
|
192
210
|
|
|
193
211
|
self._watch_completion(handler, ipcc_mode, args)
|
|
194
212
|
|
|
195
213
|
def upload_ipcc_from_path(self, file: Path, afc_client: AfcService) -> None:
|
|
196
214
|
"""Used to upload a .ipcc file to an iPhone as a folder"""
|
|
197
|
-
with file.open(
|
|
215
|
+
with file.open("rb") as fb:
|
|
198
216
|
file_name = file.name
|
|
199
217
|
file_stream = BytesIO(fb.read())
|
|
200
218
|
self._upload_ipcc(file_stream, afc_client, file_name)
|
|
@@ -206,44 +224,41 @@ class InstallationProxyService(LockdownService):
|
|
|
206
224
|
self._upload_ipcc(file_stream, afc_client, file_name)
|
|
207
225
|
|
|
208
226
|
def _upload_ipcc(self, file_stream: BytesIO, afc_client: AfcService, file_name: str) -> None:
|
|
209
|
-
self.logger.info(f
|
|
227
|
+
self.logger.info(f"Uploading {file_name} contents..")
|
|
210
228
|
|
|
211
229
|
afc_client.makedirs(TEMP_REMOTE_IPCC_FOLDER)
|
|
212
230
|
|
|
213
231
|
# we unpack it and upload it directly instead of saving it in a temp folder
|
|
214
|
-
with ZipFile(file_stream,
|
|
232
|
+
with ZipFile(file_stream, "r") as file_zip:
|
|
215
233
|
for file_name in file_zip.namelist():
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
afc_client.makedirs(f'{TEMP_REMOTE_IPCC_FOLDER}/{file_name}')
|
|
234
|
+
if file_name.endswith(("/", "\\")):
|
|
235
|
+
afc_client.makedirs(f"{TEMP_REMOTE_IPCC_FOLDER}/{file_name}")
|
|
219
236
|
continue
|
|
220
237
|
|
|
221
238
|
with file_zip.open(file_name) as inside_file_zip:
|
|
222
239
|
file_data = inside_file_zip.read()
|
|
223
240
|
afc_client.makedirs(TEMP_REMOTE_BASEDIR)
|
|
224
|
-
afc_client.set_file_contents(f
|
|
241
|
+
afc_client.set_file_contents(f"{TEMP_REMOTE_IPCC_FOLDER}/{file_name}", file_data)
|
|
225
242
|
|
|
226
|
-
self.logger.info(
|
|
243
|
+
self.logger.info("Upload complete.")
|
|
227
244
|
|
|
228
245
|
def check_capabilities_match(self, capabilities: Optional[dict] = None, options: Optional[dict] = None) -> dict:
|
|
229
246
|
if options is None:
|
|
230
247
|
options = {}
|
|
231
|
-
cmd = {
|
|
232
|
-
'ClientOptions': options}
|
|
248
|
+
cmd = {"Command": "CheckCapabilitiesMatch", "ClientOptions": options}
|
|
233
249
|
|
|
234
250
|
if capabilities:
|
|
235
|
-
cmd[
|
|
251
|
+
cmd["Capabilities"] = capabilities
|
|
236
252
|
|
|
237
|
-
return self.service.send_recv_plist(cmd).get(
|
|
253
|
+
return self.service.send_recv_plist(cmd).get("LookupResult")
|
|
238
254
|
|
|
239
|
-
def browse(self, options: Optional[dict] = None, attributes: list[str] = None) -> list[dict]:
|
|
255
|
+
def browse(self, options: Optional[dict] = None, attributes: Optional[list[str]] = None) -> list[dict]:
|
|
240
256
|
if options is None:
|
|
241
257
|
options = {}
|
|
242
258
|
if attributes:
|
|
243
|
-
options[
|
|
259
|
+
options["ReturnAttributes"] = attributes
|
|
244
260
|
|
|
245
|
-
cmd = {
|
|
246
|
-
'ClientOptions': options}
|
|
261
|
+
cmd = {"Command": "Browse", "ClientOptions": options}
|
|
247
262
|
|
|
248
263
|
self.service.send_plist(cmd)
|
|
249
264
|
|
|
@@ -253,30 +268,34 @@ class InstallationProxyService(LockdownService):
|
|
|
253
268
|
if not response:
|
|
254
269
|
break
|
|
255
270
|
|
|
256
|
-
data = response.get(
|
|
271
|
+
data = response.get("CurrentList")
|
|
257
272
|
if data is not None:
|
|
258
273
|
result += data
|
|
259
274
|
|
|
260
|
-
if response.get(
|
|
275
|
+
if response.get("Status") == "Complete":
|
|
261
276
|
break
|
|
262
277
|
|
|
263
278
|
return result
|
|
264
279
|
|
|
265
280
|
def lookup(self, options: Optional[dict] = None) -> dict:
|
|
266
|
-
"""
|
|
281
|
+
"""search installation database"""
|
|
267
282
|
if options is None:
|
|
268
283
|
options = {}
|
|
269
|
-
cmd = {
|
|
270
|
-
return self.service.send_recv_plist(cmd).get(
|
|
271
|
-
|
|
272
|
-
def get_apps(
|
|
273
|
-
|
|
274
|
-
|
|
284
|
+
cmd = {"Command": "Lookup", "ClientOptions": options}
|
|
285
|
+
return self.service.send_recv_plist(cmd).get("LookupResult")
|
|
286
|
+
|
|
287
|
+
def get_apps(
|
|
288
|
+
self,
|
|
289
|
+
application_type: str = "Any",
|
|
290
|
+
calculate_sizes: bool = False,
|
|
291
|
+
bundle_identifiers: Optional[list[str]] = None,
|
|
292
|
+
) -> dict[str, dict]:
|
|
293
|
+
"""get applications according to given criteria"""
|
|
275
294
|
options = {}
|
|
276
295
|
if bundle_identifiers is not None:
|
|
277
|
-
options[
|
|
296
|
+
options["BundleIDs"] = bundle_identifiers
|
|
278
297
|
|
|
279
|
-
options[
|
|
298
|
+
options["ApplicationType"] = application_type
|
|
280
299
|
result = self.lookup(options)
|
|
281
300
|
if calculate_sizes:
|
|
282
301
|
options.update(GET_APPS_ADDITIONAL_INFO)
|
|
@@ -5,8 +5,14 @@ from pymobiledevice3.service_connection import ServiceConnection
|
|
|
5
5
|
|
|
6
6
|
|
|
7
7
|
class LockdownService:
|
|
8
|
-
def __init__(
|
|
9
|
-
|
|
8
|
+
def __init__(
|
|
9
|
+
self,
|
|
10
|
+
lockdown: LockdownServiceProvider,
|
|
11
|
+
service_name: str,
|
|
12
|
+
is_developer_service=False,
|
|
13
|
+
service: ServiceConnection = None,
|
|
14
|
+
include_escrow_bag: bool = False,
|
|
15
|
+
):
|
|
10
16
|
"""
|
|
11
17
|
:param lockdown: server provider
|
|
12
18
|
:param service_name: wrapped service name - will attempt
|
|
@@ -15,8 +21,9 @@ class LockdownService:
|
|
|
15
21
|
"""
|
|
16
22
|
|
|
17
23
|
if service is None:
|
|
18
|
-
start_service =
|
|
19
|
-
lockdown.start_lockdown_service
|
|
24
|
+
start_service = (
|
|
25
|
+
lockdown.start_lockdown_developer_service if is_developer_service else lockdown.start_lockdown_service
|
|
26
|
+
)
|
|
20
27
|
service = start_service(service_name, include_escrow_bag=include_escrow_bag)
|
|
21
28
|
|
|
22
29
|
self.service_name = service_name
|
|
@@ -27,7 +34,7 @@ class LockdownService:
|
|
|
27
34
|
def __enter__(self):
|
|
28
35
|
return self
|
|
29
36
|
|
|
30
|
-
async def __aenter__(self) ->
|
|
37
|
+
async def __aenter__(self) -> "LockdownService":
|
|
31
38
|
return self
|
|
32
39
|
|
|
33
40
|
def __exit__(self, exc_type, exc_val, exc_tb):
|
|
@@ -10,8 +10,8 @@ class ProvisioningProfile:
|
|
|
10
10
|
def __init__(self, buf: bytes):
|
|
11
11
|
self.buf = buf
|
|
12
12
|
|
|
13
|
-
xml = b
|
|
14
|
-
xml = xml.split(b
|
|
13
|
+
xml = b"<?xml" + buf.split(b"<?xml", 1)[1]
|
|
14
|
+
xml = xml.split(b"</plist>")[0] + b"</plist>"
|
|
15
15
|
self.plist = plistlib.loads(xml)
|
|
16
16
|
|
|
17
17
|
def __str__(self):
|
|
@@ -19,8 +19,8 @@ class ProvisioningProfile:
|
|
|
19
19
|
|
|
20
20
|
|
|
21
21
|
class MisagentService(LockdownService):
|
|
22
|
-
SERVICE_NAME =
|
|
23
|
-
RSD_SERVICE_NAME =
|
|
22
|
+
SERVICE_NAME = "com.apple.misagent"
|
|
23
|
+
RSD_SERVICE_NAME = "com.apple.misagent.shim.remote"
|
|
24
24
|
|
|
25
25
|
def __init__(self, lockdown: LockdownClient):
|
|
26
26
|
if isinstance(lockdown, LockdownClient):
|
|
@@ -29,27 +29,30 @@ class MisagentService(LockdownService):
|
|
|
29
29
|
super().__init__(lockdown, self.RSD_SERVICE_NAME)
|
|
30
30
|
|
|
31
31
|
def install(self, plist: BytesIO) -> dict:
|
|
32
|
-
response = self.service.send_recv_plist({
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
32
|
+
response = self.service.send_recv_plist({
|
|
33
|
+
"MessageType": "Install",
|
|
34
|
+
"Profile": plist.read(),
|
|
35
|
+
"ProfileType": "Provisioning",
|
|
36
|
+
})
|
|
37
|
+
if response["Status"]:
|
|
38
|
+
raise PyMobileDevice3Exception(f"invalid status: {response}")
|
|
37
39
|
|
|
38
40
|
return response
|
|
39
41
|
|
|
40
42
|
def remove(self, profile_id: str) -> dict:
|
|
41
|
-
response = self.service.send_recv_plist({
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
43
|
+
response = self.service.send_recv_plist({
|
|
44
|
+
"MessageType": "Remove",
|
|
45
|
+
"ProfileID": profile_id,
|
|
46
|
+
"ProfileType": "Provisioning",
|
|
47
|
+
})
|
|
48
|
+
if response["Status"]:
|
|
49
|
+
raise PyMobileDevice3Exception(f"invalid status: {response}")
|
|
46
50
|
|
|
47
51
|
return response
|
|
48
52
|
|
|
49
53
|
def copy_all(self) -> list[ProvisioningProfile]:
|
|
50
|
-
response = self.service.send_recv_plist({
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
raise PyMobileDevice3Exception(f'invalid status: {response}')
|
|
54
|
+
response = self.service.send_recv_plist({"MessageType": "CopyAll", "ProfileType": "Provisioning"})
|
|
55
|
+
if response["Status"]:
|
|
56
|
+
raise PyMobileDevice3Exception(f"invalid status: {response}")
|
|
54
57
|
|
|
55
|
-
return [ProvisioningProfile(p) for p in response[
|
|
58
|
+
return [ProvisioningProfile(p) for p in response["Payload"]]
|