pymobiledevice3 4.27.4__py3-none-any.whl → 5.1.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.
- misc/plist_sniffer.py +15 -15
- misc/remotexpc_sniffer.py +29 -28
- pymobiledevice3/__main__.py +123 -98
- pymobiledevice3/_version.py +2 -2
- pymobiledevice3/bonjour.py +351 -117
- 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 +27 -20
- 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 +601 -519
- pymobiledevice3/cli/diagnostics.py +38 -33
- pymobiledevice3/cli/lockdown.py +82 -72
- pymobiledevice3/cli/mounter.py +84 -67
- 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 +188 -111
- 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 +156 -78
- pymobiledevice3/common.py +1 -1
- pymobiledevice3/exceptions.py +154 -60
- pymobiledevice3/irecv.py +49 -53
- pymobiledevice3/irecv_devices.py +1489 -492
- pymobiledevice3/lockdown.py +400 -251
- 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 +64 -42
- pymobiledevice3/remote/tunnel_service.py +383 -297
- pymobiledevice3/remote/utils.py +14 -13
- 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 +587 -0
- pymobiledevice3/restore/recovery.py +125 -135
- pymobiledevice3/restore/restore.py +535 -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 +137 -127
- pymobiledevice3/services/afc.py +352 -292
- pymobiledevice3/services/amfi.py +21 -18
- pymobiledevice3/services/companion.py +23 -19
- pymobiledevice3/services/crash_reports.py +61 -47
- pymobiledevice3/services/debugserver_applist.py +3 -3
- pymobiledevice3/services/device_arbitration.py +8 -8
- pymobiledevice3/services/device_link.py +56 -48
- 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 +42 -52
- 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 +331 -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 +128 -74
- 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 +183 -179
- 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 +3 -3
- pymobiledevice3/services/web_protocol/session_protocol.py +15 -10
- pymobiledevice3/services/web_protocol/switch_to.py +11 -12
- pymobiledevice3/services/webinspector.py +142 -116
- pymobiledevice3/tcp_forwarder.py +35 -22
- pymobiledevice3/tunneld/api.py +20 -15
- pymobiledevice3/tunneld/server.py +310 -193
- pymobiledevice3/usbmux.py +197 -148
- pymobiledevice3/utils.py +14 -11
- {pymobiledevice3-4.27.4.dist-info → pymobiledevice3-5.1.2.dist-info}/METADATA +1 -2
- pymobiledevice3-5.1.2.dist-info/RECORD +173 -0
- pymobiledevice3-4.27.4.dist-info/RECORD +0 -172
- {pymobiledevice3-4.27.4.dist-info → pymobiledevice3-5.1.2.dist-info}/WHEEL +0 -0
- {pymobiledevice3-4.27.4.dist-info → pymobiledevice3-5.1.2.dist-info}/entry_points.txt +0 -0
- {pymobiledevice3-4.27.4.dist-info → pymobiledevice3-5.1.2.dist-info}/licenses/LICENSE +0 -0
- {pymobiledevice3-4.27.4.dist-info → pymobiledevice3-5.1.2.dist-info}/top_level.txt +0 -0
|
@@ -6,8 +6,14 @@ from contextlib import contextmanager, suppress
|
|
|
6
6
|
from datetime import datetime
|
|
7
7
|
from pathlib import Path
|
|
8
8
|
|
|
9
|
-
from pymobiledevice3.exceptions import
|
|
10
|
-
|
|
9
|
+
from pymobiledevice3.exceptions import (
|
|
10
|
+
AfcException,
|
|
11
|
+
AfcFileNotFoundError,
|
|
12
|
+
ConnectionTerminatedError,
|
|
13
|
+
LockdownError,
|
|
14
|
+
MissingValueError,
|
|
15
|
+
PyMobileDevice3Exception,
|
|
16
|
+
)
|
|
11
17
|
from pymobiledevice3.lockdown import LockdownClient
|
|
12
18
|
from pymobiledevice3.lockdown_service_provider import LockdownServiceProvider
|
|
13
19
|
from pymobiledevice3.services.afc import AFC_LOCK_EX, AFC_LOCK_UN, AfcService, afc_error_t
|
|
@@ -19,19 +25,27 @@ from pymobiledevice3.services.springboard import SpringBoardServicesService
|
|
|
19
25
|
|
|
20
26
|
SUPPORTED_VERSIONS = [2.0, 2.1]
|
|
21
27
|
ITUNES_FILES = [
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
28
|
+
"ApertureAlbumPrefs",
|
|
29
|
+
"IC-Info.sidb",
|
|
30
|
+
"IC-Info.sidv",
|
|
31
|
+
"PhotosFolderAlbums",
|
|
32
|
+
"PhotosFolderName",
|
|
33
|
+
"PhotosFolderPrefs",
|
|
34
|
+
"VoiceMemos.plist",
|
|
35
|
+
"iPhotoAlbumPrefs",
|
|
36
|
+
"iTunesApplicationIDs",
|
|
37
|
+
"iTunesPrefs",
|
|
38
|
+
"iTunesPrefs.plist",
|
|
25
39
|
]
|
|
26
|
-
NP_SYNC_WILL_START =
|
|
27
|
-
NP_SYNC_DID_START =
|
|
28
|
-
NP_SYNC_LOCK_REQUEST =
|
|
29
|
-
NP_SYNC_DID_FINISH =
|
|
40
|
+
NP_SYNC_WILL_START = "com.apple.itunes-mobdev.syncWillStart"
|
|
41
|
+
NP_SYNC_DID_START = "com.apple.itunes-mobdev.syncDidStart"
|
|
42
|
+
NP_SYNC_LOCK_REQUEST = "com.apple.itunes-mobdev.syncLockRequest"
|
|
43
|
+
NP_SYNC_DID_FINISH = "com.apple.itunes-mobdev.syncDidFinish"
|
|
30
44
|
|
|
31
45
|
|
|
32
46
|
class Mobilebackup2Service(LockdownService):
|
|
33
|
-
SERVICE_NAME =
|
|
34
|
-
RSD_SERVICE_NAME =
|
|
47
|
+
SERVICE_NAME = "com.apple.mobilebackup2"
|
|
48
|
+
RSD_SERVICE_NAME = "com.apple.mobilebackup2.shim.remote"
|
|
35
49
|
|
|
36
50
|
def __init__(self, lockdown: LockdownServiceProvider) -> None:
|
|
37
51
|
if isinstance(lockdown, LockdownClient):
|
|
@@ -42,11 +56,11 @@ class Mobilebackup2Service(LockdownService):
|
|
|
42
56
|
@property
|
|
43
57
|
def will_encrypt(self) -> bool:
|
|
44
58
|
try:
|
|
45
|
-
return self.lockdown.get_value(
|
|
59
|
+
return self.lockdown.get_value("com.apple.mobile.backup", "WillEncrypt")
|
|
46
60
|
except LockdownError:
|
|
47
61
|
return False
|
|
48
62
|
|
|
49
|
-
def backup(self, full: bool = True, backup_directory: str =
|
|
63
|
+
def backup(self, full: bool = True, backup_directory: str = ".", progress_callback=lambda x: None) -> None:
|
|
50
64
|
"""
|
|
51
65
|
Backup a device.
|
|
52
66
|
:param full: Whether to do a full backup. If full is True, any previous backup attempts will be discarded.
|
|
@@ -58,42 +72,58 @@ class Mobilebackup2Service(LockdownService):
|
|
|
58
72
|
device_directory = backup_directory / self.lockdown.udid
|
|
59
73
|
device_directory.mkdir(exist_ok=True, mode=0o755, parents=True)
|
|
60
74
|
|
|
61
|
-
with
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
75
|
+
with (
|
|
76
|
+
self.device_link(backup_directory) as dl,
|
|
77
|
+
NotificationProxyService(self.lockdown) as notification_proxy,
|
|
78
|
+
AfcService(self.lockdown) as afc,
|
|
79
|
+
self._backup_lock(afc, notification_proxy),
|
|
80
|
+
):
|
|
81
|
+
# Initialize Info.plist
|
|
82
|
+
info_plist = self.init_mobile_backup_factory_info(afc)
|
|
83
|
+
with open(device_directory / "Info.plist", "wb") as fd:
|
|
84
|
+
plistlib.dump(info_plist, fd)
|
|
69
85
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
86
|
+
# Initialize Status.plist file if doesn't exist.
|
|
87
|
+
status_path = device_directory / "Status.plist"
|
|
88
|
+
current_date = datetime.now()
|
|
89
|
+
current_date = current_date.replace(tzinfo=None)
|
|
90
|
+
if full or not status_path.exists():
|
|
91
|
+
with open(device_directory / "Status.plist", "wb") as fd:
|
|
92
|
+
plistlib.dump(
|
|
93
|
+
{
|
|
94
|
+
"BackupState": "new",
|
|
95
|
+
"Date": current_date,
|
|
96
|
+
"IsFullBackup": full,
|
|
97
|
+
"Version": "3.3",
|
|
98
|
+
"SnapshotState": "finished",
|
|
99
|
+
"UUID": str(uuid.uuid4()).upper(),
|
|
100
|
+
},
|
|
101
|
+
fd,
|
|
102
|
+
fmt=plistlib.FMT_BINARY,
|
|
103
|
+
)
|
|
84
104
|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
105
|
+
# Create Manifest.plist if doesn't exist.
|
|
106
|
+
manifest_path = device_directory / "Manifest.plist"
|
|
107
|
+
if full:
|
|
108
|
+
manifest_path.unlink(missing_ok=True)
|
|
109
|
+
(device_directory / "Manifest.plist").touch()
|
|
90
110
|
|
|
91
|
-
|
|
92
|
-
|
|
111
|
+
dl.send_process_message({"MessageName": "Backup", "TargetIdentifier": self.lockdown.udid})
|
|
112
|
+
dl.dl_loop(progress_callback)
|
|
93
113
|
|
|
94
|
-
def restore(
|
|
95
|
-
|
|
96
|
-
|
|
114
|
+
def restore(
|
|
115
|
+
self,
|
|
116
|
+
backup_directory=".",
|
|
117
|
+
system: bool = False,
|
|
118
|
+
reboot: bool = True,
|
|
119
|
+
copy: bool = True,
|
|
120
|
+
settings: bool = True,
|
|
121
|
+
remove: bool = False,
|
|
122
|
+
password: str = "",
|
|
123
|
+
source: str = "",
|
|
124
|
+
progress_callback=lambda x: None,
|
|
125
|
+
skip_apps: bool = False,
|
|
126
|
+
):
|
|
97
127
|
"""
|
|
98
128
|
Restore a previous backup to the device.
|
|
99
129
|
:param backup_directory: Path of the backup directory.
|
|
@@ -112,46 +142,48 @@ class Mobilebackup2Service(LockdownService):
|
|
|
112
142
|
source = source if source else self.lockdown.udid
|
|
113
143
|
self._assert_backup_exists(backup_directory, source)
|
|
114
144
|
|
|
115
|
-
with
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
145
|
+
with (
|
|
146
|
+
self.device_link(backup_directory) as dl,
|
|
147
|
+
NotificationProxyService(self.lockdown) as notification_proxy,
|
|
148
|
+
AfcService(self.lockdown) as afc,
|
|
149
|
+
self._backup_lock(afc, notification_proxy),
|
|
150
|
+
):
|
|
151
|
+
manifest_plist_path = backup_directory / source / "Manifest.plist"
|
|
152
|
+
with open(manifest_plist_path, "rb") as fd:
|
|
153
|
+
manifest = plistlib.load(fd)
|
|
154
|
+
is_encrypted = manifest.get("IsEncrypted", False)
|
|
155
|
+
options = {
|
|
156
|
+
"RestoreShouldReboot": reboot,
|
|
157
|
+
"RestoreDontCopyBackup": not copy,
|
|
158
|
+
"RestorePreserveSettings": settings,
|
|
159
|
+
"RestoreSystemFiles": system,
|
|
160
|
+
"RemoveItemsNotRestored": remove,
|
|
161
|
+
}
|
|
162
|
+
if is_encrypted:
|
|
163
|
+
if password:
|
|
164
|
+
options["Password"] = password
|
|
165
|
+
else:
|
|
166
|
+
self.logger.error("Backup is encrypted, please supply password.")
|
|
167
|
+
return
|
|
168
|
+
dl.send_process_message({
|
|
169
|
+
"MessageName": "Restore",
|
|
170
|
+
"TargetIdentifier": self.lockdown.udid,
|
|
171
|
+
"SourceIdentifier": source,
|
|
172
|
+
"Options": options,
|
|
173
|
+
})
|
|
142
174
|
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
175
|
+
if not skip_apps:
|
|
176
|
+
# Write /iTunesRestore/RestoreApplications.plist so that the device will start
|
|
177
|
+
# restoring applications once the rest of the restore process is finished
|
|
178
|
+
info_plist_path = backup_directory / source / "Info.plist"
|
|
179
|
+
applications = plistlib.loads(info_plist_path.read_bytes()).get("Applications")
|
|
180
|
+
if applications is not None:
|
|
181
|
+
afc.makedirs("/iTunesRestore")
|
|
182
|
+
afc.set_file_contents("/iTunesRestore/RestoreApplications.plist", plistlib.dumps(applications))
|
|
151
183
|
|
|
152
|
-
|
|
184
|
+
dl.dl_loop(progress_callback)
|
|
153
185
|
|
|
154
|
-
def info(self, backup_directory=
|
|
186
|
+
def info(self, backup_directory=".", source: str = "") -> str:
|
|
155
187
|
"""
|
|
156
188
|
Get information about a backup.
|
|
157
189
|
:param backup_directory: Path of the backup directory.
|
|
@@ -161,14 +193,14 @@ class Mobilebackup2Service(LockdownService):
|
|
|
161
193
|
backup_dir = Path(backup_directory)
|
|
162
194
|
self._assert_backup_exists(backup_dir, source if source else self.lockdown.udid)
|
|
163
195
|
with self.device_link(backup_dir) as dl:
|
|
164
|
-
message = {
|
|
196
|
+
message = {"MessageName": "Info", "TargetIdentifier": self.lockdown.udid}
|
|
165
197
|
if source:
|
|
166
|
-
message[
|
|
198
|
+
message["SourceIdentifier"] = source
|
|
167
199
|
dl.send_process_message(message)
|
|
168
200
|
result = dl.dl_loop()
|
|
169
201
|
return result
|
|
170
202
|
|
|
171
|
-
def list(self, backup_directory=
|
|
203
|
+
def list(self, backup_directory=".", source: str = "") -> str:
|
|
172
204
|
"""
|
|
173
205
|
List the files in the last backup.
|
|
174
206
|
:param backup_directory: Path of the backup directory.
|
|
@@ -180,12 +212,14 @@ class Mobilebackup2Service(LockdownService):
|
|
|
180
212
|
self._assert_backup_exists(backup_dir, source)
|
|
181
213
|
with self.device_link(backup_dir) as dl:
|
|
182
214
|
dl.send_process_message({
|
|
183
|
-
|
|
215
|
+
"MessageName": "List",
|
|
216
|
+
"TargetIdentifier": self.lockdown.udid,
|
|
217
|
+
"SourceIdentifier": source,
|
|
184
218
|
})
|
|
185
219
|
result = dl.dl_loop()
|
|
186
220
|
return result
|
|
187
221
|
|
|
188
|
-
def unback(self, backup_directory=
|
|
222
|
+
def unback(self, backup_directory=".", password: str = "", source: str = "") -> None:
|
|
189
223
|
"""
|
|
190
224
|
Unpack a complete backup to its device hierarchy.
|
|
191
225
|
:param backup_directory: Path of the backup directory.
|
|
@@ -195,16 +229,17 @@ class Mobilebackup2Service(LockdownService):
|
|
|
195
229
|
backup_dir = Path(backup_directory)
|
|
196
230
|
self._assert_backup_exists(backup_dir, source if source else self.lockdown.udid)
|
|
197
231
|
with self.device_link(backup_dir) as dl:
|
|
198
|
-
message = {
|
|
232
|
+
message = {"MessageName": "Unback", "TargetIdentifier": self.lockdown.udid}
|
|
199
233
|
if source:
|
|
200
|
-
message[
|
|
234
|
+
message["SourceIdentifier"] = source
|
|
201
235
|
if password:
|
|
202
|
-
message[
|
|
236
|
+
message["Password"] = password
|
|
203
237
|
dl.send_process_message(message)
|
|
204
238
|
dl.dl_loop()
|
|
205
239
|
|
|
206
|
-
def extract(
|
|
207
|
-
|
|
240
|
+
def extract(
|
|
241
|
+
self, domain_name: str, relative_path: str, backup_directory=".", password: str = "", source: str = ""
|
|
242
|
+
) -> None:
|
|
208
243
|
"""
|
|
209
244
|
Extract a file from a previous backup.
|
|
210
245
|
:param domain_name: File's domain name, e.g., SystemPreferencesDomain or HomeDomain.
|
|
@@ -217,17 +252,19 @@ class Mobilebackup2Service(LockdownService):
|
|
|
217
252
|
self._assert_backup_exists(backup_dir, source if source else self.lockdown.udid)
|
|
218
253
|
with self.device_link(backup_dir) as dl:
|
|
219
254
|
message = {
|
|
220
|
-
|
|
221
|
-
|
|
255
|
+
"MessageName": "Extract",
|
|
256
|
+
"TargetIdentifier": self.lockdown.udid,
|
|
257
|
+
"DomainName": domain_name,
|
|
258
|
+
"RelativePath": relative_path,
|
|
222
259
|
}
|
|
223
260
|
if source:
|
|
224
|
-
message[
|
|
261
|
+
message["SourceIdentifier"] = source
|
|
225
262
|
if password:
|
|
226
|
-
message[
|
|
263
|
+
message["Password"] = password
|
|
227
264
|
dl.send_process_message(message)
|
|
228
265
|
dl.dl_loop()
|
|
229
266
|
|
|
230
|
-
def change_password(self, backup_directory=
|
|
267
|
+
def change_password(self, backup_directory=".", old: str = "", new: str = "") -> None:
|
|
231
268
|
"""
|
|
232
269
|
Change backup password.
|
|
233
270
|
:param backup_directory: Backups directory.
|
|
@@ -235,22 +272,21 @@ class Mobilebackup2Service(LockdownService):
|
|
|
235
272
|
:param new: New password. Omit when disabling backup encryption.
|
|
236
273
|
"""
|
|
237
274
|
with self.device_link(Path(backup_directory)) as dl:
|
|
238
|
-
message = {
|
|
275
|
+
message = {"MessageName": "ChangePassword", "TargetIdentifier": self.lockdown.udid}
|
|
239
276
|
if old:
|
|
240
|
-
message[
|
|
277
|
+
message["OldPassword"] = old
|
|
241
278
|
if new:
|
|
242
|
-
message[
|
|
279
|
+
message["NewPassword"] = new
|
|
243
280
|
dl.send_process_message(message)
|
|
244
281
|
dl.dl_loop()
|
|
245
282
|
|
|
246
|
-
def erase_device(self, backup_directory=
|
|
283
|
+
def erase_device(self, backup_directory=".") -> None:
|
|
247
284
|
"""
|
|
248
285
|
Erase the device.
|
|
249
286
|
"""
|
|
250
|
-
with suppress(ConnectionTerminatedError):
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
dl.dl_loop()
|
|
287
|
+
with suppress(ConnectionTerminatedError), self.device_link(Path(backup_directory)) as dl:
|
|
288
|
+
dl.send_process_message({"MessageName": "EraseDevice", "TargetIdentifier": self.lockdown.udid})
|
|
289
|
+
dl.dl_loop()
|
|
254
290
|
|
|
255
291
|
def version_exchange(self, dl: DeviceLink, local_versions=None) -> None:
|
|
256
292
|
"""
|
|
@@ -261,87 +297,89 @@ class Mobilebackup2Service(LockdownService):
|
|
|
261
297
|
if local_versions is None:
|
|
262
298
|
local_versions = SUPPORTED_VERSIONS
|
|
263
299
|
dl.send_process_message({
|
|
264
|
-
|
|
265
|
-
|
|
300
|
+
"MessageName": "Hello",
|
|
301
|
+
"SupportedProtocolVersions": local_versions,
|
|
266
302
|
})
|
|
267
303
|
reply = dl.receive_message()
|
|
268
|
-
assert reply[0] ==
|
|
269
|
-
assert reply[1][
|
|
304
|
+
assert reply[0] == "DLMessageProcessMessage" and reply[1]["ErrorCode"] == 0
|
|
305
|
+
assert reply[1]["ProtocolVersion"] in local_versions
|
|
270
306
|
|
|
271
307
|
def init_mobile_backup_factory_info(self, afc: AfcService):
|
|
272
308
|
with InstallationProxyService(self.lockdown) as ip, SpringBoardServicesService(self.lockdown) as sbs:
|
|
273
309
|
root_node = self.lockdown.get_value()
|
|
274
|
-
itunes_settings = self.lockdown.get_value(domain=
|
|
310
|
+
itunes_settings = self.lockdown.get_value(domain="com.apple.iTunes")
|
|
275
311
|
try:
|
|
276
|
-
min_itunes_version = self.lockdown.get_value(
|
|
312
|
+
min_itunes_version = self.lockdown.get_value("com.apple.mobile.iTunes", "MinITunesVersion")
|
|
277
313
|
except MissingValueError:
|
|
278
314
|
# iPadOS may not contain this value. See:
|
|
279
315
|
# https://github.com/doronz88/pymobiledevice3/issues/1332
|
|
280
|
-
min_itunes_version =
|
|
316
|
+
min_itunes_version = "10.0.1"
|
|
281
317
|
app_dict = {}
|
|
282
318
|
installed_apps = []
|
|
283
|
-
apps = ip.browse(
|
|
284
|
-
|
|
319
|
+
apps = ip.browse(
|
|
320
|
+
options={"ApplicationType": "User"},
|
|
321
|
+
attributes=["CFBundleIdentifier", "ApplicationSINF", "iTunesMetadata"],
|
|
322
|
+
)
|
|
285
323
|
for app in apps:
|
|
286
|
-
bundle_id = app[
|
|
324
|
+
bundle_id = app["CFBundleIdentifier"]
|
|
287
325
|
if bundle_id:
|
|
288
326
|
installed_apps.append(bundle_id)
|
|
289
|
-
if app.get(
|
|
327
|
+
if app.get("iTunesMetadata", False) and app.get("ApplicationSINF", False):
|
|
290
328
|
app_dict[bundle_id] = {
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
329
|
+
"ApplicationSINF": app["ApplicationSINF"],
|
|
330
|
+
"iTunesMetadata": app["iTunesMetadata"],
|
|
331
|
+
"PlaceholderIcon": sbs.get_icon_pngdata(bundle_id),
|
|
294
332
|
}
|
|
295
333
|
|
|
296
334
|
files = {}
|
|
297
335
|
for file in ITUNES_FILES:
|
|
298
336
|
try:
|
|
299
|
-
data_buf = afc.get_file_contents(
|
|
337
|
+
data_buf = afc.get_file_contents("/iTunes_Control/iTunes/" + file)
|
|
300
338
|
except AfcFileNotFoundError:
|
|
301
339
|
pass
|
|
302
340
|
else:
|
|
303
341
|
files[file] = data_buf
|
|
304
342
|
|
|
305
343
|
ret = {
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
344
|
+
"iTunes Version": min_itunes_version if min_itunes_version else "10.0.1",
|
|
345
|
+
"iTunes Files": files,
|
|
346
|
+
"Unique Identifier": self.lockdown.udid.upper(),
|
|
347
|
+
"Target Type": "Device",
|
|
348
|
+
"Target Identifier": root_node["UniqueDeviceID"],
|
|
349
|
+
"Serial Number": root_node["SerialNumber"],
|
|
350
|
+
"Product Version": root_node["ProductVersion"],
|
|
351
|
+
"Product Type": root_node["ProductType"],
|
|
352
|
+
"Installed Applications": installed_apps,
|
|
353
|
+
"GUID": uuid.uuid4().bytes,
|
|
354
|
+
"Display Name": root_node["DeviceName"],
|
|
355
|
+
"Device Name": root_node["DeviceName"],
|
|
356
|
+
"Build Version": root_node["BuildVersion"],
|
|
357
|
+
"Applications": app_dict,
|
|
320
358
|
}
|
|
321
359
|
|
|
322
|
-
if
|
|
323
|
-
ret[
|
|
324
|
-
if
|
|
325
|
-
ret[
|
|
326
|
-
if
|
|
327
|
-
ret[
|
|
328
|
-
if
|
|
329
|
-
ret[
|
|
360
|
+
if "IntegratedCircuitCardIdentity" in root_node:
|
|
361
|
+
ret["ICCID"] = root_node["IntegratedCircuitCardIdentity"]
|
|
362
|
+
if "InternationalMobileEquipmentIdentity" in root_node:
|
|
363
|
+
ret["IMEI"] = root_node["InternationalMobileEquipmentIdentity"]
|
|
364
|
+
if "MobileEquipmentIdentifier" in root_node:
|
|
365
|
+
ret["MEID"] = root_node["MobileEquipmentIdentifier"]
|
|
366
|
+
if "PhoneNumber" in root_node:
|
|
367
|
+
ret["Phone Number"] = root_node["PhoneNumber"]
|
|
330
368
|
|
|
331
369
|
try:
|
|
332
|
-
data_buf = afc.get_file_contents(
|
|
370
|
+
data_buf = afc.get_file_contents("/Books/iBooksData2.plist")
|
|
333
371
|
except AfcFileNotFoundError:
|
|
334
372
|
pass
|
|
335
373
|
else:
|
|
336
|
-
ret[
|
|
374
|
+
ret["iBooks Data 2"] = data_buf
|
|
337
375
|
if itunes_settings:
|
|
338
|
-
ret[
|
|
376
|
+
ret["iTunes Settings"] = itunes_settings
|
|
339
377
|
return ret
|
|
340
378
|
|
|
341
379
|
@contextmanager
|
|
342
380
|
def _backup_lock(self, afc, notification_proxy):
|
|
343
381
|
notification_proxy.notify_post(NP_SYNC_WILL_START)
|
|
344
|
-
lockfile = afc.fopen(
|
|
382
|
+
lockfile = afc.fopen("/com.apple.itunes.lock_sync", "r+")
|
|
345
383
|
if lockfile:
|
|
346
384
|
notification_proxy.notify_post(NP_SYNC_LOCK_REQUEST)
|
|
347
385
|
for _ in range(50):
|
|
@@ -358,7 +396,7 @@ class Mobilebackup2Service(LockdownService):
|
|
|
358
396
|
break
|
|
359
397
|
else: # No break, lock failed.
|
|
360
398
|
afc.fclose(lockfile)
|
|
361
|
-
raise PyMobileDevice3Exception(
|
|
399
|
+
raise PyMobileDevice3Exception("Failed to lock itunes sync file")
|
|
362
400
|
try:
|
|
363
401
|
yield
|
|
364
402
|
finally:
|
|
@@ -369,9 +407,9 @@ class Mobilebackup2Service(LockdownService):
|
|
|
369
407
|
@staticmethod
|
|
370
408
|
def _assert_backup_exists(backup_directory: Path, identifier: str):
|
|
371
409
|
device_directory = backup_directory / identifier
|
|
372
|
-
assert (device_directory /
|
|
373
|
-
assert (device_directory /
|
|
374
|
-
assert (device_directory /
|
|
410
|
+
assert (device_directory / "Info.plist").exists()
|
|
411
|
+
assert (device_directory / "Manifest.plist").exists()
|
|
412
|
+
assert (device_directory / "Status.plist").exists()
|
|
375
413
|
|
|
376
414
|
@contextmanager
|
|
377
415
|
def device_link(self, backup_directory):
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import socket
|
|
2
2
|
from collections.abc import Generator
|
|
3
|
-
from typing import Union
|
|
3
|
+
from typing import Optional, Union
|
|
4
4
|
|
|
5
5
|
from pymobiledevice3.exceptions import NotificationTimeoutError
|
|
6
6
|
from pymobiledevice3.lockdown_service_provider import LockdownServiceProvider
|
|
@@ -9,13 +9,13 @@ from pymobiledevice3.services.lockdown_service import LockdownService
|
|
|
9
9
|
|
|
10
10
|
|
|
11
11
|
class NotificationProxyService(LockdownService):
|
|
12
|
-
SERVICE_NAME =
|
|
13
|
-
RSD_SERVICE_NAME =
|
|
12
|
+
SERVICE_NAME = "com.apple.mobile.notification_proxy"
|
|
13
|
+
RSD_SERVICE_NAME = "com.apple.mobile.notification_proxy.shim.remote"
|
|
14
14
|
|
|
15
|
-
INSECURE_SERVICE_NAME =
|
|
16
|
-
RSD_INSECURE_SERVICE_NAME =
|
|
15
|
+
INSECURE_SERVICE_NAME = "com.apple.mobile.insecure_notification_proxy"
|
|
16
|
+
RSD_INSECURE_SERVICE_NAME = "com.apple.mobile.insecure_notification_proxy.shim.remote"
|
|
17
17
|
|
|
18
|
-
def __init__(self, lockdown: LockdownServiceProvider, insecure=False, timeout: Union[float, int] = None):
|
|
18
|
+
def __init__(self, lockdown: LockdownServiceProvider, insecure=False, timeout: Optional[Union[float, int]] = None):
|
|
19
19
|
if isinstance(lockdown, RemoteServiceDiscoveryService):
|
|
20
20
|
secure_service_name = self.RSD_SERVICE_NAME
|
|
21
21
|
insecure_service_name = self.RSD_INSECURE_SERVICE_NAME
|
|
@@ -32,13 +32,13 @@ class NotificationProxyService(LockdownService):
|
|
|
32
32
|
self.service.socket.settimeout(timeout)
|
|
33
33
|
|
|
34
34
|
def notify_post(self, name: str) -> None:
|
|
35
|
-
"""
|
|
36
|
-
self.service.send_plist({
|
|
35
|
+
"""Send notification to the device's notification_proxy."""
|
|
36
|
+
self.service.send_plist({"Command": "PostNotification", "Name": name})
|
|
37
37
|
|
|
38
38
|
def notify_register_dispatch(self, name: str) -> None:
|
|
39
|
-
"""
|
|
40
|
-
self.logger.info(f
|
|
41
|
-
self.service.send_plist({
|
|
39
|
+
"""Tells the device to send a notification on the specified event."""
|
|
40
|
+
self.logger.info(f"Observing {name}")
|
|
41
|
+
self.service.send_plist({"Command": "ObserveNotification", "Name": name})
|
|
42
42
|
|
|
43
43
|
def receive_notification(self) -> Generator[dict, None, None]:
|
|
44
44
|
while True:
|