pymobiledevice3 4.14.6__py3-none-any.whl → 7.0.6__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
- misc/understanding_idevice_protocol_layers.md +15 -10
- pymobiledevice3/__main__.py +317 -127
- pymobiledevice3/_version.py +22 -4
- pymobiledevice3/bonjour.py +358 -113
- pymobiledevice3/ca.py +253 -16
- pymobiledevice3/cli/activation.py +31 -23
- pymobiledevice3/cli/afc.py +49 -40
- pymobiledevice3/cli/amfi.py +16 -21
- pymobiledevice3/cli/apps.py +87 -42
- pymobiledevice3/cli/backup.py +160 -90
- pymobiledevice3/cli/bonjour.py +44 -40
- pymobiledevice3/cli/cli_common.py +204 -198
- pymobiledevice3/cli/companion_proxy.py +14 -14
- pymobiledevice3/cli/crash.py +105 -56
- pymobiledevice3/cli/developer/__init__.py +62 -0
- pymobiledevice3/cli/developer/accessibility/__init__.py +65 -0
- pymobiledevice3/cli/developer/accessibility/settings.py +43 -0
- pymobiledevice3/cli/developer/arbitration.py +50 -0
- pymobiledevice3/cli/developer/condition.py +33 -0
- pymobiledevice3/cli/developer/core_device.py +294 -0
- pymobiledevice3/cli/developer/debugserver.py +244 -0
- pymobiledevice3/cli/developer/dvt/__init__.py +438 -0
- pymobiledevice3/cli/developer/dvt/core_profile_session.py +295 -0
- pymobiledevice3/cli/developer/dvt/simulate_location.py +56 -0
- pymobiledevice3/cli/developer/dvt/sysmon/__init__.py +69 -0
- pymobiledevice3/cli/developer/dvt/sysmon/process.py +188 -0
- pymobiledevice3/cli/developer/fetch_symbols.py +108 -0
- pymobiledevice3/cli/developer/simulate_location.py +51 -0
- pymobiledevice3/cli/diagnostics/__init__.py +75 -0
- pymobiledevice3/cli/diagnostics/battery.py +47 -0
- pymobiledevice3/cli/idam.py +42 -0
- pymobiledevice3/cli/lockdown.py +108 -103
- pymobiledevice3/cli/mounter.py +158 -99
- pymobiledevice3/cli/notification.py +38 -26
- pymobiledevice3/cli/pcap.py +45 -24
- pymobiledevice3/cli/power_assertion.py +18 -17
- pymobiledevice3/cli/processes.py +17 -23
- pymobiledevice3/cli/profile.py +165 -109
- pymobiledevice3/cli/provision.py +35 -34
- pymobiledevice3/cli/remote.py +217 -129
- pymobiledevice3/cli/restore.py +159 -143
- pymobiledevice3/cli/springboard.py +63 -53
- pymobiledevice3/cli/syslog.py +193 -86
- pymobiledevice3/cli/usbmux.py +73 -33
- pymobiledevice3/cli/version.py +5 -7
- pymobiledevice3/cli/webinspector.py +376 -214
- pymobiledevice3/common.py +3 -1
- pymobiledevice3/exceptions.py +182 -58
- pymobiledevice3/irecv.py +52 -53
- pymobiledevice3/irecv_devices.py +1489 -464
- pymobiledevice3/lockdown.py +473 -275
- pymobiledevice3/lockdown_service_provider.py +15 -8
- pymobiledevice3/osu/os_utils.py +27 -9
- pymobiledevice3/osu/posix_util.py +34 -15
- pymobiledevice3/osu/win_util.py +14 -8
- pymobiledevice3/pair_records.py +102 -21
- pymobiledevice3/remote/common.py +8 -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 +19 -4
- pymobiledevice3/remote/core_device/file_service.py +53 -23
- pymobiledevice3/remote/remote_service_discovery.py +79 -45
- pymobiledevice3/remote/remotexpc.py +73 -44
- pymobiledevice3/remote/tunnel_service.py +442 -317
- 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 +20 -16
- pymobiledevice3/resources/notifications.txt +144 -0
- pymobiledevice3/restore/asr.py +27 -27
- pymobiledevice3/restore/base_restore.py +110 -21
- pymobiledevice3/restore/consts.py +87 -66
- pymobiledevice3/restore/device.py +59 -12
- pymobiledevice3/restore/fdr.py +46 -48
- pymobiledevice3/restore/ftab.py +19 -19
- pymobiledevice3/restore/img4.py +163 -0
- pymobiledevice3/restore/mbn.py +587 -0
- pymobiledevice3/restore/recovery.py +151 -151
- pymobiledevice3/restore/restore.py +562 -544
- pymobiledevice3/restore/restore_options.py +131 -110
- pymobiledevice3/restore/restored_client.py +51 -31
- pymobiledevice3/restore/tss.py +385 -267
- pymobiledevice3/service_connection.py +252 -59
- pymobiledevice3/services/accessibilityaudit.py +202 -120
- pymobiledevice3/services/afc.py +962 -365
- pymobiledevice3/services/amfi.py +24 -30
- pymobiledevice3/services/companion.py +23 -19
- pymobiledevice3/services/crash_reports.py +71 -47
- pymobiledevice3/services/debugserver_applist.py +3 -3
- pymobiledevice3/services/device_arbitration.py +8 -8
- pymobiledevice3/services/device_link.py +101 -79
- pymobiledevice3/services/diagnostics.py +973 -967
- 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 +20 -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 +35 -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 +9 -8
- pymobiledevice3/services/house_arrest.py +16 -15
- pymobiledevice3/services/idam.py +20 -0
- pymobiledevice3/services/installation_proxy.py +173 -81
- pymobiledevice3/services/lockdown_service.py +20 -10
- pymobiledevice3/services/misagent.py +22 -19
- pymobiledevice3/services/mobile_activation.py +147 -64
- pymobiledevice3/services/mobile_config.py +331 -294
- pymobiledevice3/services/mobile_image_mounter.py +141 -113
- pymobiledevice3/services/mobilebackup2.py +203 -145
- pymobiledevice3/services/notification_proxy.py +11 -11
- pymobiledevice3/services/os_trace.py +134 -74
- pymobiledevice3/services/pcapd.py +314 -302
- pymobiledevice3/services/power_assertion.py +10 -9
- pymobiledevice3/services/preboard.py +4 -4
- pymobiledevice3/services/remote_fetch_symbols.py +21 -14
- pymobiledevice3/services/remote_server.py +176 -146
- pymobiledevice3/services/restore_service.py +16 -16
- pymobiledevice3/services/screenshot.py +15 -12
- 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 +11 -11
- pymobiledevice3/services/web_protocol/automation_session.py +251 -239
- pymobiledevice3/services/web_protocol/cdp_screencast.py +46 -37
- pymobiledevice3/services/web_protocol/cdp_server.py +19 -19
- pymobiledevice3/services/web_protocol/cdp_target.py +411 -373
- pymobiledevice3/services/web_protocol/driver.py +114 -111
- pymobiledevice3/services/web_protocol/element.py +124 -111
- pymobiledevice3/services/web_protocol/inspector_session.py +106 -102
- pymobiledevice3/services/web_protocol/selenium_api.py +49 -49
- pymobiledevice3/services/web_protocol/session_protocol.py +18 -12
- pymobiledevice3/services/web_protocol/switch_to.py +30 -27
- pymobiledevice3/services/webinspector.py +189 -155
- pymobiledevice3/tcp_forwarder.py +87 -69
- pymobiledevice3/tunneld/__init__.py +0 -0
- pymobiledevice3/tunneld/api.py +63 -0
- pymobiledevice3/tunneld/server.py +603 -0
- pymobiledevice3/usbmux.py +198 -147
- pymobiledevice3/utils.py +14 -11
- {pymobiledevice3-4.14.6.dist-info → pymobiledevice3-7.0.6.dist-info}/METADATA +55 -28
- pymobiledevice3-7.0.6.dist-info/RECORD +188 -0
- {pymobiledevice3-4.14.6.dist-info → pymobiledevice3-7.0.6.dist-info}/WHEEL +1 -1
- pymobiledevice3/cli/developer.py +0 -1215
- pymobiledevice3/cli/diagnostics.py +0 -99
- pymobiledevice3/tunneld.py +0 -524
- pymobiledevice3-4.14.6.dist-info/RECORD +0 -168
- {pymobiledevice3-4.14.6.dist-info → pymobiledevice3-7.0.6.dist-info}/entry_points.txt +0 -0
- {pymobiledevice3-4.14.6.dist-info → pymobiledevice3-7.0.6.dist-info/licenses}/LICENSE +0 -0
- {pymobiledevice3-4.14.6.dist-info → pymobiledevice3-7.0.6.dist-info}/top_level.txt +0 -0
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import datetime
|
|
2
2
|
import logging
|
|
3
3
|
from abc import abstractmethod
|
|
4
|
-
from typing import Optional
|
|
4
|
+
from typing import Any, Optional
|
|
5
5
|
|
|
6
6
|
from pymobiledevice3.exceptions import StartServiceError
|
|
7
7
|
from pymobiledevice3.service_connection import ServiceConnection
|
|
@@ -17,6 +17,11 @@ class LockdownServiceProvider:
|
|
|
17
17
|
def product_version(self) -> str:
|
|
18
18
|
pass
|
|
19
19
|
|
|
20
|
+
@property
|
|
21
|
+
@abstractmethod
|
|
22
|
+
def product_build_version(self) -> str:
|
|
23
|
+
pass
|
|
24
|
+
|
|
20
25
|
@property
|
|
21
26
|
@abstractmethod
|
|
22
27
|
def ecid(self) -> int:
|
|
@@ -41,17 +46,19 @@ class LockdownServiceProvider:
|
|
|
41
46
|
pass
|
|
42
47
|
|
|
43
48
|
@abstractmethod
|
|
44
|
-
async def aio_start_lockdown_service(
|
|
45
|
-
|
|
49
|
+
async def aio_start_lockdown_service(self, name: str, include_escrow_bag: bool = False) -> ServiceConnection:
|
|
50
|
+
pass
|
|
51
|
+
|
|
52
|
+
@abstractmethod
|
|
53
|
+
def get_value(self, domain: Optional[str] = None, key: Optional[str] = None) -> Any:
|
|
46
54
|
pass
|
|
47
55
|
|
|
48
|
-
def start_lockdown_developer_service(
|
|
49
|
-
self, name: str, include_escrow_bag: bool = False) -> ServiceConnection:
|
|
56
|
+
def start_lockdown_developer_service(self, name: str, include_escrow_bag: bool = False) -> ServiceConnection:
|
|
50
57
|
try:
|
|
51
58
|
return self.start_lockdown_service(name, include_escrow_bag=include_escrow_bag)
|
|
52
59
|
except StartServiceError:
|
|
53
|
-
logging.getLogger(self.__module__).
|
|
54
|
-
|
|
55
|
-
|
|
60
|
+
logging.getLogger(self.__module__).exception(
|
|
61
|
+
"Failed to connect to required service. Make sure DeveloperDiskImage.dmg has been mounted. "
|
|
62
|
+
"You can do so using: pymobiledevice3 mounter mount"
|
|
56
63
|
)
|
|
57
64
|
raise
|
pymobiledevice3/osu/os_utils.py
CHANGED
|
@@ -11,25 +11,38 @@ DEFAULT_INTERVAL_SEC = 3
|
|
|
11
11
|
DEFAULT_MAX_FAILS = 3
|
|
12
12
|
|
|
13
13
|
|
|
14
|
+
def is_wsl() -> bool:
|
|
15
|
+
try:
|
|
16
|
+
with open("/proc/version") as f:
|
|
17
|
+
version_info = f.read()
|
|
18
|
+
return "Microsoft" in version_info or "WSL" in version_info
|
|
19
|
+
except FileNotFoundError:
|
|
20
|
+
return False
|
|
21
|
+
|
|
22
|
+
|
|
14
23
|
class OsUtils:
|
|
15
24
|
_instance = None
|
|
16
25
|
_os_name = None
|
|
17
26
|
|
|
18
27
|
@classmethod
|
|
19
|
-
def create(cls) ->
|
|
28
|
+
def create(cls) -> "OsUtils":
|
|
20
29
|
if cls._instance is None:
|
|
21
30
|
cls._os_name = sys.platform
|
|
22
|
-
if cls._os_name ==
|
|
31
|
+
if cls._os_name == "win32":
|
|
23
32
|
from pymobiledevice3.osu.win_util import Win32
|
|
33
|
+
|
|
24
34
|
cls._instance = Win32()
|
|
25
|
-
elif cls._os_name ==
|
|
35
|
+
elif cls._os_name == "darwin":
|
|
26
36
|
from pymobiledevice3.osu.posix_util import Darwin
|
|
37
|
+
|
|
27
38
|
cls._instance = Darwin()
|
|
28
|
-
elif cls._os_name ==
|
|
29
|
-
from pymobiledevice3.osu.posix_util import Linux
|
|
30
|
-
|
|
31
|
-
|
|
39
|
+
elif cls._os_name == "linux":
|
|
40
|
+
from pymobiledevice3.osu.posix_util import Linux, Wsl
|
|
41
|
+
|
|
42
|
+
cls._instance = Wsl() if is_wsl() else Linux()
|
|
43
|
+
elif cls._os_name == "cygwin":
|
|
32
44
|
from pymobiledevice3.osu.posix_util import Cygwin
|
|
45
|
+
|
|
33
46
|
cls._instance = Cygwin()
|
|
34
47
|
else:
|
|
35
48
|
raise OSNotSupportedError(cls._os_name)
|
|
@@ -62,8 +75,13 @@ class OsUtils:
|
|
|
62
75
|
def get_ipv6_ips(self) -> list[str]:
|
|
63
76
|
raise FeatureNotSupportedError(self._os_name, inspect.currentframe().f_code.co_name)
|
|
64
77
|
|
|
65
|
-
def set_keepalive(
|
|
66
|
-
|
|
78
|
+
def set_keepalive(
|
|
79
|
+
self,
|
|
80
|
+
sock: socket.socket,
|
|
81
|
+
after_idle_sec: int = DEFAULT_AFTER_IDLE_SEC,
|
|
82
|
+
interval_sec: int = DEFAULT_INTERVAL_SEC,
|
|
83
|
+
max_fails: int = DEFAULT_MAX_FAILS,
|
|
84
|
+
) -> None:
|
|
67
85
|
raise FeatureNotSupportedError(self._os_name, inspect.currentframe().f_code.co_name)
|
|
68
86
|
|
|
69
87
|
def parse_timestamp(self, time_stamp) -> datetime:
|
|
@@ -33,13 +33,16 @@ class Posix(OsUtils):
|
|
|
33
33
|
return 'This command requires root privileges. Consider retrying with "sudo".'
|
|
34
34
|
|
|
35
35
|
def get_ipv6_ips(self) -> list[str]:
|
|
36
|
-
return [
|
|
37
|
-
|
|
36
|
+
return [
|
|
37
|
+
f"{adapter.ips[0].ip[0]}%{adapter.nice_name}"
|
|
38
|
+
for adapter in get_adapters()
|
|
39
|
+
if adapter.ips[0].is_IPv6 and not adapter.nice_name.startswith("tun")
|
|
40
|
+
]
|
|
38
41
|
|
|
39
42
|
def chown_to_non_sudo_if_needed(self, path: Path) -> None:
|
|
40
|
-
if os.getenv(
|
|
43
|
+
if os.getenv("SUDO_UID") is None:
|
|
41
44
|
return
|
|
42
|
-
os.chown(path, int(os.getenv(
|
|
45
|
+
os.chown(path, int(os.getenv("SUDO_UID")), int(os.getenv("SUDO_GID")))
|
|
43
46
|
|
|
44
47
|
def parse_timestamp(self, time_stamp) -> datetime:
|
|
45
48
|
return datetime.datetime.fromtimestamp(time_stamp)
|
|
@@ -52,14 +55,19 @@ class Posix(OsUtils):
|
|
|
52
55
|
class Darwin(Posix):
|
|
53
56
|
@property
|
|
54
57
|
def pair_record_path(self) -> Path:
|
|
55
|
-
return Path(
|
|
58
|
+
return Path("/var/db/lockdown/")
|
|
56
59
|
|
|
57
60
|
@property
|
|
58
61
|
def loopback_header(self) -> bytes:
|
|
59
|
-
return struct.pack(
|
|
60
|
-
|
|
61
|
-
def set_keepalive(
|
|
62
|
-
|
|
62
|
+
return struct.pack(">I", socket.AF_INET6)
|
|
63
|
+
|
|
64
|
+
def set_keepalive(
|
|
65
|
+
self,
|
|
66
|
+
sock: socket.socket,
|
|
67
|
+
after_idle_sec: int = DEFAULT_AFTER_IDLE_SEC,
|
|
68
|
+
interval_sec: int = DEFAULT_INTERVAL_SEC,
|
|
69
|
+
max_fails: int = DEFAULT_MAX_FAILS,
|
|
70
|
+
) -> None:
|
|
63
71
|
sock.setsockopt(socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1)
|
|
64
72
|
sock.setsockopt(socket.IPPROTO_TCP, _DARWIN_TCP_KEEPALIVE, after_idle_sec)
|
|
65
73
|
sock.setsockopt(socket.IPPROTO_TCP, _DARWIN_TCP_KEEPINTVL, interval_sec)
|
|
@@ -69,24 +77,35 @@ class Darwin(Posix):
|
|
|
69
77
|
class Linux(Posix):
|
|
70
78
|
@property
|
|
71
79
|
def pair_record_path(self) -> Path:
|
|
72
|
-
return Path(
|
|
80
|
+
return Path("/var/lib/lockdown/")
|
|
73
81
|
|
|
74
82
|
@property
|
|
75
83
|
def loopback_header(self) -> bytes:
|
|
76
|
-
return b
|
|
77
|
-
|
|
78
|
-
def set_keepalive(
|
|
79
|
-
|
|
84
|
+
return b"\x00\x00\x86\xdd"
|
|
85
|
+
|
|
86
|
+
def set_keepalive(
|
|
87
|
+
self,
|
|
88
|
+
sock: socket.socket,
|
|
89
|
+
after_idle_sec: int = DEFAULT_AFTER_IDLE_SEC,
|
|
90
|
+
interval_sec: int = DEFAULT_INTERVAL_SEC,
|
|
91
|
+
max_fails: int = DEFAULT_MAX_FAILS,
|
|
92
|
+
) -> None:
|
|
80
93
|
sock.setsockopt(socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1)
|
|
81
94
|
sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_KEEPIDLE, after_idle_sec)
|
|
82
95
|
sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_KEEPINTVL, interval_sec)
|
|
83
96
|
sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_KEEPCNT, max_fails)
|
|
84
97
|
|
|
85
98
|
def get_homedir(self) -> Path:
|
|
86
|
-
return Path(
|
|
99
|
+
return Path("~" + os.environ.get("SUDO_USER", "")).expanduser()
|
|
87
100
|
|
|
88
101
|
|
|
89
102
|
class Cygwin(Posix):
|
|
90
103
|
@property
|
|
91
104
|
def usbmux_address(self) -> tuple[str, int]:
|
|
92
105
|
return MuxConnection.ITUNES_HOST, socket.AF_INET
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
class Wsl(Linux):
|
|
109
|
+
@property
|
|
110
|
+
def usbmux_address(self) -> tuple[str, int]:
|
|
111
|
+
return MuxConnection.ITUNES_HOST, socket.AF_INET
|
pymobiledevice3/osu/win_util.py
CHANGED
|
@@ -13,7 +13,7 @@ from pymobiledevice3.usbmux import MuxConnection
|
|
|
13
13
|
class Win32(OsUtils):
|
|
14
14
|
@property
|
|
15
15
|
def is_admin(self) -> bool:
|
|
16
|
-
"""
|
|
16
|
+
"""Check if the current OS user is an Administrator or root.
|
|
17
17
|
See: https://github.com/Preston-Landers/pyuac/blob/master/pyuac/admin.py
|
|
18
18
|
:return: True if the current user is an 'Administrator', otherwise False.
|
|
19
19
|
"""
|
|
@@ -33,7 +33,7 @@ class Win32(OsUtils):
|
|
|
33
33
|
|
|
34
34
|
@property
|
|
35
35
|
def loopback_header(self) -> bytes:
|
|
36
|
-
return b
|
|
36
|
+
return b"\x00\x00\x86\xdd"
|
|
37
37
|
|
|
38
38
|
@property
|
|
39
39
|
def access_denied_error(self) -> str:
|
|
@@ -41,14 +41,20 @@ class Win32(OsUtils):
|
|
|
41
41
|
|
|
42
42
|
@property
|
|
43
43
|
def pair_record_path(self) -> Path:
|
|
44
|
-
return Path(os.environ.get(
|
|
44
|
+
return Path(os.environ.get("ALLUSERSPROFILE", ""), "Apple", "Lockdown")
|
|
45
45
|
|
|
46
46
|
def get_ipv6_ips(self) -> list[str]:
|
|
47
|
-
return [
|
|
48
|
-
|
|
47
|
+
return [
|
|
48
|
+
f"{adapter.ips[0].ip[0]}%{adapter.ips[0].ip[2]}" for adapter in get_adapters() if adapter.ips[0].is_IPv6
|
|
49
|
+
]
|
|
49
50
|
|
|
50
|
-
def set_keepalive(
|
|
51
|
-
|
|
51
|
+
def set_keepalive(
|
|
52
|
+
self,
|
|
53
|
+
sock: socket.socket,
|
|
54
|
+
after_idle_sec: int = DEFAULT_AFTER_IDLE_SEC,
|
|
55
|
+
interval_sec: int = DEFAULT_INTERVAL_SEC,
|
|
56
|
+
**kwargs,
|
|
57
|
+
) -> None:
|
|
52
58
|
sock.ioctl(socket.SIO_KEEPALIVE_VALS, (1, after_idle_sec * 1000, interval_sec * 1000))
|
|
53
59
|
|
|
54
60
|
def parse_timestamp(self, time_stamp) -> datetime:
|
|
@@ -58,4 +64,4 @@ class Win32(OsUtils):
|
|
|
58
64
|
return
|
|
59
65
|
|
|
60
66
|
def wait_return(self):
|
|
61
|
-
input(
|
|
67
|
+
input("Press ENTER to exit>")
|
pymobiledevice3/pair_records.py
CHANGED
|
@@ -15,19 +15,55 @@ from pymobiledevice3.usbmux import PlistMuxConnection
|
|
|
15
15
|
|
|
16
16
|
logger = logging.getLogger(__name__)
|
|
17
17
|
OSUTILS = get_os_utils()
|
|
18
|
-
PAIRING_RECORD_EXT =
|
|
18
|
+
PAIRING_RECORD_EXT = "plist"
|
|
19
19
|
|
|
20
20
|
|
|
21
|
-
def generate_host_id(hostname: str = None) -> str:
|
|
21
|
+
def generate_host_id(hostname: Optional[str] = None) -> str:
|
|
22
|
+
"""
|
|
23
|
+
Generate a unique host ID based on the hostname.
|
|
24
|
+
|
|
25
|
+
:param hostname: The hostname to use for generating the host ID.
|
|
26
|
+
If None, the current hostname is used.
|
|
27
|
+
:type hostname: str, optional
|
|
28
|
+
:return: The generated host ID.
|
|
29
|
+
:rtype: str
|
|
30
|
+
"""
|
|
22
31
|
hostname = platform.node() if hostname is None else hostname
|
|
23
32
|
host_id = uuid.uuid3(uuid.NAMESPACE_DNS, hostname)
|
|
24
33
|
return str(host_id).upper()
|
|
25
34
|
|
|
26
35
|
|
|
36
|
+
def get_usbmux_pairing_record(identifier: str, usbmux_address: Optional[str] = None):
|
|
37
|
+
"""
|
|
38
|
+
Retrieve the pairing record from usbmuxd.
|
|
39
|
+
|
|
40
|
+
:param identifier: The identifier of the device.
|
|
41
|
+
:type identifier: str
|
|
42
|
+
:param usbmux_address: The address of the usbmuxd server.
|
|
43
|
+
:type usbmux_address: Optional[str], optional
|
|
44
|
+
:return: The pairing record if found, otherwise None.
|
|
45
|
+
:rtype: dict or None
|
|
46
|
+
"""
|
|
47
|
+
with suppress(NotPairedError, MuxException), usbmux.create_mux(usbmux_address=usbmux_address) as mux:
|
|
48
|
+
if isinstance(mux, PlistMuxConnection):
|
|
49
|
+
pair_record = mux.get_pair_record(identifier)
|
|
50
|
+
if pair_record is not None:
|
|
51
|
+
return pair_record
|
|
52
|
+
return None
|
|
53
|
+
|
|
54
|
+
|
|
27
55
|
def get_itunes_pairing_record(identifier: str) -> Optional[dict]:
|
|
28
|
-
|
|
56
|
+
"""
|
|
57
|
+
Retrieve the pairing record from iTunes.
|
|
58
|
+
|
|
59
|
+
:param identifier: The identifier of the device.
|
|
60
|
+
:type identifier: str
|
|
61
|
+
:return: The pairing record if found, otherwise None.
|
|
62
|
+
:rtype: Optional[dict]
|
|
63
|
+
"""
|
|
64
|
+
filename = OSUTILS.pair_record_path / f"{identifier}.plist"
|
|
29
65
|
try:
|
|
30
|
-
with open(filename,
|
|
66
|
+
with open(filename, "rb") as f:
|
|
31
67
|
pair_record = plistlib.load(f)
|
|
32
68
|
except (PermissionError, FileNotFoundError, plistlib.InvalidFileException):
|
|
33
69
|
return None
|
|
@@ -35,30 +71,46 @@ def get_itunes_pairing_record(identifier: str) -> Optional[dict]:
|
|
|
35
71
|
|
|
36
72
|
|
|
37
73
|
def get_local_pairing_record(identifier: str, pairing_records_cache_folder: Path) -> Optional[dict]:
|
|
38
|
-
|
|
39
|
-
|
|
74
|
+
"""
|
|
75
|
+
Retrieve the pairing record from local storage.
|
|
76
|
+
|
|
77
|
+
:param identifier: The identifier of the device.
|
|
78
|
+
:type identifier: str
|
|
79
|
+
:param pairing_records_cache_folder: The path to the local pairing records cache folder.
|
|
80
|
+
:type pairing_records_cache_folder: Path
|
|
81
|
+
:return: The pairing record if found, otherwise None.
|
|
82
|
+
:rtype: Optional[dict]
|
|
83
|
+
"""
|
|
84
|
+
logger.debug("Looking for pymobiledevice3 pairing record")
|
|
85
|
+
path = pairing_records_cache_folder / f"{identifier}.{PAIRING_RECORD_EXT}"
|
|
40
86
|
if not path.exists():
|
|
41
|
-
logger.debug(f
|
|
87
|
+
logger.debug(f"No pymobiledevice3 pairing record found for device {identifier}")
|
|
42
88
|
return None
|
|
43
89
|
return plistlib.loads(path.read_bytes())
|
|
44
90
|
|
|
45
91
|
|
|
46
|
-
def get_preferred_pair_record(
|
|
47
|
-
|
|
92
|
+
def get_preferred_pair_record(
|
|
93
|
+
identifier: str, pairing_records_cache_folder: Path, usbmux_address: Optional[str] = None
|
|
94
|
+
) -> dict:
|
|
48
95
|
"""
|
|
49
|
-
|
|
96
|
+
Look for an existing pair record for the connected device in the following order:
|
|
50
97
|
- usbmuxd
|
|
51
98
|
- iTunes
|
|
52
99
|
- local storage
|
|
53
|
-
"""
|
|
54
100
|
|
|
101
|
+
:param identifier: The identifier of the device.
|
|
102
|
+
:type identifier: str
|
|
103
|
+
:param pairing_records_cache_folder: The path to the local pairing records cache folder.
|
|
104
|
+
:type pairing_records_cache_folder: Path
|
|
105
|
+
:param usbmux_address: The address of the usbmuxd server.
|
|
106
|
+
:type usbmux_address: Optional[str], optional
|
|
107
|
+
:return: The preferred pairing record.
|
|
108
|
+
:rtype: dict
|
|
109
|
+
"""
|
|
55
110
|
# usbmuxd
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
pair_record = mux.get_pair_record(identifier)
|
|
60
|
-
if pair_record is not None:
|
|
61
|
-
return pair_record
|
|
111
|
+
pair_record = get_usbmux_pairing_record(identifier=identifier, usbmux_address=usbmux_address)
|
|
112
|
+
if pair_record is not None:
|
|
113
|
+
return pair_record
|
|
62
114
|
|
|
63
115
|
# iTunes
|
|
64
116
|
pair_record = get_itunes_pairing_record(identifier)
|
|
@@ -69,7 +121,16 @@ def get_preferred_pair_record(identifier: str, pairing_records_cache_folder: Pat
|
|
|
69
121
|
return get_local_pairing_record(identifier, pairing_records_cache_folder)
|
|
70
122
|
|
|
71
123
|
|
|
72
|
-
def create_pairing_records_cache_folder(pairing_records_cache_folder: Path = None) -> Path:
|
|
124
|
+
def create_pairing_records_cache_folder(pairing_records_cache_folder: Optional[Path] = None) -> Path:
|
|
125
|
+
"""
|
|
126
|
+
Create the pairing records cache folder if it does not exist.
|
|
127
|
+
|
|
128
|
+
:param pairing_records_cache_folder: The path to the local pairing records cache folder.
|
|
129
|
+
If None, the home folder is used.
|
|
130
|
+
:type pairing_records_cache_folder: Path, optional
|
|
131
|
+
:return: The path to the pairing records cache folder.
|
|
132
|
+
:rtype: Path
|
|
133
|
+
"""
|
|
73
134
|
if pairing_records_cache_folder is None:
|
|
74
135
|
pairing_records_cache_folder = get_home_folder()
|
|
75
136
|
else:
|
|
@@ -79,13 +140,33 @@ def create_pairing_records_cache_folder(pairing_records_cache_folder: Path = Non
|
|
|
79
140
|
|
|
80
141
|
|
|
81
142
|
def get_remote_pairing_record_filename(identifier: str) -> str:
|
|
82
|
-
|
|
143
|
+
"""
|
|
144
|
+
Generate the filename for the remote pairing record.
|
|
145
|
+
|
|
146
|
+
:param identifier: The identifier of the device.
|
|
147
|
+
:type identifier: str
|
|
148
|
+
:return: The filename for the remote pairing record.
|
|
149
|
+
:rtype: str
|
|
150
|
+
"""
|
|
151
|
+
return f"remote_{identifier}"
|
|
83
152
|
|
|
84
153
|
|
|
85
154
|
def iter_remote_pair_records() -> Generator[Path, None, None]:
|
|
86
|
-
|
|
155
|
+
"""
|
|
156
|
+
Iterate over the remote pairing records in the home folder.
|
|
157
|
+
|
|
158
|
+
:return: A generator yielding paths to the remote pairing records.
|
|
159
|
+
:rtype: Generator[Path, None, None]
|
|
160
|
+
"""
|
|
161
|
+
return get_home_folder().glob("remote_*")
|
|
87
162
|
|
|
88
163
|
|
|
89
164
|
def iter_remote_paired_identifiers() -> Generator[str, None, None]:
|
|
165
|
+
"""
|
|
166
|
+
Iterate over the identifiers of the remote paired devices.
|
|
167
|
+
|
|
168
|
+
:return: A generator yielding the identifiers of the remote paired devices.
|
|
169
|
+
:rtype: Generator[str, None, None]
|
|
170
|
+
"""
|
|
90
171
|
for file in iter_remote_pair_records():
|
|
91
|
-
yield file.parts[-1].split(
|
|
172
|
+
yield file.parts[-1].split("remote_", 1)[1].split(".", 1)[0]
|
pymobiledevice3/remote/common.py
CHANGED
|
@@ -1,11 +1,15 @@
|
|
|
1
|
+
import sys
|
|
1
2
|
from enum import Enum
|
|
2
3
|
|
|
3
4
|
|
|
4
5
|
class ConnectionType(Enum):
|
|
5
|
-
USB =
|
|
6
|
-
WIFI =
|
|
6
|
+
USB = "usb"
|
|
7
|
+
WIFI = "wifi"
|
|
7
8
|
|
|
8
9
|
|
|
9
10
|
class TunnelProtocol(Enum):
|
|
10
|
-
TCP =
|
|
11
|
-
QUIC =
|
|
11
|
+
TCP = "tcp"
|
|
12
|
+
QUIC = "quic"
|
|
13
|
+
|
|
14
|
+
# TODO: make only TCP the default once 3.12 becomes deprecated
|
|
15
|
+
DEFAULT = TCP if sys.version_info >= (3, 13) else QUIC
|
|
@@ -11,45 +11,63 @@ class AppServiceService(CoreDeviceService):
|
|
|
11
11
|
Manage applications
|
|
12
12
|
"""
|
|
13
13
|
|
|
14
|
-
SERVICE_NAME =
|
|
14
|
+
SERVICE_NAME = "com.apple.coredevice.appservice"
|
|
15
15
|
|
|
16
16
|
def __init__(self, rsd: RemoteServiceDiscoveryService):
|
|
17
17
|
super().__init__(rsd, self.SERVICE_NAME)
|
|
18
18
|
|
|
19
|
-
async def list_apps(
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
19
|
+
async def list_apps(
|
|
20
|
+
self,
|
|
21
|
+
include_app_clips: bool = True,
|
|
22
|
+
include_removable_apps: bool = True,
|
|
23
|
+
include_hidden_apps: bool = True,
|
|
24
|
+
include_internal_apps: bool = True,
|
|
25
|
+
include_default_apps: bool = True,
|
|
26
|
+
) -> list[dict]:
|
|
27
|
+
"""List applications"""
|
|
28
|
+
return await self.invoke(
|
|
29
|
+
"com.apple.coredevice.feature.listapps",
|
|
30
|
+
{
|
|
31
|
+
"includeAppClips": include_app_clips,
|
|
32
|
+
"includeRemovableApps": include_removable_apps,
|
|
33
|
+
"includeHiddenApps": include_hidden_apps,
|
|
34
|
+
"includeInternalApps": include_internal_apps,
|
|
35
|
+
"includeDefaultApps": include_default_apps,
|
|
36
|
+
},
|
|
37
|
+
)
|
|
27
38
|
|
|
28
39
|
async def launch_application(
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
40
|
+
self,
|
|
41
|
+
bundle_id: str,
|
|
42
|
+
arguments: Optional[list[str]] = None,
|
|
43
|
+
kill_existing: bool = True,
|
|
44
|
+
start_suspended: bool = False,
|
|
45
|
+
environment: Optional[dict] = None,
|
|
46
|
+
extra_options: Optional[dict] = None,
|
|
47
|
+
) -> list[dict]:
|
|
48
|
+
"""launch application"""
|
|
49
|
+
return await self.invoke(
|
|
50
|
+
"com.apple.coredevice.feature.launchapplication",
|
|
51
|
+
{
|
|
52
|
+
"applicationSpecifier": {
|
|
53
|
+
"bundleIdentifier": {"_0": bundle_id},
|
|
54
|
+
},
|
|
55
|
+
"options": {
|
|
56
|
+
"arguments": arguments if arguments is not None else [],
|
|
57
|
+
"environmentVariables": environment if environment is not None else {},
|
|
58
|
+
"standardIOUsesPseudoterminals": True,
|
|
59
|
+
"startStopped": start_suspended,
|
|
60
|
+
"terminateExisting": kill_existing,
|
|
61
|
+
"user": {"shortName": "mobile"},
|
|
62
|
+
"platformSpecificOptions": plistlib.dumps(extra_options if extra_options is not None else {}),
|
|
63
|
+
},
|
|
64
|
+
"standardIOIdentifiers": {},
|
|
47
65
|
},
|
|
48
|
-
|
|
66
|
+
)
|
|
49
67
|
|
|
50
68
|
async def list_processes(self) -> list[dict]:
|
|
51
|
-
"""
|
|
52
|
-
return (await self.invoke(
|
|
69
|
+
"""List processes"""
|
|
70
|
+
return (await self.invoke("com.apple.coredevice.feature.listprocesses"))["processTokens"]
|
|
53
71
|
|
|
54
72
|
async def list_roots(self) -> dict:
|
|
55
73
|
"""
|
|
@@ -57,10 +75,7 @@ class AppServiceService(CoreDeviceService):
|
|
|
57
75
|
|
|
58
76
|
Can only be performed on certain devices
|
|
59
77
|
"""
|
|
60
|
-
return await self.invoke(
|
|
61
|
-
'rootPoint': {
|
|
62
|
-
'relative': '/'
|
|
63
|
-
}})
|
|
78
|
+
return await self.invoke("com.apple.coredevice.feature.listroots", {"rootPoint": {"relative": "/"}})
|
|
64
79
|
|
|
65
80
|
async def spawn_executable(self, executable: str, arguments: list[str]) -> dict:
|
|
66
81
|
"""
|
|
@@ -68,26 +83,29 @@ class AppServiceService(CoreDeviceService):
|
|
|
68
83
|
|
|
69
84
|
Can only be performed on certain devices
|
|
70
85
|
"""
|
|
71
|
-
return await self.invoke(
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
86
|
+
return await self.invoke(
|
|
87
|
+
"com.apple.coredevice.feature.spawnexecutable",
|
|
88
|
+
{
|
|
89
|
+
"executableItem": {
|
|
90
|
+
"url": {
|
|
91
|
+
"_0": {
|
|
92
|
+
"relative": executable,
|
|
93
|
+
},
|
|
94
|
+
}
|
|
95
|
+
},
|
|
96
|
+
"standardIOIdentifiers": {},
|
|
97
|
+
"options": {
|
|
98
|
+
"arguments": arguments,
|
|
99
|
+
"environmentVariables": {},
|
|
100
|
+
"standardIOUsesPseudoterminals": True,
|
|
101
|
+
"startStopped": False,
|
|
102
|
+
"user": {
|
|
103
|
+
"active": True,
|
|
76
104
|
},
|
|
77
|
-
|
|
78
|
-
},
|
|
79
|
-
'standardIOIdentifiers': {},
|
|
80
|
-
'options': {
|
|
81
|
-
'arguments': arguments,
|
|
82
|
-
'environmentVariables': {},
|
|
83
|
-
'standardIOUsesPseudoterminals': True,
|
|
84
|
-
'startStopped': False,
|
|
85
|
-
'user': {
|
|
86
|
-
'active': True,
|
|
105
|
+
"platformSpecificOptions": plistlib.dumps({}),
|
|
87
106
|
},
|
|
88
|
-
'platformSpecificOptions': plistlib.dumps({}),
|
|
89
107
|
},
|
|
90
|
-
|
|
108
|
+
)
|
|
91
109
|
|
|
92
110
|
async def monitor_process_termination(self, pid: int) -> dict:
|
|
93
111
|
"""
|
|
@@ -95,33 +113,42 @@ class AppServiceService(CoreDeviceService):
|
|
|
95
113
|
|
|
96
114
|
Can only be performed on certain devices
|
|
97
115
|
"""
|
|
98
|
-
return await self.invoke(
|
|
99
|
-
|
|
116
|
+
return await self.invoke(
|
|
117
|
+
"com.apple.coredevice.feature.monitorprocesstermination",
|
|
118
|
+
{"processToken": {"processIdentifier": XpcInt64Type(pid)}},
|
|
119
|
+
)
|
|
100
120
|
|
|
101
121
|
async def uninstall_app(self, bundle_identifier: str) -> None:
|
|
102
122
|
"""
|
|
103
123
|
Uninstall given application by its bundle identifier
|
|
104
124
|
"""
|
|
105
|
-
await self.invoke(
|
|
125
|
+
await self.invoke("com.apple.coredevice.feature.uninstallapp", {"bundleIdentifier": bundle_identifier})
|
|
106
126
|
|
|
107
127
|
async def send_signal_to_process(self, pid: int, signal: int) -> dict:
|
|
108
128
|
"""
|
|
109
129
|
Send signal to given process by its pid
|
|
110
130
|
"""
|
|
111
|
-
return await self.invoke(
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
131
|
+
return await self.invoke(
|
|
132
|
+
"com.apple.coredevice.feature.sendsignaltoprocess",
|
|
133
|
+
{
|
|
134
|
+
"process": {"processIdentifier": XpcInt64Type(pid)},
|
|
135
|
+
"signal": XpcInt64Type(signal),
|
|
136
|
+
},
|
|
137
|
+
)
|
|
115
138
|
|
|
116
|
-
async def fetch_icons(
|
|
117
|
-
|
|
139
|
+
async def fetch_icons(
|
|
140
|
+
self, bundle_identifier: str, width: float, height: float, scale: float, allow_placeholder: bool
|
|
141
|
+
) -> dict:
|
|
118
142
|
"""
|
|
119
143
|
Fetch given application's icons
|
|
120
144
|
"""
|
|
121
|
-
return await self.invoke(
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
145
|
+
return await self.invoke(
|
|
146
|
+
"com.apple.coredevice.feature.fetchappicons",
|
|
147
|
+
{
|
|
148
|
+
"width": width,
|
|
149
|
+
"height": height,
|
|
150
|
+
"scale": scale,
|
|
151
|
+
"allowPlaceholder": allow_placeholder,
|
|
152
|
+
"bundleIdentifier": bundle_identifier,
|
|
153
|
+
},
|
|
154
|
+
)
|