pymobiledevice3 5.0.1__py3-none-any.whl → 5.0.3__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 +36 -59
- 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 +396 -242
- 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 +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 +137 -127
- 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 +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 +442 -421
- 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 +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 +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 +129 -117
- 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.1.dist-info → pymobiledevice3-5.0.3.dist-info}/METADATA +1 -1
- pymobiledevice3-5.0.3.dist-info/RECORD +173 -0
- pymobiledevice3-5.0.1.dist-info/RECORD +0 -173
- {pymobiledevice3-5.0.1.dist-info → pymobiledevice3-5.0.3.dist-info}/WHEEL +0 -0
- {pymobiledevice3-5.0.1.dist-info → pymobiledevice3-5.0.3.dist-info}/entry_points.txt +0 -0
- {pymobiledevice3-5.0.1.dist-info → pymobiledevice3-5.0.3.dist-info}/licenses/LICENSE +0 -0
- {pymobiledevice3-5.0.1.dist-info → pymobiledevice3-5.0.3.dist-info}/top_level.txt +0 -0
pymobiledevice3/usbmux.py
CHANGED
|
@@ -5,76 +5,115 @@ import time
|
|
|
5
5
|
from dataclasses import dataclass
|
|
6
6
|
from typing import Optional
|
|
7
7
|
|
|
8
|
-
from construct import
|
|
9
|
-
|
|
8
|
+
from construct import (
|
|
9
|
+
Const,
|
|
10
|
+
CString,
|
|
11
|
+
Enum,
|
|
12
|
+
FixedSized,
|
|
13
|
+
GreedyBytes,
|
|
14
|
+
Int16ul,
|
|
15
|
+
Int32ul,
|
|
16
|
+
Padding,
|
|
17
|
+
Prefixed,
|
|
18
|
+
StreamError,
|
|
19
|
+
Struct,
|
|
20
|
+
Switch,
|
|
21
|
+
this,
|
|
22
|
+
)
|
|
10
23
|
|
|
11
|
-
from pymobiledevice3.exceptions import
|
|
12
|
-
|
|
24
|
+
from pymobiledevice3.exceptions import (
|
|
25
|
+
BadCommandError,
|
|
26
|
+
BadDevError,
|
|
27
|
+
ConnectionFailedError,
|
|
28
|
+
ConnectionFailedToUsbmuxdError,
|
|
29
|
+
MuxException,
|
|
30
|
+
MuxVersionError,
|
|
31
|
+
NotPairedError,
|
|
32
|
+
)
|
|
13
33
|
from pymobiledevice3.osu.os_utils import get_os_utils
|
|
14
34
|
|
|
15
|
-
usbmuxd_version = Enum(
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
35
|
+
usbmuxd_version = Enum(
|
|
36
|
+
Int32ul,
|
|
37
|
+
BINARY=0,
|
|
38
|
+
PLIST=1,
|
|
39
|
+
)
|
|
40
|
+
|
|
41
|
+
usbmuxd_result = Enum(
|
|
42
|
+
Int32ul,
|
|
43
|
+
OK=0,
|
|
44
|
+
BADCOMMAND=1,
|
|
45
|
+
BADDEV=2,
|
|
46
|
+
CONNREFUSED=3,
|
|
47
|
+
NOSUCHSERVICE=4,
|
|
48
|
+
BADVERSION=6,
|
|
49
|
+
)
|
|
50
|
+
|
|
51
|
+
usbmuxd_msgtype = Enum(
|
|
52
|
+
Int32ul,
|
|
53
|
+
RESULT=1,
|
|
54
|
+
CONNECT=2,
|
|
55
|
+
LISTEN=3,
|
|
56
|
+
ADD=4,
|
|
57
|
+
REMOVE=5,
|
|
58
|
+
PAIRED=6,
|
|
59
|
+
PLIST=8,
|
|
60
|
+
)
|
|
38
61
|
|
|
39
62
|
usbmuxd_header = Struct(
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
63
|
+
"version" / usbmuxd_version, # protocol version
|
|
64
|
+
"message" / usbmuxd_msgtype, # message type
|
|
65
|
+
"tag" / Int32ul, # responses to this query will echo back this tag
|
|
43
66
|
)
|
|
44
67
|
|
|
45
|
-
usbmuxd_request = Prefixed(
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
68
|
+
usbmuxd_request = Prefixed(
|
|
69
|
+
Int32ul,
|
|
70
|
+
Struct(
|
|
71
|
+
"header" / usbmuxd_header,
|
|
72
|
+
"data"
|
|
73
|
+
/ Switch(
|
|
74
|
+
this.header.message,
|
|
75
|
+
{
|
|
76
|
+
usbmuxd_msgtype.CONNECT: Struct(
|
|
77
|
+
"device_id" / Int32ul,
|
|
78
|
+
"port" / Int16ul, # TCP port number
|
|
79
|
+
"reserved" / Const(0, Int16ul),
|
|
80
|
+
),
|
|
81
|
+
usbmuxd_msgtype.PLIST: GreedyBytes,
|
|
82
|
+
},
|
|
52
83
|
),
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
)
|
|
84
|
+
),
|
|
85
|
+
includelength=True,
|
|
86
|
+
)
|
|
56
87
|
|
|
57
88
|
usbmuxd_device_record = Struct(
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
89
|
+
"device_id" / Int32ul,
|
|
90
|
+
"product_id" / Int16ul,
|
|
91
|
+
"serial_number" / FixedSized(256, CString("ascii")),
|
|
61
92
|
Padding(2),
|
|
62
|
-
|
|
93
|
+
"location" / Int32ul,
|
|
63
94
|
)
|
|
64
95
|
|
|
65
|
-
usbmuxd_response = Prefixed(
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
96
|
+
usbmuxd_response = Prefixed(
|
|
97
|
+
Int32ul,
|
|
98
|
+
Struct(
|
|
99
|
+
"header" / usbmuxd_header,
|
|
100
|
+
"data"
|
|
101
|
+
/ Switch(
|
|
102
|
+
this.header.message,
|
|
103
|
+
{
|
|
104
|
+
usbmuxd_msgtype.RESULT: Struct(
|
|
105
|
+
"result" / usbmuxd_result,
|
|
106
|
+
),
|
|
107
|
+
usbmuxd_msgtype.ADD: usbmuxd_device_record,
|
|
108
|
+
usbmuxd_msgtype.REMOVE: Struct(
|
|
109
|
+
"device_id" / Int32ul,
|
|
110
|
+
),
|
|
111
|
+
usbmuxd_msgtype.PLIST: GreedyBytes,
|
|
112
|
+
},
|
|
70
113
|
),
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
),
|
|
75
|
-
usbmuxd_msgtype.PLIST: GreedyBytes,
|
|
76
|
-
}),
|
|
77
|
-
), includelength=True)
|
|
114
|
+
),
|
|
115
|
+
includelength=True,
|
|
116
|
+
)
|
|
78
117
|
|
|
79
118
|
|
|
80
119
|
@dataclass
|
|
@@ -87,24 +126,24 @@ class MuxDevice:
|
|
|
87
126
|
mux = create_mux(usbmux_address=usbmux_address)
|
|
88
127
|
try:
|
|
89
128
|
return mux.connect(self, port)
|
|
90
|
-
except:
|
|
129
|
+
except:
|
|
91
130
|
mux.close()
|
|
92
131
|
raise
|
|
93
132
|
|
|
94
133
|
@property
|
|
95
134
|
def is_usb(self) -> bool:
|
|
96
|
-
return self.connection_type ==
|
|
135
|
+
return self.connection_type == "USB"
|
|
97
136
|
|
|
98
137
|
@property
|
|
99
138
|
def is_network(self) -> bool:
|
|
100
|
-
return self.connection_type ==
|
|
139
|
+
return self.connection_type == "Network"
|
|
101
140
|
|
|
102
141
|
def matches_udid(self, udid: str) -> bool:
|
|
103
|
-
return self.serial.replace(
|
|
142
|
+
return self.serial.replace("-", "") == udid.replace("-", "")
|
|
104
143
|
|
|
105
144
|
|
|
106
145
|
class SafeStreamSocket:
|
|
107
|
-
"""
|
|
146
|
+
"""wrapper to native python socket object to be used with construct as a stream"""
|
|
108
147
|
|
|
109
148
|
def __init__(self, address, family):
|
|
110
149
|
self._offset = 0
|
|
@@ -117,12 +156,12 @@ class SafeStreamSocket:
|
|
|
117
156
|
return len(msg)
|
|
118
157
|
|
|
119
158
|
def recv(self, size: int) -> bytes:
|
|
120
|
-
msg = b
|
|
159
|
+
msg = b""
|
|
121
160
|
while len(msg) < size:
|
|
122
161
|
chunk = self.sock.recv(size - len(msg))
|
|
123
162
|
self._offset += len(chunk)
|
|
124
163
|
if not chunk:
|
|
125
|
-
raise MuxException(
|
|
164
|
+
raise MuxException("socket connection broken")
|
|
126
165
|
msg += chunk
|
|
127
166
|
return msg
|
|
128
167
|
|
|
@@ -144,18 +183,18 @@ class SafeStreamSocket:
|
|
|
144
183
|
|
|
145
184
|
class MuxConnection:
|
|
146
185
|
# used on Windows
|
|
147
|
-
ITUNES_HOST = (
|
|
186
|
+
ITUNES_HOST = ("127.0.0.1", 27015)
|
|
148
187
|
|
|
149
188
|
# used for macOS and Linux
|
|
150
|
-
USBMUXD_PIPE =
|
|
189
|
+
USBMUXD_PIPE = "/var/run/usbmuxd"
|
|
151
190
|
|
|
152
191
|
@staticmethod
|
|
153
192
|
def create_usbmux_socket(usbmux_address: Optional[str] = None) -> SafeStreamSocket:
|
|
154
193
|
try:
|
|
155
194
|
if usbmux_address is not None:
|
|
156
|
-
if
|
|
195
|
+
if ":" in usbmux_address:
|
|
157
196
|
# assume tcp address
|
|
158
|
-
hostname, port = usbmux_address.split(
|
|
197
|
+
hostname, port = usbmux_address.split(":")
|
|
159
198
|
port = int(port)
|
|
160
199
|
address = (hostname, port)
|
|
161
200
|
family = socket.AF_INET
|
|
@@ -166,8 +205,8 @@ class MuxConnection:
|
|
|
166
205
|
else:
|
|
167
206
|
address, family = get_os_utils().usbmux_address
|
|
168
207
|
return SafeStreamSocket(address, family)
|
|
169
|
-
except ConnectionRefusedError:
|
|
170
|
-
raise ConnectionFailedToUsbmuxdError()
|
|
208
|
+
except ConnectionRefusedError as e:
|
|
209
|
+
raise ConnectionFailedToUsbmuxdError() from e
|
|
171
210
|
|
|
172
211
|
@staticmethod
|
|
173
212
|
def create(usbmux_address: Optional[str] = None):
|
|
@@ -176,8 +215,8 @@ class MuxConnection:
|
|
|
176
215
|
|
|
177
216
|
try:
|
|
178
217
|
message = usbmuxd_request.build({
|
|
179
|
-
|
|
180
|
-
|
|
218
|
+
"header": {"version": usbmuxd_version.PLIST, "message": usbmuxd_msgtype.PLIST, "tag": 1},
|
|
219
|
+
"data": plistlib.dumps({"MessageType": "ReadBUID"}),
|
|
181
220
|
})
|
|
182
221
|
sock.send(message)
|
|
183
222
|
response = usbmuxd_response.parse_stream(sock)
|
|
@@ -192,7 +231,7 @@ class MuxConnection:
|
|
|
192
231
|
elif response.header.version == usbmuxd_version.PLIST:
|
|
193
232
|
return PlistMuxConnection(sock)
|
|
194
233
|
|
|
195
|
-
raise MuxVersionError(f
|
|
234
|
+
raise MuxVersionError(f"usbmuxd returned unsupported version: {response.version}")
|
|
196
235
|
|
|
197
236
|
def __init__(self, sock: SafeStreamSocket):
|
|
198
237
|
self._sock = sock
|
|
@@ -209,30 +248,30 @@ class MuxConnection:
|
|
|
209
248
|
|
|
210
249
|
@abc.abstractmethod
|
|
211
250
|
def _connect(self, device_id: int, port: int):
|
|
212
|
-
"""
|
|
251
|
+
"""initiate a "Connect" request to target port"""
|
|
213
252
|
pass
|
|
214
253
|
|
|
215
254
|
@abc.abstractmethod
|
|
216
|
-
def get_device_list(self, timeout: float = None):
|
|
255
|
+
def get_device_list(self, timeout: Optional[float] = None):
|
|
217
256
|
"""
|
|
218
257
|
request an update to current device list
|
|
219
258
|
"""
|
|
220
259
|
pass
|
|
221
260
|
|
|
222
261
|
def connect(self, device: MuxDevice, port: int) -> socket.socket:
|
|
223
|
-
"""
|
|
262
|
+
"""connect to a relay port on target machine and get a raw python socket object for the connection"""
|
|
224
263
|
self._connect(device.devid, socket.htons(port))
|
|
225
264
|
self._connected = True
|
|
226
265
|
return self._sock.sock
|
|
227
266
|
|
|
228
267
|
def close(self):
|
|
229
|
-
"""
|
|
268
|
+
"""close current socket"""
|
|
230
269
|
self._sock.close()
|
|
231
270
|
|
|
232
271
|
def _assert_not_connected(self):
|
|
233
|
-
"""
|
|
272
|
+
"""verify active state is in state for control messages"""
|
|
234
273
|
if self._connected:
|
|
235
|
-
raise MuxException(
|
|
274
|
+
raise MuxException("Mux is connected, cannot issue control packets")
|
|
236
275
|
|
|
237
276
|
def _raise_mux_exception(self, result: int, message: Optional[str] = None) -> None:
|
|
238
277
|
exceptions = {
|
|
@@ -253,14 +292,14 @@ class MuxConnection:
|
|
|
253
292
|
|
|
254
293
|
|
|
255
294
|
class BinaryMuxConnection(MuxConnection):
|
|
256
|
-
"""
|
|
295
|
+
"""old binary protocol"""
|
|
257
296
|
|
|
258
297
|
def __init__(self, sock: SafeStreamSocket):
|
|
259
298
|
super().__init__(sock)
|
|
260
299
|
self._version = usbmuxd_version.BINARY
|
|
261
300
|
|
|
262
|
-
def get_device_list(self, timeout: float = None):
|
|
263
|
-
"""
|
|
301
|
+
def get_device_list(self, timeout: Optional[float] = None):
|
|
302
|
+
"""use timeout to wait for the device list to be fully populated"""
|
|
264
303
|
self._assert_not_connected()
|
|
265
304
|
end = time.time() + timeout
|
|
266
305
|
self.listen()
|
|
@@ -270,55 +309,54 @@ class BinaryMuxConnection(MuxConnection):
|
|
|
270
309
|
self._receive_device_state_update()
|
|
271
310
|
except (BlockingIOError, StreamError):
|
|
272
311
|
continue
|
|
273
|
-
except
|
|
312
|
+
except OSError as e:
|
|
274
313
|
try:
|
|
275
314
|
self._sock.setblocking(True)
|
|
276
315
|
self.close()
|
|
277
316
|
except OSError:
|
|
278
317
|
pass
|
|
279
|
-
raise MuxException(
|
|
318
|
+
raise MuxException("Exception in listener socket") from e
|
|
280
319
|
|
|
281
320
|
def listen(self):
|
|
282
|
-
"""
|
|
321
|
+
"""start listening for events of attached and detached devices"""
|
|
283
322
|
self._send_receive(usbmuxd_msgtype.LISTEN)
|
|
284
323
|
|
|
285
324
|
def _connect(self, device_id: int, port: int):
|
|
286
|
-
self._send({
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
})
|
|
325
|
+
self._send({
|
|
326
|
+
"header": {"version": self._version, "message": usbmuxd_msgtype.CONNECT, "tag": self._tag},
|
|
327
|
+
"data": {"device_id": device_id, "port": port},
|
|
328
|
+
})
|
|
291
329
|
response = self._receive()
|
|
292
330
|
if response.header.message != usbmuxd_msgtype.RESULT:
|
|
293
|
-
raise MuxException(f
|
|
331
|
+
raise MuxException(f"unexpected message type received: {response}")
|
|
294
332
|
|
|
295
333
|
if response.data.result != usbmuxd_result.OK:
|
|
296
|
-
raise self._raise_mux_exception(
|
|
297
|
-
|
|
298
|
-
|
|
334
|
+
raise self._raise_mux_exception(
|
|
335
|
+
int(response.data.result),
|
|
336
|
+
f"failed to connect to device: {device_id} at port: {port}. reason: {response.data.result}",
|
|
337
|
+
)
|
|
299
338
|
|
|
300
339
|
def _send(self, data: dict) -> None:
|
|
301
340
|
self._assert_not_connected()
|
|
302
341
|
self._sock.send(usbmuxd_request.build(data))
|
|
303
342
|
self._tag += 1
|
|
304
343
|
|
|
305
|
-
def _receive(self, expected_tag: int = None):
|
|
344
|
+
def _receive(self, expected_tag: Optional[int] = None):
|
|
306
345
|
self._assert_not_connected()
|
|
307
346
|
response = usbmuxd_response.parse_stream(self._sock)
|
|
308
347
|
if expected_tag and response.header.tag != expected_tag:
|
|
309
|
-
raise MuxException(f
|
|
348
|
+
raise MuxException(f"Reply tag mismatch: expected {expected_tag}, got {response.header.tag}")
|
|
310
349
|
return response
|
|
311
350
|
|
|
312
351
|
def _send_receive(self, message_type: int):
|
|
313
|
-
self._send({
|
|
314
|
-
'data': b''})
|
|
352
|
+
self._send({"header": {"version": self._version, "message": message_type, "tag": self._tag}, "data": b""})
|
|
315
353
|
response = self._receive(self._tag - 1)
|
|
316
354
|
if response.header.message != usbmuxd_msgtype.RESULT:
|
|
317
|
-
raise MuxException(f
|
|
355
|
+
raise MuxException(f"unexpected message type received: {response}")
|
|
318
356
|
|
|
319
357
|
result = response.data.result
|
|
320
358
|
if result != usbmuxd_result.OK:
|
|
321
|
-
raise self._raise_mux_exception(int(result), f
|
|
359
|
+
raise self._raise_mux_exception(int(result), f"{message_type} failed: error {result}")
|
|
322
360
|
|
|
323
361
|
def _add_device(self, device: MuxDevice):
|
|
324
362
|
self.devices.append(device)
|
|
@@ -330,11 +368,11 @@ class BinaryMuxConnection(MuxConnection):
|
|
|
330
368
|
response = self._receive()
|
|
331
369
|
if response.header.message == usbmuxd_msgtype.ADD:
|
|
332
370
|
# old protocol only supported USB devices
|
|
333
|
-
self._add_device(MuxDevice(response.data.device_id, response.data.serial_number,
|
|
371
|
+
self._add_device(MuxDevice(response.data.device_id, response.data.serial_number, "USB"))
|
|
334
372
|
elif response.header.message == usbmuxd_msgtype.REMOVE:
|
|
335
373
|
self._remove_device(response.data.device_id)
|
|
336
374
|
else:
|
|
337
|
-
raise MuxException(f
|
|
375
|
+
raise MuxException(f"Invalid packet type received: {response}")
|
|
338
376
|
|
|
339
377
|
|
|
340
378
|
class PlistMuxConnection(BinaryMuxConnection):
|
|
@@ -343,71 +381,77 @@ class PlistMuxConnection(BinaryMuxConnection):
|
|
|
343
381
|
self._version = usbmuxd_version.PLIST
|
|
344
382
|
|
|
345
383
|
def listen(self) -> None:
|
|
346
|
-
self._send_receive({
|
|
384
|
+
self._send_receive({"MessageType": "Listen"})
|
|
347
385
|
|
|
348
386
|
def get_pair_record(self, serial: str) -> dict:
|
|
349
387
|
# serials are saved inside usbmuxd without '-'
|
|
350
|
-
self._send({
|
|
388
|
+
self._send({"MessageType": "ReadPairRecord", "PairRecordID": serial})
|
|
351
389
|
response = self._receive(self._tag - 1)
|
|
352
|
-
pair_record = response.get(
|
|
390
|
+
pair_record = response.get("PairRecordData")
|
|
353
391
|
if pair_record is None:
|
|
354
|
-
raise NotPairedError(
|
|
392
|
+
raise NotPairedError("device should be paired first")
|
|
355
393
|
return plistlib.loads(pair_record)
|
|
356
394
|
|
|
357
|
-
def get_device_list(self, timeout: float = None) -> None:
|
|
358
|
-
"""
|
|
395
|
+
def get_device_list(self, timeout: Optional[float] = None) -> None:
|
|
396
|
+
"""get device list synchronously without waiting the timeout"""
|
|
359
397
|
self.devices = []
|
|
360
|
-
self._send({
|
|
398
|
+
self._send({"MessageType": "ListDevices"})
|
|
361
399
|
response = self._receive(self._tag - 1)
|
|
362
|
-
device_list = response.get(
|
|
400
|
+
device_list = response.get("DeviceList")
|
|
363
401
|
if device_list is None:
|
|
364
|
-
raise MuxException(f
|
|
402
|
+
raise MuxException(f"Got an invalid response from usbmux: {response}")
|
|
365
403
|
for response in device_list:
|
|
366
|
-
if response[
|
|
367
|
-
super()._add_device(
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
404
|
+
if response["MessageType"] == "Attached":
|
|
405
|
+
super()._add_device(
|
|
406
|
+
MuxDevice(
|
|
407
|
+
response["DeviceID"],
|
|
408
|
+
response["Properties"]["SerialNumber"],
|
|
409
|
+
response["Properties"]["ConnectionType"],
|
|
410
|
+
)
|
|
411
|
+
)
|
|
412
|
+
elif response["MessageType"] == "Detached":
|
|
413
|
+
super()._remove_device(response["DeviceID"])
|
|
371
414
|
else:
|
|
372
|
-
raise MuxException(f
|
|
415
|
+
raise MuxException(f"Invalid packet type received: {response}")
|
|
373
416
|
|
|
374
417
|
def get_buid(self) -> str:
|
|
375
|
-
"""
|
|
376
|
-
self._send({
|
|
377
|
-
return self._receive(self._tag - 1)[
|
|
418
|
+
"""get SystemBUID"""
|
|
419
|
+
self._send({"MessageType": "ReadBUID"})
|
|
420
|
+
return self._receive(self._tag - 1)["BUID"]
|
|
378
421
|
|
|
379
422
|
def save_pair_record(self, serial: str, device_id: int, record_data: bytes):
|
|
380
423
|
# serials are saved inside usbmuxd without '-'
|
|
381
|
-
self._send_receive({
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
424
|
+
self._send_receive({
|
|
425
|
+
"MessageType": "SavePairRecord",
|
|
426
|
+
"PairRecordID": serial,
|
|
427
|
+
"PairRecordData": record_data,
|
|
428
|
+
"DeviceID": device_id,
|
|
429
|
+
})
|
|
385
430
|
|
|
386
431
|
def _connect(self, device_id: int, port: int):
|
|
387
|
-
self._send_receive({
|
|
432
|
+
self._send_receive({"MessageType": "Connect", "DeviceID": device_id, "PortNumber": port})
|
|
388
433
|
|
|
389
434
|
def _send(self, data: dict):
|
|
390
|
-
request = {
|
|
435
|
+
request = {"ClientVersionString": "qt4i-usbmuxd", "ProgName": "pymobiledevice3", "kLibUSBMuxVersion": 3}
|
|
391
436
|
request.update(data)
|
|
392
|
-
super()._send({
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
})
|
|
437
|
+
super()._send({
|
|
438
|
+
"header": {"version": self._version, "message": usbmuxd_msgtype.PLIST, "tag": self._tag},
|
|
439
|
+
"data": plistlib.dumps(request),
|
|
440
|
+
})
|
|
397
441
|
|
|
398
|
-
def _receive(self, expected_tag: int = None) -> dict:
|
|
442
|
+
def _receive(self, expected_tag: Optional[int] = None) -> dict:
|
|
399
443
|
response = super()._receive(expected_tag=expected_tag)
|
|
400
444
|
if response.header.message != usbmuxd_msgtype.PLIST:
|
|
401
|
-
raise MuxException(f
|
|
445
|
+
raise MuxException(f"Received non-plist type {response}")
|
|
402
446
|
return plistlib.loads(response.data)
|
|
403
447
|
|
|
404
448
|
def _send_receive(self, data: dict):
|
|
405
449
|
self._send(data)
|
|
406
450
|
response = self._receive(self._tag - 1)
|
|
407
|
-
if response[
|
|
408
|
-
raise MuxException(f
|
|
409
|
-
if response[
|
|
410
|
-
raise self._raise_mux_exception(response[
|
|
451
|
+
if response["MessageType"] != "Result":
|
|
452
|
+
raise MuxException(f"got an invalid message: {response}")
|
|
453
|
+
if response["Number"] != 0:
|
|
454
|
+
raise self._raise_mux_exception(response["Number"], f"got an error message: {response}")
|
|
411
455
|
|
|
412
456
|
|
|
413
457
|
def create_mux(usbmux_address: Optional[str] = None) -> MuxConnection:
|
|
@@ -424,8 +468,9 @@ def list_devices(usbmux_address: Optional[str] = None) -> list[MuxDevice]:
|
|
|
424
468
|
return devices
|
|
425
469
|
|
|
426
470
|
|
|
427
|
-
def select_device(
|
|
428
|
-
|
|
471
|
+
def select_device(
|
|
472
|
+
udid: Optional[str] = None, connection_type: Optional[str] = None, usbmux_address: Optional[str] = None
|
|
473
|
+
) -> Optional[MuxDevice]:
|
|
429
474
|
"""
|
|
430
475
|
select a UsbMux device according to given arguments.
|
|
431
476
|
if more than one device could be selected, always prefer the usb one.
|
pymobiledevice3/utils.py
CHANGED
|
@@ -15,16 +15,16 @@ def plist_access_path(d, path: tuple, type_=None, required=False):
|
|
|
15
15
|
if d is None:
|
|
16
16
|
break
|
|
17
17
|
|
|
18
|
-
if type_
|
|
19
|
-
if d.lower() not in (
|
|
18
|
+
if type_ is bool and isinstance(d, str):
|
|
19
|
+
if d.lower() not in ("true", "false"):
|
|
20
20
|
raise ValueError()
|
|
21
|
-
d =
|
|
21
|
+
d = d.lower() == "true"
|
|
22
22
|
elif type_ is not None and not isinstance(d, type_):
|
|
23
23
|
# wrong type
|
|
24
24
|
d = None
|
|
25
25
|
|
|
26
26
|
if d is None and required:
|
|
27
|
-
raise KeyError(f
|
|
27
|
+
raise KeyError(f"path: {path} doesn't exist in given plist object")
|
|
28
28
|
|
|
29
29
|
return d
|
|
30
30
|
|
|
@@ -35,7 +35,7 @@ def bytes_to_uint(b: bytes):
|
|
|
35
35
|
|
|
36
36
|
def try_decode(s: bytes):
|
|
37
37
|
try:
|
|
38
|
-
return s.decode(
|
|
38
|
+
return s.decode("utf8")
|
|
39
39
|
except UnicodeDecodeError:
|
|
40
40
|
return s
|
|
41
41
|
|
|
@@ -45,7 +45,7 @@ def asyncio_print_traceback(f: Callable):
|
|
|
45
45
|
async def wrapper(*args, **kwargs):
|
|
46
46
|
try:
|
|
47
47
|
return await f(*args, **kwargs)
|
|
48
|
-
except (Exception, RuntimeError) as e:
|
|
48
|
+
except (Exception, RuntimeError) as e:
|
|
49
49
|
if not isinstance(e, asyncio.CancelledError):
|
|
50
50
|
traceback.print_exc()
|
|
51
51
|
raise
|
|
@@ -57,7 +57,7 @@ def get_asyncio_loop() -> asyncio.AbstractEventLoop:
|
|
|
57
57
|
try:
|
|
58
58
|
loop = asyncio.get_running_loop()
|
|
59
59
|
if loop.is_closed():
|
|
60
|
-
raise RuntimeError(
|
|
60
|
+
raise RuntimeError("The existing loop is closed.")
|
|
61
61
|
except RuntimeError:
|
|
62
62
|
# This happens when there is no current event loop
|
|
63
63
|
loop = asyncio.new_event_loop()
|
|
@@ -67,14 +67,17 @@ def get_asyncio_loop() -> asyncio.AbstractEventLoop:
|
|
|
67
67
|
|
|
68
68
|
def file_download(url: str, outfile: Path, chunk_size=1024) -> None:
|
|
69
69
|
resp = requests.get(url, stream=True)
|
|
70
|
-
total = int(resp.headers.get(
|
|
71
|
-
with
|
|
70
|
+
total = int(resp.headers.get("content-length", 0))
|
|
71
|
+
with (
|
|
72
|
+
outfile.open("wb") as file,
|
|
73
|
+
tqdm(
|
|
72
74
|
desc=outfile.name,
|
|
73
75
|
total=total,
|
|
74
|
-
unit=
|
|
76
|
+
unit="iB",
|
|
75
77
|
unit_scale=True,
|
|
76
78
|
unit_divisor=1024,
|
|
77
|
-
|
|
79
|
+
) as bar,
|
|
80
|
+
):
|
|
78
81
|
for data in resp.iter_content(chunk_size=chunk_size):
|
|
79
82
|
size = file.write(data)
|
|
80
83
|
bar.update(size)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: pymobiledevice3
|
|
3
|
-
Version: 5.0.
|
|
3
|
+
Version: 5.0.3
|
|
4
4
|
Summary: Pure python3 implementation for working with iDevices (iPhone, etc...)
|
|
5
5
|
Author-email: doronz88 <doron88@gmail.com>, matan <matan1008@gmail.com>
|
|
6
6
|
Maintainer-email: doronz88 <doron88@gmail.com>, matan <matan1008@gmail.com>
|