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
|
@@ -1,15 +1,20 @@
|
|
|
1
1
|
import asyncio
|
|
2
|
+
import contextlib
|
|
2
3
|
import json
|
|
3
4
|
import logging
|
|
4
5
|
import uuid
|
|
6
|
+
from collections.abc import Coroutine
|
|
5
7
|
from dataclasses import dataclass, fields
|
|
6
8
|
from enum import Enum
|
|
7
|
-
from typing import Optional, Union
|
|
9
|
+
from typing import Any, Optional, Union
|
|
8
10
|
|
|
9
11
|
import nest_asyncio
|
|
10
12
|
|
|
11
|
-
from pymobiledevice3.exceptions import
|
|
12
|
-
|
|
13
|
+
from pymobiledevice3.exceptions import (
|
|
14
|
+
LaunchingApplicationError,
|
|
15
|
+
RemoteAutomationNotEnabledError,
|
|
16
|
+
WebInspectorNotEnabledError,
|
|
17
|
+
)
|
|
13
18
|
from pymobiledevice3.lockdown import LockdownClient
|
|
14
19
|
from pymobiledevice3.lockdown_service_provider import LockdownServiceProvider
|
|
15
20
|
from pymobiledevice3.service_connection import ServiceConnection
|
|
@@ -17,55 +22,55 @@ from pymobiledevice3.services.web_protocol.automation_session import AutomationS
|
|
|
17
22
|
from pymobiledevice3.services.web_protocol.inspector_session import InspectorSession
|
|
18
23
|
from pymobiledevice3.services.web_protocol.session_protocol import SessionProtocol
|
|
19
24
|
|
|
20
|
-
SAFARI =
|
|
25
|
+
SAFARI = "com.apple.mobilesafari"
|
|
21
26
|
|
|
22
27
|
|
|
23
28
|
def key_to_pid(key: str) -> int:
|
|
24
|
-
return int(key.split(
|
|
29
|
+
return int(key.split(":")[1])
|
|
25
30
|
|
|
26
31
|
|
|
27
32
|
class WirTypes(Enum):
|
|
28
|
-
AUTOMATION =
|
|
29
|
-
ITML =
|
|
30
|
-
JAVASCRIPT =
|
|
31
|
-
PAGE =
|
|
32
|
-
SERVICE_WORKER =
|
|
33
|
-
WEB =
|
|
34
|
-
WEB_PAGE =
|
|
35
|
-
AUTOMATICALLY_PAUSE =
|
|
33
|
+
AUTOMATION = "WIRTypeAutomation"
|
|
34
|
+
ITML = "WIRTypeITML"
|
|
35
|
+
JAVASCRIPT = "WIRTypeJavaScript"
|
|
36
|
+
PAGE = "WIRTypePage"
|
|
37
|
+
SERVICE_WORKER = "WIRTypeServiceWorker"
|
|
38
|
+
WEB = "WIRTypeWeb"
|
|
39
|
+
WEB_PAGE = "WIRTypeWebPage"
|
|
40
|
+
AUTOMATICALLY_PAUSE = "WIRAutomaticallyPause"
|
|
36
41
|
|
|
37
42
|
|
|
38
43
|
class AutomationAvailability(Enum):
|
|
39
|
-
NOT_AVAILABLE =
|
|
40
|
-
AVAILABLE =
|
|
41
|
-
UNKNOWN =
|
|
44
|
+
NOT_AVAILABLE = "WIRAutomationAvailabilityNotAvailable"
|
|
45
|
+
AVAILABLE = "WIRAutomationAvailabilityAvailable"
|
|
46
|
+
UNKNOWN = "WIRAutomationAvailabilityUnknown"
|
|
42
47
|
|
|
43
48
|
|
|
44
49
|
@dataclass
|
|
45
50
|
class Page:
|
|
46
51
|
id_: int
|
|
47
52
|
type_: WirTypes
|
|
48
|
-
web_url: str =
|
|
49
|
-
web_title: str =
|
|
53
|
+
web_url: str = ""
|
|
54
|
+
web_title: str = ""
|
|
50
55
|
automation_is_paired_key: bool = False
|
|
51
|
-
automation_name: str =
|
|
52
|
-
automation_version: str =
|
|
53
|
-
automation_session_id: str =
|
|
54
|
-
automation_connection_id: str =
|
|
56
|
+
automation_name: str = ""
|
|
57
|
+
automation_version: str = ""
|
|
58
|
+
automation_session_id: str = ""
|
|
59
|
+
automation_connection_id: str = ""
|
|
55
60
|
|
|
56
61
|
@classmethod
|
|
57
|
-
def from_page_dictionary(cls, page_dict: dict) ->
|
|
58
|
-
p = cls(page_dict[
|
|
62
|
+
def from_page_dictionary(cls, page_dict: dict) -> "Page":
|
|
63
|
+
p = cls(page_dict["WIRPageIdentifierKey"], WirTypes(page_dict["WIRTypeKey"]))
|
|
59
64
|
if p.type_ in (WirTypes.WEB, WirTypes.WEB_PAGE):
|
|
60
|
-
p.web_title = page_dict[
|
|
61
|
-
p.web_url = page_dict[
|
|
65
|
+
p.web_title = page_dict["WIRTitleKey"]
|
|
66
|
+
p.web_url = page_dict["WIRURLKey"]
|
|
62
67
|
if p.type_ == WirTypes.AUTOMATION:
|
|
63
|
-
p.automation_is_paired_key = page_dict[
|
|
64
|
-
p.automation_name = page_dict[
|
|
65
|
-
p.automation_version = page_dict[
|
|
66
|
-
p.automation_session_id = page_dict[
|
|
67
|
-
if
|
|
68
|
-
p.automation_connection_id = page_dict[
|
|
68
|
+
p.automation_is_paired_key = page_dict["WIRAutomationTargetIsPairedKey"]
|
|
69
|
+
p.automation_name = page_dict["WIRAutomationTargetNameKey"]
|
|
70
|
+
p.automation_version = page_dict["WIRAutomationTargetVersionKey"]
|
|
71
|
+
p.automation_session_id = page_dict["WIRSessionIdentifierKey"]
|
|
72
|
+
if "WIRConnectionIdentifierKey" in page_dict:
|
|
73
|
+
p.automation_connection_id = page_dict["WIRConnectionIdentifierKey"]
|
|
69
74
|
return p
|
|
70
75
|
|
|
71
76
|
def update(self, page_dict: dict):
|
|
@@ -74,7 +79,7 @@ class Page:
|
|
|
74
79
|
setattr(self, field.name, getattr(new_p, field.name))
|
|
75
80
|
|
|
76
81
|
def __str__(self):
|
|
77
|
-
return f
|
|
82
|
+
return f"id: {self.id_}, title: {self.web_title}, url: {self.web_url}"
|
|
78
83
|
|
|
79
84
|
|
|
80
85
|
@dataclass
|
|
@@ -87,20 +92,20 @@ class Application:
|
|
|
87
92
|
active: int
|
|
88
93
|
proxy: bool
|
|
89
94
|
ready: bool
|
|
90
|
-
host: str =
|
|
95
|
+
host: str = ""
|
|
91
96
|
|
|
92
97
|
@classmethod
|
|
93
|
-
def from_application_dictionary(cls, app_dict) ->
|
|
98
|
+
def from_application_dictionary(cls, app_dict) -> "Application":
|
|
94
99
|
return cls(
|
|
95
|
-
app_dict[
|
|
96
|
-
app_dict[
|
|
97
|
-
key_to_pid(app_dict[
|
|
98
|
-
app_dict[
|
|
99
|
-
AutomationAvailability(app_dict[
|
|
100
|
-
app_dict[
|
|
101
|
-
app_dict[
|
|
102
|
-
app_dict[
|
|
103
|
-
app_dict.get(
|
|
100
|
+
app_dict["WIRApplicationIdentifierKey"],
|
|
101
|
+
app_dict["WIRApplicationBundleIdentifierKey"],
|
|
102
|
+
key_to_pid(app_dict["WIRApplicationIdentifierKey"]),
|
|
103
|
+
app_dict["WIRApplicationNameKey"],
|
|
104
|
+
AutomationAvailability(app_dict["WIRAutomationAvailabilityKey"]),
|
|
105
|
+
app_dict["WIRIsApplicationActiveKey"],
|
|
106
|
+
app_dict["WIRIsApplicationProxyKey"],
|
|
107
|
+
app_dict["WIRIsApplicationReadyKey"],
|
|
108
|
+
app_dict.get("WIRHostApplicationIdentifierKey", ""),
|
|
104
109
|
)
|
|
105
110
|
|
|
106
111
|
|
|
@@ -110,12 +115,12 @@ class ApplicationPage:
|
|
|
110
115
|
page: Page
|
|
111
116
|
|
|
112
117
|
def __str__(self) -> str:
|
|
113
|
-
return f
|
|
118
|
+
return f"<{self.application.name}({self.application.pid}) TYPE:{self.page.type_.value} URL:{self.page.web_url}>"
|
|
114
119
|
|
|
115
120
|
|
|
116
121
|
class WebinspectorService:
|
|
117
|
-
SERVICE_NAME =
|
|
118
|
-
RSD_SERVICE_NAME =
|
|
122
|
+
SERVICE_NAME = "com.apple.webinspector"
|
|
123
|
+
RSD_SERVICE_NAME = "com.apple.webinspector.shim.remote"
|
|
119
124
|
|
|
120
125
|
def __init__(self, lockdown: LockdownServiceProvider, loop=None):
|
|
121
126
|
if loop is None:
|
|
@@ -142,32 +147,30 @@ class WebinspectorService:
|
|
|
142
147
|
self.wir_message_results = {}
|
|
143
148
|
self.wir_events = []
|
|
144
149
|
self.receive_handlers = {
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
150
|
+
"_rpc_reportCurrentState:": self._handle_report_current_state,
|
|
151
|
+
"_rpc_reportConnectedApplicationList:": self._handle_report_connected_application_list,
|
|
152
|
+
"_rpc_reportConnectedDriverList:": self._handle_report_connected_driver_list,
|
|
153
|
+
"_rpc_applicationSentListing:": self._handle_application_sent_listing,
|
|
154
|
+
"_rpc_applicationUpdated:": self._handle_application_updated,
|
|
155
|
+
"_rpc_applicationConnected:": self._handle_application_connected,
|
|
156
|
+
"_rpc_applicationSentData:": self._handle_application_sent_data,
|
|
157
|
+
"_rpc_applicationDisconnected:": self._handle_application_disconnected,
|
|
153
158
|
}
|
|
154
159
|
self._recv_task: Optional[asyncio.Task] = None
|
|
155
160
|
|
|
156
|
-
def connect(self, timeout: Union[float, int] = None):
|
|
161
|
+
def connect(self, timeout: Optional[Union[float, int]] = None):
|
|
157
162
|
self.service = self.await_(self.lockdown.aio_start_lockdown_service(self.service_name))
|
|
158
163
|
self.await_(self._report_identifier())
|
|
159
164
|
try:
|
|
160
|
-
self._handle_recv(self.
|
|
165
|
+
self._handle_recv(self._await_with_timeout(self._recv_message(), timeout))
|
|
161
166
|
except asyncio.TimeoutError as e:
|
|
162
167
|
raise WebInspectorNotEnabledError from e
|
|
163
168
|
self._recv_task = self.loop.create_task(self._receiving_task())
|
|
164
169
|
|
|
165
170
|
def close(self):
|
|
166
171
|
self._recv_task.cancel()
|
|
167
|
-
|
|
172
|
+
with contextlib.suppress(asyncio.CancelledError):
|
|
168
173
|
self.await_(self._recv_task)
|
|
169
|
-
except asyncio.CancelledError:
|
|
170
|
-
pass
|
|
171
174
|
self.await_(self.service.aio_close())
|
|
172
175
|
|
|
173
176
|
async def _recv_message(self):
|
|
@@ -182,7 +185,7 @@ class WebinspectorService:
|
|
|
182
185
|
self._handle_recv(await self._recv_message())
|
|
183
186
|
|
|
184
187
|
def automation_session(self, app: Application) -> AutomationSession:
|
|
185
|
-
if self.state ==
|
|
188
|
+
if self.state == "WIRAutomationAvailabilityNotAvailable":
|
|
186
189
|
raise RemoteAutomationNotEnabledError()
|
|
187
190
|
session_id = str(uuid.uuid4()).upper()
|
|
188
191
|
self.await_(self._forward_automation_session_request(session_id, app.id_))
|
|
@@ -196,8 +199,10 @@ class WebinspectorService:
|
|
|
196
199
|
|
|
197
200
|
async def inspector_session(self, app: Application, page: Page) -> InspectorSession:
|
|
198
201
|
session_id = str(uuid.uuid4()).upper()
|
|
199
|
-
return await InspectorSession.create(
|
|
200
|
-
|
|
202
|
+
return await InspectorSession.create(
|
|
203
|
+
SessionProtocol(self, session_id, app, page, method_prefix=""),
|
|
204
|
+
wait_target=page.type_ != WirTypes.JAVASCRIPT,
|
|
205
|
+
)
|
|
201
206
|
|
|
202
207
|
def get_open_pages(self) -> dict:
|
|
203
208
|
apps = {}
|
|
@@ -225,9 +230,9 @@ class WebinspectorService:
|
|
|
225
230
|
self.await_(self._request_application_launch(bundle))
|
|
226
231
|
self.get_open_pages()
|
|
227
232
|
try:
|
|
228
|
-
return self.
|
|
229
|
-
except TimeoutError:
|
|
230
|
-
raise LaunchingApplicationError()
|
|
233
|
+
return self._await_with_timeout(self._wait_for_application(bundle), timeout)
|
|
234
|
+
except TimeoutError as e:
|
|
235
|
+
raise LaunchingApplicationError() from e
|
|
231
236
|
|
|
232
237
|
async def send_socket_data(self, session_id: str, app_id: str, page_id: int, data: dict):
|
|
233
238
|
await self._forward_socket_data(session_id, app_id, page_id, data)
|
|
@@ -244,18 +249,30 @@ class WebinspectorService:
|
|
|
244
249
|
def flush_input(self, duration: Union[float, int] = 0):
|
|
245
250
|
return self.await_(asyncio.sleep(duration))
|
|
246
251
|
|
|
247
|
-
def await_(self, awaitable):
|
|
248
|
-
return self.loop.run_until_complete(
|
|
252
|
+
def await_(self, awaitable: Coroutine) -> Any:
|
|
253
|
+
return self.loop.run_until_complete(awaitable)
|
|
254
|
+
|
|
255
|
+
def _await_with_timeout(self, coro: Coroutine, timeout: Optional[float] = None) -> Any:
|
|
256
|
+
# Create a task explicitly so we're definitely inside a Task
|
|
257
|
+
task = self.loop.create_task(coro)
|
|
258
|
+
done, _pending = self.loop.run_until_complete(asyncio.wait({task}, timeout=timeout))
|
|
259
|
+
if not done:
|
|
260
|
+
task.cancel()
|
|
261
|
+
# Give the task a chance to cancel cleanly
|
|
262
|
+
with contextlib.suppress(asyncio.CancelledError):
|
|
263
|
+
self.loop.run_until_complete(task)
|
|
264
|
+
raise WebInspectorNotEnabledError()
|
|
265
|
+
return task.result()
|
|
249
266
|
|
|
250
267
|
def _handle_recv(self, plist):
|
|
251
|
-
self.receive_handlers[plist[
|
|
268
|
+
self.receive_handlers[plist["__selector"]](plist["__argument"])
|
|
252
269
|
|
|
253
270
|
def _handle_report_current_state(self, arg):
|
|
254
|
-
self.state = arg[
|
|
271
|
+
self.state = arg["WIRAutomationAvailabilityKey"]
|
|
255
272
|
|
|
256
273
|
def _handle_report_connected_application_list(self, arg):
|
|
257
274
|
self.connected_application = {}
|
|
258
|
-
for key, application in arg[
|
|
275
|
+
for key, application in arg["WIRApplicationDictionaryKey"].items():
|
|
259
276
|
self.connected_application[key] = Application.from_application_dictionary(application)
|
|
260
277
|
|
|
261
278
|
# Immediately also query the application pages
|
|
@@ -265,19 +282,19 @@ class WebinspectorService:
|
|
|
265
282
|
pass
|
|
266
283
|
|
|
267
284
|
def _handle_application_sent_listing(self, arg):
|
|
268
|
-
if arg[
|
|
285
|
+
if arg["WIRApplicationIdentifierKey"] in self.application_pages:
|
|
269
286
|
# Update existing application pages
|
|
270
|
-
for id_, page in arg[
|
|
271
|
-
if id_ in self.application_pages[arg[
|
|
272
|
-
self.application_pages[arg[
|
|
287
|
+
for id_, page in arg["WIRListingKey"].items():
|
|
288
|
+
if id_ in self.application_pages[arg["WIRApplicationIdentifierKey"]]:
|
|
289
|
+
self.application_pages[arg["WIRApplicationIdentifierKey"]][id_].update(page)
|
|
273
290
|
else:
|
|
274
|
-
self.application_pages[arg[
|
|
291
|
+
self.application_pages[arg["WIRApplicationIdentifierKey"]][id_] = Page.from_page_dictionary(page)
|
|
275
292
|
else:
|
|
276
293
|
# Add new application pages
|
|
277
294
|
pages = {}
|
|
278
|
-
for id_, page in arg[
|
|
295
|
+
for id_, page in arg["WIRListingKey"].items():
|
|
279
296
|
pages[id_] = Page.from_page_dictionary(page)
|
|
280
|
-
self.application_pages[arg[
|
|
297
|
+
self.application_pages[arg["WIRApplicationIdentifierKey"]] = pages
|
|
281
298
|
|
|
282
299
|
def _handle_application_updated(self, arg):
|
|
283
300
|
app = Application.from_application_dictionary(arg)
|
|
@@ -288,72 +305,81 @@ class WebinspectorService:
|
|
|
288
305
|
self.connected_application[app.id_] = app
|
|
289
306
|
|
|
290
307
|
def _handle_application_sent_data(self, arg):
|
|
291
|
-
response = json.loads(arg[
|
|
308
|
+
response = json.loads(arg["WIRMessageDataKey"])
|
|
292
309
|
|
|
293
|
-
if
|
|
294
|
-
self.wir_message_results[response[
|
|
310
|
+
if "id" in response:
|
|
311
|
+
self.wir_message_results[response["id"]] = response
|
|
295
312
|
else:
|
|
296
313
|
self.wir_events.append(response)
|
|
297
314
|
|
|
298
315
|
def _handle_application_disconnected(self, arg):
|
|
299
|
-
self.connected_application.pop(arg[
|
|
300
|
-
self.application_pages.pop(arg[
|
|
316
|
+
self.connected_application.pop(arg["WIRApplicationIdentifierKey"], None)
|
|
317
|
+
self.application_pages.pop(arg["WIRApplicationIdentifierKey"], None)
|
|
301
318
|
|
|
302
319
|
async def _report_identifier(self):
|
|
303
|
-
await self._send_message(
|
|
320
|
+
await self._send_message("_rpc_reportIdentifier:")
|
|
304
321
|
|
|
305
322
|
async def _forward_get_listing(self, app_id):
|
|
306
|
-
self.logger.debug(f
|
|
307
|
-
await self._send_message(
|
|
323
|
+
self.logger.debug(f"Listing app with id {app_id}")
|
|
324
|
+
await self._send_message("_rpc_forwardGetListing:", {"WIRApplicationIdentifierKey": app_id})
|
|
308
325
|
|
|
309
326
|
async def _request_application_launch(self, bundle: str):
|
|
310
|
-
await self._send_message(
|
|
327
|
+
await self._send_message("_rpc_requestApplicationLaunch:", {"WIRApplicationBundleIdentifierKey": bundle})
|
|
311
328
|
|
|
312
329
|
async def _get_connected_applications(self) -> None:
|
|
313
|
-
await self._send_message(
|
|
330
|
+
await self._send_message("_rpc_getConnectedApplications:", {})
|
|
314
331
|
|
|
315
332
|
async def _forward_automation_session_request(self, session_id: str, app_id: str):
|
|
316
|
-
await self._send_message(
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
333
|
+
await self._send_message(
|
|
334
|
+
"_rpc_forwardAutomationSessionRequest:",
|
|
335
|
+
{
|
|
336
|
+
"WIRApplicationIdentifierKey": app_id,
|
|
337
|
+
"WIRSessionCapabilitiesKey": {
|
|
338
|
+
"org.webkit.webdriver.webrtc.allow-insecure-media-capture": True,
|
|
339
|
+
"org.webkit.webdriver.webrtc.suppress-ice-candidate-filtering": False,
|
|
340
|
+
},
|
|
341
|
+
"WIRSessionIdentifierKey": session_id,
|
|
321
342
|
},
|
|
322
|
-
|
|
323
|
-
})
|
|
343
|
+
)
|
|
324
344
|
|
|
325
345
|
async def _forward_socket_setup(self, session_id: str, app_id: str, page_id: int, pause: bool = True):
|
|
326
346
|
message = {
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
347
|
+
"WIRApplicationIdentifierKey": app_id,
|
|
348
|
+
"WIRPageIdentifierKey": page_id,
|
|
349
|
+
"WIRSenderKey": session_id,
|
|
350
|
+
"WIRMessageDataTypeChunkSupportedKey": 0,
|
|
331
351
|
}
|
|
332
352
|
if not pause:
|
|
333
|
-
message[
|
|
334
|
-
await self._send_message(
|
|
353
|
+
message["WIRAutomaticallyPause"] = False
|
|
354
|
+
await self._send_message("_rpc_forwardSocketSetup:", message)
|
|
335
355
|
|
|
336
356
|
async def _forward_socket_data(self, session_id: str, app_id: str, page_id: int, data: dict):
|
|
337
|
-
await self._send_message(
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
357
|
+
await self._send_message(
|
|
358
|
+
"_rpc_forwardSocketData:",
|
|
359
|
+
{
|
|
360
|
+
"WIRApplicationIdentifierKey": app_id,
|
|
361
|
+
"WIRPageIdentifierKey": page_id,
|
|
362
|
+
"WIRSessionIdentifierKey": session_id,
|
|
363
|
+
"WIRSenderKey": session_id,
|
|
364
|
+
"WIRSocketDataKey": json.dumps(data).encode(),
|
|
365
|
+
},
|
|
366
|
+
)
|
|
344
367
|
|
|
345
368
|
async def _forward_indicate_web_view(self, app_id: str, page_id: int, enable: bool):
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
369
|
+
(
|
|
370
|
+
await self._send_message("_rpc_forwardIndicateWebView"),
|
|
371
|
+
{
|
|
372
|
+
"WIRApplicationIdentifierKey": app_id,
|
|
373
|
+
"WIRPageIdentifierKey": page_id,
|
|
374
|
+
"WIRIndicateEnabledKey": enable,
|
|
375
|
+
},
|
|
376
|
+
)
|
|
351
377
|
|
|
352
378
|
async def _send_message(self, selector: str, args=None):
|
|
353
379
|
if args is None:
|
|
354
380
|
args = {}
|
|
355
|
-
args[
|
|
356
|
-
await self.service.aio_send_plist({
|
|
381
|
+
args["WIRConnectionIdentifierKey"] = self.connection_id
|
|
382
|
+
await self.service.aio_send_plist({"__selector": selector, "__argument": args})
|
|
357
383
|
|
|
358
384
|
def _page_by_automation_session(self, session_id: str) -> Page:
|
|
359
385
|
for app_id in self.application_pages:
|
|
@@ -369,7 +395,7 @@ class WebinspectorService:
|
|
|
369
395
|
return page
|
|
370
396
|
await asyncio.sleep(0)
|
|
371
397
|
|
|
372
|
-
async def _wait_for_application(self, bundle: str =
|
|
398
|
+
async def _wait_for_application(self, bundle: str = "", app_id: str = "") -> Application:
|
|
373
399
|
while True:
|
|
374
400
|
for app in self.connected_application.values():
|
|
375
401
|
if bundle and app.bundle == bundle:
|
pymobiledevice3/tcp_forwarder.py
CHANGED
|
@@ -19,7 +19,7 @@ class TcpForwarderBase:
|
|
|
19
19
|
MAX_FORWARDED_CONNECTIONS = 200
|
|
20
20
|
TIMEOUT = 1
|
|
21
21
|
|
|
22
|
-
def __init__(self, src_port: int, listening_event: threading.Event = None):
|
|
22
|
+
def __init__(self, src_port: int, listening_event: Optional[threading.Event] = None):
|
|
23
23
|
"""
|
|
24
24
|
Initialize a new tcp forwarder
|
|
25
25
|
|
|
@@ -38,8 +38,8 @@ class TcpForwarderBase:
|
|
|
38
38
|
# socket to its remote socket and vice versa
|
|
39
39
|
self.connections = {}
|
|
40
40
|
|
|
41
|
-
def start(self, address=
|
|
42
|
-
"""
|
|
41
|
+
def start(self, address="0.0.0.0"):
|
|
42
|
+
"""forward each connection from given local machine port to remote device port"""
|
|
43
43
|
# create local tcp server socket
|
|
44
44
|
self.server_socket = socket.socket()
|
|
45
45
|
self.server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
|
|
@@ -55,7 +55,7 @@ class TcpForwarderBase:
|
|
|
55
55
|
while self.inputs:
|
|
56
56
|
# will only perform the socket select on the inputs. the outputs will handled
|
|
57
57
|
# as synchronous blocking
|
|
58
|
-
readable,
|
|
58
|
+
readable, _writable, exceptional = select.select(self.inputs, [], self.inputs, self.TIMEOUT)
|
|
59
59
|
if self.stopped.is_set():
|
|
60
60
|
self.logger.debug("Closing since stopped is set")
|
|
61
61
|
break
|
|
@@ -70,7 +70,7 @@ class TcpForwarderBase:
|
|
|
70
70
|
try:
|
|
71
71
|
self._handle_data(current_sock, closed_sockets)
|
|
72
72
|
except ConnectionResetError:
|
|
73
|
-
self.logger.
|
|
73
|
+
self.logger.error("Error when handling data")
|
|
74
74
|
self._handle_close_or_error(current_sock)
|
|
75
75
|
else:
|
|
76
76
|
self.logger.debug("Is closed")
|
|
@@ -85,7 +85,7 @@ class TcpForwarderBase:
|
|
|
85
85
|
current_sock.close()
|
|
86
86
|
|
|
87
87
|
def _handle_close_or_error(self, from_sock):
|
|
88
|
-
"""
|
|
88
|
+
"""if an error occurred its time to close the two sockets"""
|
|
89
89
|
other_sock = self.connections[from_sock]
|
|
90
90
|
|
|
91
91
|
other_sock.close()
|
|
@@ -93,7 +93,7 @@ class TcpForwarderBase:
|
|
|
93
93
|
self.inputs.remove(other_sock)
|
|
94
94
|
self.inputs.remove(from_sock)
|
|
95
95
|
|
|
96
|
-
self.logger.info(f
|
|
96
|
+
self.logger.info(f"connection {other_sock} was closed")
|
|
97
97
|
|
|
98
98
|
def _handle_data(self, from_sock, closed_sockets):
|
|
99
99
|
self.logger.debug(f"Handling data from {from_sock}")
|
|
@@ -104,8 +104,8 @@ class TcpForwarderBase:
|
|
|
104
104
|
except BlockingIOError:
|
|
105
105
|
self.logger.warning(f"Non-blocking read failed on {from_sock}, retrying later.")
|
|
106
106
|
return
|
|
107
|
-
except OSError
|
|
108
|
-
self.logger.error(f"Error reading from socket {from_sock}
|
|
107
|
+
except OSError:
|
|
108
|
+
self.logger.error(f"Error reading from socket {from_sock}")
|
|
109
109
|
self._handle_close_or_error(from_sock)
|
|
110
110
|
closed_sockets.add(from_sock)
|
|
111
111
|
return
|
|
@@ -127,8 +127,8 @@ class TcpForwarderBase:
|
|
|
127
127
|
except BrokenPipeError:
|
|
128
128
|
self.logger.error(f"Broken pipe error on {other_sock}.")
|
|
129
129
|
raise
|
|
130
|
-
except OSError
|
|
131
|
-
self.logger.error(
|
|
130
|
+
except OSError:
|
|
131
|
+
self.logger.error("Unhandled error while forwarding data")
|
|
132
132
|
self._handle_close_or_error(from_sock)
|
|
133
133
|
closed_sockets.add(from_sock)
|
|
134
134
|
closed_sockets.add(other_sock)
|
|
@@ -138,14 +138,14 @@ class TcpForwarderBase:
|
|
|
138
138
|
pass
|
|
139
139
|
|
|
140
140
|
def _handle_server_connection(self):
|
|
141
|
-
"""
|
|
142
|
-
local_connection,
|
|
141
|
+
"""accept the connection from local machine and attempt to connect at remote"""
|
|
142
|
+
local_connection, _client_address = self.server_socket.accept()
|
|
143
143
|
local_connection.setblocking(False)
|
|
144
144
|
|
|
145
145
|
try:
|
|
146
146
|
remote_connection = self._establish_remote_connection()
|
|
147
147
|
except ConnectionFailedError:
|
|
148
|
-
self.logger.error(f
|
|
148
|
+
self.logger.error(f"failed to connect to port: {self.dst_port}")
|
|
149
149
|
local_connection.close()
|
|
150
150
|
return
|
|
151
151
|
|
|
@@ -159,10 +159,10 @@ class TcpForwarderBase:
|
|
|
159
159
|
self.connections[remote_connection] = local_connection
|
|
160
160
|
self.connections[local_connection] = remote_connection
|
|
161
161
|
|
|
162
|
-
self.logger.info(
|
|
162
|
+
self.logger.info("connection established from local to remote")
|
|
163
163
|
|
|
164
164
|
def stop(self):
|
|
165
|
-
"""
|
|
165
|
+
"""stop forwarding"""
|
|
166
166
|
self.stopped.set()
|
|
167
167
|
|
|
168
168
|
|
|
@@ -171,8 +171,15 @@ class UsbmuxTcpForwarder(TcpForwarderBase):
|
|
|
171
171
|
Allows forwarding local tcp connection into the device via a given lockdown connection
|
|
172
172
|
"""
|
|
173
173
|
|
|
174
|
-
def __init__(
|
|
175
|
-
|
|
174
|
+
def __init__(
|
|
175
|
+
self,
|
|
176
|
+
serial: str,
|
|
177
|
+
dst_port: int,
|
|
178
|
+
src_port: int,
|
|
179
|
+
listening_event: Optional[threading.Event] = None,
|
|
180
|
+
usbmux_connection_type: Optional[str] = None,
|
|
181
|
+
usbmux_address: Optional[str] = None,
|
|
182
|
+
):
|
|
176
183
|
"""
|
|
177
184
|
Initialize a new tcp forwarder
|
|
178
185
|
|
|
@@ -191,8 +198,9 @@ class UsbmuxTcpForwarder(TcpForwarderBase):
|
|
|
191
198
|
|
|
192
199
|
def _establish_remote_connection(self) -> socket.socket:
|
|
193
200
|
# connect directly using usbmuxd
|
|
194
|
-
mux_device = usbmux.select_device(
|
|
195
|
-
|
|
201
|
+
mux_device = usbmux.select_device(
|
|
202
|
+
self.serial, connection_type=self.usbmux_connection_type, usbmux_address=self.usbmux_address
|
|
203
|
+
)
|
|
196
204
|
self.logger.debug("Selected device: %r", mux_device)
|
|
197
205
|
if mux_device is None:
|
|
198
206
|
raise ConnectionFailedError()
|
|
@@ -204,8 +212,13 @@ class LockdownTcpForwarder(TcpForwarderBase):
|
|
|
204
212
|
Allows forwarding local tcp connection into the device via a given lockdown connection
|
|
205
213
|
"""
|
|
206
214
|
|
|
207
|
-
def __init__(
|
|
208
|
-
|
|
215
|
+
def __init__(
|
|
216
|
+
self,
|
|
217
|
+
service_provider: LockdownServiceProvider,
|
|
218
|
+
src_port: int,
|
|
219
|
+
service_name: str,
|
|
220
|
+
listening_event: Optional[threading.Event] = None,
|
|
221
|
+
):
|
|
209
222
|
"""
|
|
210
223
|
Initialize a new tcp forwarder
|
|
211
224
|
|