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/services/afc.py
CHANGED
|
@@ -34,97 +34,116 @@ from pymobiledevice3.lockdown_service_provider import LockdownServiceProvider
|
|
|
34
34
|
from pymobiledevice3.services.lockdown_service import LockdownService
|
|
35
35
|
from pymobiledevice3.utils import try_decode
|
|
36
36
|
|
|
37
|
-
MAXIMUM_READ_SIZE = 4 * 1024
|
|
37
|
+
MAXIMUM_READ_SIZE = 4 * 1024**2 # 4 MB
|
|
38
38
|
MODE_MASK = 0o0000777
|
|
39
39
|
|
|
40
|
-
StatResult = namedtuple(
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
FILE_OPEN_RES=0x0000000e, # FileRefOpenResult */
|
|
59
|
-
READ=0x0000000f, # FileRefRead */
|
|
60
|
-
WRITE=0x00000010, # FileRefWrite */
|
|
61
|
-
FILE_SEEK=0x00000011, # FileRefSeek */
|
|
62
|
-
FILE_TELL=0x00000012, # FileRefTell */
|
|
63
|
-
FILE_TELL_RES=0x00000013, # FileRefTellResult */
|
|
64
|
-
FILE_CLOSE=0x00000014, # FileRefClose */
|
|
65
|
-
FILE_SET_SIZE=0x00000015, # FileRefSetFileSize (ftruncate) */
|
|
66
|
-
GET_CON_INFO=0x00000016, # GetConnectionInfo */
|
|
67
|
-
SET_CON_OPTIONS=0x00000017, # SetConnectionOptions */
|
|
68
|
-
RENAME_PATH=0x00000018, # RenamePath */
|
|
69
|
-
SET_FS_BS=0x00000019, # SetFSBlockSize (0x800000) */
|
|
70
|
-
SET_SOCKET_BS=0x0000001A, # SetSocketBlockSize (0x800000) */
|
|
71
|
-
FILE_LOCK=0x0000001B, # FileRefLock */
|
|
72
|
-
MAKE_LINK=0x0000001C, # MakeLink */
|
|
73
|
-
SET_FILE_TIME=0x0000001E, # set st_mtime */
|
|
74
|
-
)
|
|
40
|
+
StatResult = namedtuple(
|
|
41
|
+
"StatResult",
|
|
42
|
+
[
|
|
43
|
+
"st_mode",
|
|
44
|
+
"st_ino",
|
|
45
|
+
"st_dev",
|
|
46
|
+
"st_nlink",
|
|
47
|
+
"st_uid",
|
|
48
|
+
"st_gid",
|
|
49
|
+
"st_size",
|
|
50
|
+
"st_atime",
|
|
51
|
+
"st_mtime",
|
|
52
|
+
"st_ctime",
|
|
53
|
+
"st_blocks",
|
|
54
|
+
"st_blksize",
|
|
55
|
+
"st_birthtime",
|
|
56
|
+
],
|
|
57
|
+
)
|
|
75
58
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
59
|
+
afc_opcode_t = Enum(
|
|
60
|
+
Int64ul,
|
|
61
|
+
STATUS=0x00000001,
|
|
62
|
+
DATA=0x00000002, # Data */
|
|
63
|
+
READ_DIR=0x00000003, # ReadDir */
|
|
64
|
+
READ_FILE=0x00000004, # ReadFile */
|
|
65
|
+
WRITE_FILE=0x00000005, # WriteFile */
|
|
66
|
+
WRITE_PART=0x00000006, # WritePart */
|
|
67
|
+
TRUNCATE=0x00000007, # TruncateFile */
|
|
68
|
+
REMOVE_PATH=0x00000008, # RemovePath */
|
|
69
|
+
MAKE_DIR=0x00000009, # MakeDir */
|
|
70
|
+
GET_FILE_INFO=0x0000000A, # GetFileInfo */
|
|
71
|
+
GET_DEVINFO=0x0000000B, # GetDeviceInfo */
|
|
72
|
+
WRITE_FILE_ATOM=0x0000000C, # WriteFileAtomic (tmp file+rename) */
|
|
73
|
+
FILE_OPEN=0x0000000D, # FileRefOpen */
|
|
74
|
+
FILE_OPEN_RES=0x0000000E, # FileRefOpenResult */
|
|
75
|
+
READ=0x0000000F, # FileRefRead */
|
|
76
|
+
WRITE=0x00000010, # FileRefWrite */
|
|
77
|
+
FILE_SEEK=0x00000011, # FileRefSeek */
|
|
78
|
+
FILE_TELL=0x00000012, # FileRefTell */
|
|
79
|
+
FILE_TELL_RES=0x00000013, # FileRefTellResult */
|
|
80
|
+
FILE_CLOSE=0x00000014, # FileRefClose */
|
|
81
|
+
FILE_SET_SIZE=0x00000015, # FileRefSetFileSize (ftruncate) */
|
|
82
|
+
GET_CON_INFO=0x00000016, # GetConnectionInfo */
|
|
83
|
+
SET_CON_OPTIONS=0x00000017, # SetConnectionOptions */
|
|
84
|
+
RENAME_PATH=0x00000018, # RenamePath */
|
|
85
|
+
SET_FS_BS=0x00000019, # SetFSBlockSize (0x800000) */
|
|
86
|
+
SET_SOCKET_BS=0x0000001A, # SetSocketBlockSize (0x800000) */
|
|
87
|
+
FILE_LOCK=0x0000001B, # FileRefLock */
|
|
88
|
+
MAKE_LINK=0x0000001C, # MakeLink */
|
|
89
|
+
SET_FILE_TIME=0x0000001E, # set st_mtime */
|
|
90
|
+
)
|
|
91
|
+
|
|
92
|
+
afc_error_t = Enum(
|
|
93
|
+
Int64ul,
|
|
94
|
+
SUCCESS=0,
|
|
95
|
+
UNKNOWN_ERROR=1,
|
|
96
|
+
OP_HEADER_INVALID=2,
|
|
97
|
+
NO_RESOURCES=3,
|
|
98
|
+
READ_ERROR=4,
|
|
99
|
+
WRITE_ERROR=5,
|
|
100
|
+
UNKNOWN_PACKET_TYPE=6,
|
|
101
|
+
INVALID_ARG=7,
|
|
102
|
+
OBJECT_NOT_FOUND=8,
|
|
103
|
+
OBJECT_IS_DIR=9,
|
|
104
|
+
PERM_DENIED=10,
|
|
105
|
+
SERVICE_NOT_CONNECTED=11,
|
|
106
|
+
OP_TIMEOUT=12,
|
|
107
|
+
TOO_MUCH_DATA=13,
|
|
108
|
+
END_OF_DATA=14,
|
|
109
|
+
OP_NOT_SUPPORTED=15,
|
|
110
|
+
OBJECT_EXISTS=16,
|
|
111
|
+
OBJECT_BUSY=17,
|
|
112
|
+
NO_SPACE_LEFT=18,
|
|
113
|
+
OP_WOULD_BLOCK=19,
|
|
114
|
+
IO_ERROR=20,
|
|
115
|
+
OP_INTERRUPTED=21,
|
|
116
|
+
OP_IN_PROGRESS=22,
|
|
117
|
+
INTERNAL_ERROR=23,
|
|
118
|
+
MUX_ERROR=30,
|
|
119
|
+
NO_MEM=31,
|
|
120
|
+
NOT_ENOUGH_DATA=32,
|
|
121
|
+
DIR_NOT_EMPTY=33,
|
|
122
|
+
)
|
|
123
|
+
|
|
124
|
+
afc_link_type_t = Enum(
|
|
125
|
+
Int64ul,
|
|
126
|
+
HARDLINK=1,
|
|
127
|
+
SYMLINK=2,
|
|
128
|
+
)
|
|
129
|
+
|
|
130
|
+
afc_fopen_mode_t = Enum(
|
|
131
|
+
Int64ul,
|
|
132
|
+
RDONLY=0x00000001, # /**< r O_RDONLY */
|
|
133
|
+
RW=0x00000002, # /**< r+ O_RDWR | O_CREAT */
|
|
134
|
+
WRONLY=0x00000003, # /**< w O_WRONLY | O_CREAT | O_TRUNC */
|
|
135
|
+
WR=0x00000004, # /**< w+ O_RDWR | O_CREAT | O_TRUNC */
|
|
136
|
+
APPEND=0x00000005, # /**< a O_WRONLY | O_APPEND | O_CREAT */
|
|
137
|
+
RDAPPEND=0x00000006, # /**< a+ O_RDWR | O_APPEND | O_CREAT */
|
|
138
|
+
)
|
|
120
139
|
|
|
121
140
|
AFC_FOPEN_TEXTUAL_MODES = {
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
141
|
+
"r": afc_fopen_mode_t.RDONLY,
|
|
142
|
+
"r+": afc_fopen_mode_t.RW,
|
|
143
|
+
"w": afc_fopen_mode_t.WRONLY,
|
|
144
|
+
"w+": afc_fopen_mode_t.WR,
|
|
145
|
+
"a": afc_fopen_mode_t.APPEND,
|
|
146
|
+
"a+": afc_fopen_mode_t.RDAPPEND,
|
|
128
147
|
}
|
|
129
148
|
|
|
130
149
|
AFC_LOCK_SH = 1 | 4 # /**< shared lock */
|
|
@@ -133,75 +152,75 @@ AFC_LOCK_UN = 8 | 4 # /**< unlock */
|
|
|
133
152
|
|
|
134
153
|
MAXIMUM_WRITE_SIZE = 1 << 30
|
|
135
154
|
|
|
136
|
-
AFCMAGIC = b
|
|
155
|
+
AFCMAGIC = b"CFA6LPAA"
|
|
137
156
|
|
|
138
157
|
afc_header_t = Struct(
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
158
|
+
"magic" / Const(AFCMAGIC),
|
|
159
|
+
"entire_length" / Int64ul,
|
|
160
|
+
"this_length" / Int64ul,
|
|
161
|
+
"packet_num" / Int64ul,
|
|
162
|
+
"operation" / afc_opcode_t,
|
|
163
|
+
"_data_offset" / Tell,
|
|
145
164
|
)
|
|
146
165
|
|
|
147
166
|
afc_read_dir_req_t = Struct(
|
|
148
|
-
|
|
167
|
+
"filename" / CString("utf8"),
|
|
149
168
|
)
|
|
150
169
|
|
|
151
170
|
afc_read_dir_resp_t = Struct(
|
|
152
|
-
|
|
171
|
+
"filenames" / GreedyRange(CString("utf8")),
|
|
153
172
|
)
|
|
154
173
|
|
|
155
174
|
afc_mkdir_req_t = Struct(
|
|
156
|
-
|
|
175
|
+
"filename" / CString("utf8"),
|
|
157
176
|
)
|
|
158
177
|
|
|
159
178
|
afc_stat_t = Struct(
|
|
160
|
-
|
|
179
|
+
"filename" / CString("utf8"),
|
|
161
180
|
)
|
|
162
181
|
|
|
163
182
|
afc_make_link_req_t = Struct(
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
183
|
+
"type" / afc_link_type_t,
|
|
184
|
+
"target" / CString("utf8"),
|
|
185
|
+
"source" / CString("utf8"),
|
|
167
186
|
)
|
|
168
187
|
|
|
169
188
|
afc_fopen_req_t = Struct(
|
|
170
|
-
|
|
171
|
-
|
|
189
|
+
"mode" / afc_fopen_mode_t,
|
|
190
|
+
"filename" / CString("utf8"),
|
|
172
191
|
)
|
|
173
192
|
|
|
174
193
|
afc_fopen_resp_t = Struct(
|
|
175
|
-
|
|
194
|
+
"handle" / Int64ul,
|
|
176
195
|
)
|
|
177
196
|
|
|
178
197
|
afc_fclose_req_t = Struct(
|
|
179
|
-
|
|
198
|
+
"handle" / Int64ul,
|
|
180
199
|
)
|
|
181
200
|
|
|
182
201
|
afc_rm_req_t = Struct(
|
|
183
|
-
|
|
202
|
+
"filename" / CString("utf8"),
|
|
184
203
|
)
|
|
185
204
|
|
|
186
205
|
afc_rename_req_t = Struct(
|
|
187
|
-
|
|
188
|
-
|
|
206
|
+
"source" / CString("utf8"),
|
|
207
|
+
"target" / CString("utf8"),
|
|
189
208
|
)
|
|
190
209
|
|
|
191
210
|
afc_fread_req_t = Struct(
|
|
192
|
-
|
|
193
|
-
|
|
211
|
+
"handle" / Int64ul,
|
|
212
|
+
"size" / Int64ul,
|
|
194
213
|
)
|
|
195
214
|
|
|
196
215
|
afc_lock_t = Struct(
|
|
197
|
-
|
|
198
|
-
|
|
216
|
+
"handle" / Int64ul,
|
|
217
|
+
"op" / Int64ul,
|
|
199
218
|
)
|
|
200
219
|
|
|
201
220
|
|
|
202
221
|
def list_to_dict(d):
|
|
203
|
-
d = d.decode(
|
|
204
|
-
t = d.split(
|
|
222
|
+
d = d.decode("utf-8")
|
|
223
|
+
t = d.split("\x00")
|
|
205
224
|
t = t[:-1]
|
|
206
225
|
|
|
207
226
|
assert len(t) % 2 == 0
|
|
@@ -212,28 +231,33 @@ def list_to_dict(d):
|
|
|
212
231
|
|
|
213
232
|
|
|
214
233
|
class AfcService(LockdownService):
|
|
215
|
-
SERVICE_NAME =
|
|
216
|
-
RSD_SERVICE_NAME =
|
|
234
|
+
SERVICE_NAME = "com.apple.afc"
|
|
235
|
+
RSD_SERVICE_NAME = "com.apple.afc.shim.remote"
|
|
217
236
|
|
|
218
|
-
def __init__(self, lockdown: LockdownServiceProvider, service_name: str = None):
|
|
237
|
+
def __init__(self, lockdown: LockdownServiceProvider, service_name: Optional[str] = None):
|
|
219
238
|
if service_name is None:
|
|
220
|
-
if isinstance(lockdown, LockdownClient)
|
|
221
|
-
service_name = self.SERVICE_NAME
|
|
222
|
-
else:
|
|
223
|
-
service_name = self.RSD_SERVICE_NAME
|
|
239
|
+
service_name = self.SERVICE_NAME if isinstance(lockdown, LockdownClient) else self.RSD_SERVICE_NAME
|
|
224
240
|
super().__init__(lockdown, service_name)
|
|
225
241
|
self.packet_num = 0
|
|
226
242
|
|
|
227
|
-
def pull(
|
|
228
|
-
|
|
243
|
+
def pull(
|
|
244
|
+
self,
|
|
245
|
+
relative_src: str,
|
|
246
|
+
dst: str,
|
|
247
|
+
match: Optional[Pattern] = None,
|
|
248
|
+
callback: Optional[Callable] = None,
|
|
249
|
+
src_dir: str = "",
|
|
250
|
+
ignore_errors: bool = False,
|
|
251
|
+
progress_bar: bool = True,
|
|
252
|
+
) -> None:
|
|
229
253
|
src = self.resolve_path(posixpath.join(src_dir, relative_src))
|
|
230
254
|
|
|
231
255
|
if not self.isdir(src):
|
|
232
256
|
# normal file
|
|
233
257
|
if os.path.isdir(dst):
|
|
234
258
|
dst = os.path.join(dst, os.path.basename(relative_src))
|
|
235
|
-
with open(dst,
|
|
236
|
-
src_size = self.stat(src)[
|
|
259
|
+
with open(dst, "wb") as f:
|
|
260
|
+
src_size = self.stat(src)["st_size"]
|
|
237
261
|
if src_size <= MAXIMUM_READ_SIZE:
|
|
238
262
|
f.write(self.get_file_contents(src))
|
|
239
263
|
else:
|
|
@@ -247,7 +271,7 @@ class AfcService(LockdownService):
|
|
|
247
271
|
f.write(self.fread(handle, min(MAXIMUM_READ_SIZE, left_size)))
|
|
248
272
|
left_size -= MAXIMUM_READ_SIZE
|
|
249
273
|
self.fclose(handle)
|
|
250
|
-
os.utime(dst, (os.stat(dst).st_atime, self.stat(src)[
|
|
274
|
+
os.utime(dst, (os.stat(dst).st_atime, self.stat(src)["st_mtime"].timestamp()))
|
|
251
275
|
if callback is not None:
|
|
252
276
|
callback(src, dst)
|
|
253
277
|
else:
|
|
@@ -267,12 +291,22 @@ class AfcService(LockdownService):
|
|
|
267
291
|
try:
|
|
268
292
|
if self.isdir(src_filename):
|
|
269
293
|
dst_filename.mkdir(exist_ok=True)
|
|
270
|
-
self.pull(
|
|
271
|
-
|
|
294
|
+
self.pull(
|
|
295
|
+
src_filename,
|
|
296
|
+
str(dst_path),
|
|
297
|
+
callback=callback,
|
|
298
|
+
ignore_errors=ignore_errors,
|
|
299
|
+
progress_bar=progress_bar,
|
|
300
|
+
)
|
|
272
301
|
continue
|
|
273
302
|
|
|
274
|
-
self.pull(
|
|
275
|
-
|
|
303
|
+
self.pull(
|
|
304
|
+
src_filename,
|
|
305
|
+
str(dst_path),
|
|
306
|
+
callback=callback,
|
|
307
|
+
ignore_errors=ignore_errors,
|
|
308
|
+
progress_bar=progress_bar,
|
|
309
|
+
)
|
|
276
310
|
|
|
277
311
|
except Exception as afc_exception:
|
|
278
312
|
if not ignore_errors:
|
|
@@ -283,9 +317,9 @@ class AfcService(LockdownService):
|
|
|
283
317
|
def exists(self, filename):
|
|
284
318
|
try:
|
|
285
319
|
self.stat(filename)
|
|
286
|
-
return True
|
|
287
320
|
except AfcFileNotFoundError:
|
|
288
321
|
return False
|
|
322
|
+
return True
|
|
289
323
|
|
|
290
324
|
@path_to_str()
|
|
291
325
|
def wait_exists(self, filename):
|
|
@@ -309,7 +343,7 @@ class AfcService(LockdownService):
|
|
|
309
343
|
if not self.exists(remote_parent):
|
|
310
344
|
raise
|
|
311
345
|
remote_path = posixpath.join(remote_parent, os.path.basename(remote_path))
|
|
312
|
-
with open(local_path,
|
|
346
|
+
with open(local_path, "rb") as f:
|
|
313
347
|
self.set_file_contents(remote_path, f.read())
|
|
314
348
|
else:
|
|
315
349
|
# directory
|
|
@@ -318,7 +352,7 @@ class AfcService(LockdownService):
|
|
|
318
352
|
|
|
319
353
|
for filename in os.listdir(local_path):
|
|
320
354
|
local_filename = os.path.join(local_path, filename)
|
|
321
|
-
remote_filename = posixpath.join(remote_path, filename).removeprefix(
|
|
355
|
+
remote_filename = posixpath.join(remote_path, filename).removeprefix("/")
|
|
322
356
|
|
|
323
357
|
if os.path.isdir(local_filename):
|
|
324
358
|
if not self.exists(remote_filename):
|
|
@@ -336,7 +370,7 @@ class AfcService(LockdownService):
|
|
|
336
370
|
|
|
337
371
|
@path_to_str()
|
|
338
372
|
def rm_single(self, filename: str, force: bool = False) -> bool:
|
|
339
|
-
"""
|
|
373
|
+
"""remove single file or directory
|
|
340
374
|
|
|
341
375
|
return if succeed or raise exception depending on force parameter.
|
|
342
376
|
|
|
@@ -346,16 +380,16 @@ class AfcService(LockdownService):
|
|
|
346
380
|
:rtype: bool
|
|
347
381
|
"""
|
|
348
382
|
try:
|
|
349
|
-
self._do_operation(afc_opcode_t.REMOVE_PATH, afc_rm_req_t.build({
|
|
350
|
-
return True
|
|
383
|
+
self._do_operation(afc_opcode_t.REMOVE_PATH, afc_rm_req_t.build({"filename": filename}))
|
|
351
384
|
except AfcException:
|
|
352
385
|
if force:
|
|
353
386
|
return False
|
|
354
387
|
raise
|
|
388
|
+
return True
|
|
355
389
|
|
|
356
390
|
@path_to_str()
|
|
357
391
|
def rm(self, filename: str, match: Optional[Pattern] = None, force: bool = False) -> list[str]:
|
|
358
|
-
"""
|
|
392
|
+
"""recursive removal of a directory or a file
|
|
359
393
|
|
|
360
394
|
if did not succeed, return list of undeleted filenames or raise exception depending on force parameter.
|
|
361
395
|
|
|
@@ -365,9 +399,8 @@ class AfcService(LockdownService):
|
|
|
365
399
|
:return: list of undeleted paths
|
|
366
400
|
:rtype: list[str]
|
|
367
401
|
"""
|
|
368
|
-
if not self.exists(filename):
|
|
369
|
-
|
|
370
|
-
return [filename]
|
|
402
|
+
if not self.exists(filename) and not self.rm_single(filename, force=force):
|
|
403
|
+
return [filename]
|
|
371
404
|
|
|
372
405
|
# single file
|
|
373
406
|
if not self.isdir(filename):
|
|
@@ -402,7 +435,7 @@ class AfcService(LockdownService):
|
|
|
402
435
|
raise
|
|
403
436
|
|
|
404
437
|
if undeleted_items:
|
|
405
|
-
raise AfcException(f
|
|
438
|
+
raise AfcException(f"Failed to delete paths: {undeleted_items}", None)
|
|
406
439
|
|
|
407
440
|
return []
|
|
408
441
|
|
|
@@ -411,129 +444,132 @@ class AfcService(LockdownService):
|
|
|
411
444
|
|
|
412
445
|
@path_to_str()
|
|
413
446
|
def listdir(self, filename: str):
|
|
414
|
-
data = self._do_operation(afc_opcode_t.READ_DIR, afc_read_dir_req_t.build({
|
|
447
|
+
data = self._do_operation(afc_opcode_t.READ_DIR, afc_read_dir_req_t.build({"filename": filename}))
|
|
415
448
|
return afc_read_dir_resp_t.parse(data).filenames[2:] # skip the . and ..
|
|
416
449
|
|
|
417
450
|
@path_to_str()
|
|
418
451
|
def makedirs(self, filename: str):
|
|
419
|
-
return self._do_operation(afc_opcode_t.MAKE_DIR, afc_mkdir_req_t.build({
|
|
452
|
+
return self._do_operation(afc_opcode_t.MAKE_DIR, afc_mkdir_req_t.build({"filename": filename}))
|
|
420
453
|
|
|
421
454
|
@path_to_str()
|
|
422
455
|
def isdir(self, filename: str) -> bool:
|
|
423
456
|
stat = self.stat(filename)
|
|
424
|
-
return stat.get(
|
|
457
|
+
return stat.get("st_ifmt") == "S_IFDIR"
|
|
425
458
|
|
|
426
459
|
@path_to_str()
|
|
427
460
|
def stat(self, filename: str):
|
|
428
461
|
try:
|
|
429
462
|
stat = list_to_dict(
|
|
430
|
-
self._do_operation(afc_opcode_t.GET_FILE_INFO, afc_stat_t.build({
|
|
463
|
+
self._do_operation(afc_opcode_t.GET_FILE_INFO, afc_stat_t.build({"filename": filename}))
|
|
464
|
+
)
|
|
431
465
|
except AfcException as e:
|
|
432
466
|
if e.status != afc_error_t.READ_ERROR:
|
|
433
467
|
raise
|
|
434
468
|
raise AfcFileNotFoundError(e.args[0], e.status) from e
|
|
435
469
|
|
|
436
|
-
stat[
|
|
437
|
-
stat[
|
|
438
|
-
stat[
|
|
439
|
-
stat[
|
|
440
|
-
stat[
|
|
441
|
-
stat[
|
|
442
|
-
stat[
|
|
470
|
+
stat["st_size"] = int(stat["st_size"])
|
|
471
|
+
stat["st_blocks"] = int(stat["st_blocks"])
|
|
472
|
+
stat["st_mtime"] = int(stat["st_mtime"])
|
|
473
|
+
stat["st_birthtime"] = int(stat["st_birthtime"])
|
|
474
|
+
stat["st_nlink"] = int(stat["st_nlink"])
|
|
475
|
+
stat["st_mtime"] = datetime.fromtimestamp(stat["st_mtime"] / (10**9))
|
|
476
|
+
stat["st_birthtime"] = datetime.fromtimestamp(stat["st_birthtime"] / (10**9))
|
|
443
477
|
return stat
|
|
444
478
|
|
|
445
479
|
@path_to_str()
|
|
446
480
|
def os_stat(self, path: str):
|
|
447
481
|
stat = self.stat(path)
|
|
448
482
|
mode = 0
|
|
449
|
-
for s_mode in [
|
|
450
|
-
if stat[
|
|
483
|
+
for s_mode in ["S_IFDIR", "S_IFCHR", "S_IFBLK", "S_IFREG", "S_IFIFO", "S_IFLNK", "S_IFSOCK"]:
|
|
484
|
+
if stat["st_ifmt"] == s_mode:
|
|
451
485
|
mode = getattr(stat_module, s_mode)
|
|
452
486
|
return StatResult(
|
|
453
|
-
mode,
|
|
454
|
-
|
|
455
|
-
|
|
487
|
+
mode,
|
|
488
|
+
hash(posixpath.normpath(path)),
|
|
489
|
+
0,
|
|
490
|
+
stat["st_nlink"],
|
|
491
|
+
0,
|
|
492
|
+
0,
|
|
493
|
+
stat["st_size"],
|
|
494
|
+
stat["st_mtime"].timestamp(),
|
|
495
|
+
stat["st_mtime"].timestamp(),
|
|
496
|
+
stat["st_birthtime"].timestamp(),
|
|
497
|
+
stat["st_blocks"],
|
|
498
|
+
4096,
|
|
499
|
+
stat["st_birthtime"].timestamp(),
|
|
456
500
|
)
|
|
457
501
|
|
|
458
502
|
@path_to_str()
|
|
459
503
|
def link(self, target: str, source: str, type_=afc_link_type_t.SYMLINK):
|
|
460
|
-
return self._do_operation(
|
|
461
|
-
|
|
504
|
+
return self._do_operation(
|
|
505
|
+
afc_opcode_t.MAKE_LINK, afc_make_link_req_t.build({"type": type_, "target": target, "source": source})
|
|
506
|
+
)
|
|
462
507
|
|
|
463
508
|
@path_to_str()
|
|
464
|
-
def fopen(self, filename: str, mode: str =
|
|
509
|
+
def fopen(self, filename: str, mode: str = "r") -> int:
|
|
465
510
|
if mode not in AFC_FOPEN_TEXTUAL_MODES:
|
|
466
|
-
raise ArgumentError(f
|
|
511
|
+
raise ArgumentError(f"mode can be only one of: {AFC_FOPEN_TEXTUAL_MODES.keys()}")
|
|
467
512
|
|
|
468
|
-
data = self._do_operation(
|
|
469
|
-
|
|
513
|
+
data = self._do_operation(
|
|
514
|
+
afc_opcode_t.FILE_OPEN, afc_fopen_req_t.build({"mode": AFC_FOPEN_TEXTUAL_MODES[mode], "filename": filename})
|
|
515
|
+
)
|
|
470
516
|
return afc_fopen_resp_t.parse(data).handle
|
|
471
517
|
|
|
472
518
|
def fclose(self, handle: int):
|
|
473
|
-
return self._do_operation(afc_opcode_t.FILE_CLOSE, afc_fclose_req_t.build({
|
|
519
|
+
return self._do_operation(afc_opcode_t.FILE_CLOSE, afc_fclose_req_t.build({"handle": handle}))
|
|
474
520
|
|
|
475
521
|
@path_to_str()
|
|
476
522
|
def rename(self, source: str, target: str):
|
|
477
523
|
try:
|
|
478
|
-
return self._do_operation(
|
|
479
|
-
|
|
524
|
+
return self._do_operation(
|
|
525
|
+
afc_opcode_t.RENAME_PATH, afc_rename_req_t.build({"source": source, "target": target})
|
|
526
|
+
)
|
|
480
527
|
except AfcException as e:
|
|
481
528
|
if self.exists(source):
|
|
482
529
|
raise
|
|
483
530
|
raise AfcFileNotFoundError(e.args[0], e.status) from e
|
|
484
531
|
|
|
485
532
|
def fread(self, handle: int, sz: bytes) -> bytes:
|
|
486
|
-
data = b
|
|
533
|
+
data = b""
|
|
487
534
|
while sz > 0:
|
|
488
|
-
if sz > MAXIMUM_READ_SIZE
|
|
489
|
-
|
|
490
|
-
else:
|
|
491
|
-
to_read = sz
|
|
492
|
-
self._dispatch_packet(afc_opcode_t.READ, afc_fread_req_t.build({'handle': handle, 'size': to_read}))
|
|
535
|
+
to_read = MAXIMUM_READ_SIZE if sz > MAXIMUM_READ_SIZE else sz
|
|
536
|
+
self._dispatch_packet(afc_opcode_t.READ, afc_fread_req_t.build({"handle": handle, "size": to_read}))
|
|
493
537
|
status, chunk = self._receive_data()
|
|
494
538
|
if status != afc_error_t.SUCCESS:
|
|
495
|
-
raise AfcException(
|
|
539
|
+
raise AfcException("fread error", status)
|
|
496
540
|
sz -= to_read
|
|
497
541
|
data += chunk
|
|
498
542
|
return data
|
|
499
543
|
|
|
500
544
|
def fwrite(self, handle, data, chunk_size=MAXIMUM_WRITE_SIZE):
|
|
501
|
-
file_handle = struct.pack(
|
|
545
|
+
file_handle = struct.pack("<Q", handle)
|
|
502
546
|
chunks_count = len(data) // chunk_size
|
|
503
|
-
b = b
|
|
547
|
+
b = b""
|
|
504
548
|
for i in range(chunks_count):
|
|
505
|
-
chunk = data[i * chunk_size:(i + 1) * chunk_size]
|
|
506
|
-
self._dispatch_packet(afc_opcode_t.WRITE,
|
|
507
|
-
file_handle + chunk,
|
|
508
|
-
this_length=48)
|
|
549
|
+
chunk = data[i * chunk_size : (i + 1) * chunk_size]
|
|
550
|
+
self._dispatch_packet(afc_opcode_t.WRITE, file_handle + chunk, this_length=48)
|
|
509
551
|
b += chunk
|
|
510
552
|
|
|
511
|
-
status,
|
|
553
|
+
status, _response = self._receive_data()
|
|
512
554
|
if status != afc_error_t.SUCCESS:
|
|
513
|
-
raise AfcException(f
|
|
555
|
+
raise AfcException(f"failed to write chunk: {status}", status)
|
|
514
556
|
|
|
515
557
|
if len(data) % chunk_size:
|
|
516
|
-
chunk = data[chunks_count * chunk_size:]
|
|
517
|
-
self._dispatch_packet(afc_opcode_t.WRITE,
|
|
518
|
-
file_handle + chunk,
|
|
519
|
-
this_length=48)
|
|
558
|
+
chunk = data[chunks_count * chunk_size :]
|
|
559
|
+
self._dispatch_packet(afc_opcode_t.WRITE, file_handle + chunk, this_length=48)
|
|
520
560
|
|
|
521
561
|
b += chunk
|
|
522
562
|
|
|
523
|
-
status,
|
|
563
|
+
status, _response = self._receive_data()
|
|
524
564
|
if status != afc_error_t.SUCCESS:
|
|
525
|
-
raise AfcException(f
|
|
565
|
+
raise AfcException(f"failed to write last chunk: {status}", status)
|
|
526
566
|
|
|
527
567
|
@path_to_str()
|
|
528
568
|
def resolve_path(self, filename: str):
|
|
529
569
|
info = self.stat(filename)
|
|
530
|
-
if info[
|
|
531
|
-
target = info[
|
|
532
|
-
if not target.startswith(
|
|
533
|
-
# relative path
|
|
534
|
-
filename = posixpath.join(posixpath.dirname(filename), target)
|
|
535
|
-
else:
|
|
536
|
-
filename = target
|
|
570
|
+
if info["st_ifmt"] == "S_IFLNK":
|
|
571
|
+
target = info["LinkTarget"]
|
|
572
|
+
filename = posixpath.join(posixpath.dirname(filename), target) if not target.startswith("/") else target
|
|
537
573
|
return filename
|
|
538
574
|
|
|
539
575
|
@path_to_str()
|
|
@@ -541,19 +577,19 @@ class AfcService(LockdownService):
|
|
|
541
577
|
filename = self.resolve_path(filename)
|
|
542
578
|
info = self.stat(filename)
|
|
543
579
|
|
|
544
|
-
if info[
|
|
545
|
-
raise AfcException(f
|
|
580
|
+
if info["st_ifmt"] != "S_IFREG":
|
|
581
|
+
raise AfcException(f"{filename} isn't a file", afc_error_t.INVALID_ARG)
|
|
546
582
|
|
|
547
583
|
h = self.fopen(filename)
|
|
548
584
|
if not h:
|
|
549
585
|
return
|
|
550
|
-
d = self.fread(h, int(info[
|
|
586
|
+
d = self.fread(h, int(info["st_size"]))
|
|
551
587
|
self.fclose(h)
|
|
552
588
|
return d
|
|
553
589
|
|
|
554
590
|
@path_to_str()
|
|
555
591
|
def set_file_contents(self, filename: str, data: bytes) -> None:
|
|
556
|
-
h = self.fopen(filename,
|
|
592
|
+
h = self.fopen(filename, "w")
|
|
557
593
|
self.fwrite(h, data)
|
|
558
594
|
self.fclose(h)
|
|
559
595
|
|
|
@@ -562,10 +598,10 @@ class AfcService(LockdownService):
|
|
|
562
598
|
dirs = []
|
|
563
599
|
files = []
|
|
564
600
|
for fd in self.listdir(dirname):
|
|
565
|
-
if fd in (
|
|
601
|
+
if fd in (".", "..", ""):
|
|
566
602
|
continue
|
|
567
603
|
infos = self.stat(posixpath.join(dirname, fd))
|
|
568
|
-
if infos and infos.get(
|
|
604
|
+
if infos and infos.get("st_ifmt") == "S_IFDIR":
|
|
569
605
|
dirs.append(fd)
|
|
570
606
|
else:
|
|
571
607
|
files.append(fd)
|
|
@@ -589,14 +625,16 @@ class AfcService(LockdownService):
|
|
|
589
625
|
yield posixpath.join(folder, entry)
|
|
590
626
|
|
|
591
627
|
def lock(self, handle, operation):
|
|
592
|
-
return self._do_operation(afc_opcode_t.FILE_LOCK, afc_lock_t.build({
|
|
628
|
+
return self._do_operation(afc_opcode_t.FILE_LOCK, afc_lock_t.build({"handle": handle, "op": operation}))
|
|
593
629
|
|
|
594
630
|
def _dispatch_packet(self, operation, data, this_length=0):
|
|
595
|
-
afcpack = Container(
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
631
|
+
afcpack = Container(
|
|
632
|
+
magic=AFCMAGIC,
|
|
633
|
+
entire_length=afc_header_t.sizeof() + len(data),
|
|
634
|
+
this_length=afc_header_t.sizeof() + len(data),
|
|
635
|
+
packet_num=self.packet_num,
|
|
636
|
+
operation=operation,
|
|
637
|
+
)
|
|
600
638
|
if this_length:
|
|
601
639
|
afcpack.this_length = this_length
|
|
602
640
|
header = afc_header_t.build(afcpack)
|
|
@@ -606,21 +644,21 @@ class AfcService(LockdownService):
|
|
|
606
644
|
def _receive_data(self):
|
|
607
645
|
res = self.service.recvall(afc_header_t.sizeof())
|
|
608
646
|
status = afc_error_t.SUCCESS
|
|
609
|
-
data =
|
|
647
|
+
data = ""
|
|
610
648
|
if res:
|
|
611
649
|
res = afc_header_t.parse(res)
|
|
612
|
-
assert res[
|
|
613
|
-
length = res[
|
|
650
|
+
assert res["entire_length"] >= afc_header_t.sizeof()
|
|
651
|
+
length = res["entire_length"] - afc_header_t.sizeof()
|
|
614
652
|
data = self.service.recvall(length)
|
|
615
653
|
if res.operation == afc_opcode_t.STATUS:
|
|
616
654
|
if length != 8:
|
|
617
|
-
self.logger.error(
|
|
655
|
+
self.logger.error("Status length != 8")
|
|
618
656
|
status = afc_error_t.parse(data)
|
|
619
657
|
elif res.operation != afc_opcode_t.DATA:
|
|
620
658
|
pass
|
|
621
659
|
return status, data
|
|
622
660
|
|
|
623
|
-
def _do_operation(self, opcode, data: bytes = b
|
|
661
|
+
def _do_operation(self, opcode, data: bytes = b""):
|
|
624
662
|
self._dispatch_packet(opcode, data)
|
|
625
663
|
status, data = self._receive_data()
|
|
626
664
|
|
|
@@ -629,7 +667,7 @@ class AfcService(LockdownService):
|
|
|
629
667
|
if status == afc_error_t.OBJECT_NOT_FOUND:
|
|
630
668
|
exception = AfcFileNotFoundError
|
|
631
669
|
|
|
632
|
-
raise exception(f
|
|
670
|
+
raise exception(f"opcode: {opcode} failed with status: {status}", status)
|
|
633
671
|
|
|
634
672
|
return data
|
|
635
673
|
|
|
@@ -667,24 +705,24 @@ class AfcLsStub(LsStub):
|
|
|
667
705
|
return posixpath.basename(path)
|
|
668
706
|
|
|
669
707
|
def getgroup(self, st_gid):
|
|
670
|
-
return
|
|
708
|
+
return "-"
|
|
671
709
|
|
|
672
710
|
def getuser(self, st_uid):
|
|
673
|
-
return
|
|
711
|
+
return "-"
|
|
674
712
|
|
|
675
713
|
def now(self):
|
|
676
714
|
return self.afc_shell.lockdown.date
|
|
677
715
|
|
|
678
|
-
def listdir(self, path=
|
|
716
|
+
def listdir(self, path="."):
|
|
679
717
|
return self.afc_shell.afc.listdir(path)
|
|
680
718
|
|
|
681
719
|
def system(self):
|
|
682
|
-
return
|
|
720
|
+
return "Darwin"
|
|
683
721
|
|
|
684
722
|
def getenv(self, key, default=None):
|
|
685
|
-
return
|
|
723
|
+
return ""
|
|
686
724
|
|
|
687
|
-
def print(self, *objects, sep=
|
|
725
|
+
def print(self, *objects, sep=" ", end="\n", file=sys.stdout, flush=False):
|
|
688
726
|
print(objects[0], end=end)
|
|
689
727
|
|
|
690
728
|
def get_tty_width(self):
|
|
@@ -692,9 +730,9 @@ class AfcLsStub(LsStub):
|
|
|
692
730
|
|
|
693
731
|
|
|
694
732
|
def path_completer(xsh, action, completer, alias, command) -> list[str]:
|
|
695
|
-
shell: AfcShell = XSH.ctx[
|
|
733
|
+
shell: AfcShell = XSH.ctx["_shell"]
|
|
696
734
|
pwd = shell.cwd
|
|
697
|
-
is_absolute = command.prefix.startswith(
|
|
735
|
+
is_absolute = command.prefix.startswith("/")
|
|
698
736
|
dirpath = posixpath.join(pwd, command.prefix)
|
|
699
737
|
if not shell.afc.exists(dirpath):
|
|
700
738
|
dirpath = posixpath.dirname(dirpath)
|
|
@@ -706,7 +744,7 @@ def path_completer(xsh, action, completer, alias, command) -> list[str]:
|
|
|
706
744
|
completion_option = posixpath.relpath(posixpath.join(dirpath, f), pwd)
|
|
707
745
|
try:
|
|
708
746
|
if shell.afc.isdir(posixpath.join(dirpath, f)):
|
|
709
|
-
result.append(f
|
|
747
|
+
result.append(f"{completion_option}/")
|
|
710
748
|
else:
|
|
711
749
|
result.append(completion_option)
|
|
712
750
|
except AfcException:
|
|
@@ -715,9 +753,9 @@ def path_completer(xsh, action, completer, alias, command) -> list[str]:
|
|
|
715
753
|
|
|
716
754
|
|
|
717
755
|
def dir_completer(xsh, action, completer, alias, command):
|
|
718
|
-
shell: AfcShell = XSH.ctx[
|
|
756
|
+
shell: AfcShell = XSH.ctx["_shell"]
|
|
719
757
|
pwd = shell.cwd
|
|
720
|
-
is_absolute = command.prefix.startswith(
|
|
758
|
+
is_absolute = command.prefix.startswith("/")
|
|
721
759
|
dirpath = posixpath.join(pwd, command.prefix)
|
|
722
760
|
if not shell.afc.exists(dirpath):
|
|
723
761
|
dirpath = posixpath.dirname(dirpath)
|
|
@@ -729,7 +767,7 @@ def dir_completer(xsh, action, completer, alias, command):
|
|
|
729
767
|
completion_option = posixpath.relpath(posixpath.join(dirpath, f), pwd)
|
|
730
768
|
try:
|
|
731
769
|
if shell.afc.isdir(posixpath.join(dirpath, f)):
|
|
732
|
-
result.append(f
|
|
770
|
+
result.append(f"{completion_option}/")
|
|
733
771
|
except AfcException:
|
|
734
772
|
result.append(completion_option)
|
|
735
773
|
return result
|
|
@@ -737,22 +775,27 @@ def dir_completer(xsh, action, completer, alias, command):
|
|
|
737
775
|
|
|
738
776
|
class AfcShell:
|
|
739
777
|
@classmethod
|
|
740
|
-
def create(
|
|
741
|
-
|
|
778
|
+
def create(
|
|
779
|
+
cls,
|
|
780
|
+
service_provider: LockdownServiceProvider,
|
|
781
|
+
service_name: Optional[str] = None,
|
|
782
|
+
service: Optional[LockdownService] = None,
|
|
783
|
+
auto_cd: Optional[str] = "/",
|
|
784
|
+
):
|
|
742
785
|
warnings.filterwarnings("ignore", category=DeprecationWarning)
|
|
743
|
-
args = [
|
|
744
|
-
os.environ[
|
|
745
|
-
XSH.ctx[
|
|
746
|
-
XSH.ctx[
|
|
747
|
-
XSH.ctx[
|
|
786
|
+
args = ["--rc", str(pathlib.Path(__file__).absolute())]
|
|
787
|
+
os.environ["XONSH_COLOR_STYLE"] = "default"
|
|
788
|
+
XSH.ctx["_class"] = cls
|
|
789
|
+
XSH.ctx["_lockdown"] = service_provider
|
|
790
|
+
XSH.ctx["_auto_cd"] = auto_cd
|
|
748
791
|
if service is not None:
|
|
749
|
-
XSH.ctx[
|
|
792
|
+
XSH.ctx["_service"] = service
|
|
750
793
|
else:
|
|
751
|
-
XSH.ctx[
|
|
794
|
+
XSH.ctx["_service"] = AfcService(service_provider, service_name=service_name)
|
|
752
795
|
|
|
753
796
|
try:
|
|
754
|
-
logging.getLogger(
|
|
755
|
-
logging.getLogger(
|
|
797
|
+
logging.getLogger("parso.python.diff").disabled = True
|
|
798
|
+
logging.getLogger("parso.cache").disabled = True
|
|
756
799
|
xonsh_main(args)
|
|
757
800
|
except SystemExit:
|
|
758
801
|
pass
|
|
@@ -760,18 +803,18 @@ class AfcShell:
|
|
|
760
803
|
def __init__(self, lockdown: LockdownServiceProvider, service: AfcService):
|
|
761
804
|
self.lockdown = lockdown
|
|
762
805
|
self.afc = service
|
|
763
|
-
XSH.ctx[
|
|
764
|
-
self.cwd = XSH.ctx.get(
|
|
806
|
+
XSH.ctx["_shell"] = self
|
|
807
|
+
self.cwd = XSH.ctx.get("_auto_cd", "/")
|
|
765
808
|
self._commands = {}
|
|
766
809
|
self._orig_aliases = {}
|
|
767
|
-
self._orig_prompt = XSH.env[
|
|
810
|
+
self._orig_prompt = XSH.env["PROMPT"]
|
|
768
811
|
self._setup_shell_commands()
|
|
769
812
|
|
|
770
|
-
print_color(
|
|
813
|
+
print_color("""
|
|
771
814
|
{BOLD_WHITE}Welcome to xonsh-afc shell! 👋{RESET}
|
|
772
815
|
Use {CYAN}show-help{RESET} to view a list of all available special commands.
|
|
773
816
|
These special commands will replace all already existing commands.
|
|
774
|
-
|
|
817
|
+
""")
|
|
775
818
|
|
|
776
819
|
def _register_arg_parse_alias(self, name: str, handler: Union[Callable, str]):
|
|
777
820
|
handler = ArgParserAlias(func=handler, has_args=True, prog=name)
|
|
@@ -788,38 +831,38 @@ class AfcShell:
|
|
|
788
831
|
|
|
789
832
|
def _setup_shell_commands(self):
|
|
790
833
|
# clear all host commands except for some useful ones
|
|
791
|
-
XSH.env[
|
|
834
|
+
XSH.env["PATH"].clear()
|
|
792
835
|
# adding "file" just to fix xonsh errors
|
|
793
|
-
for cmd in [
|
|
836
|
+
for cmd in ["wc", "grep", "egrep", "sed", "awk", "print", "yes", "cat"]:
|
|
794
837
|
executable = shutil.which(cmd)
|
|
795
838
|
if executable is not None:
|
|
796
839
|
self._register_rpc_command(cmd, executable)
|
|
797
840
|
|
|
798
|
-
self._register_rpc_command(
|
|
799
|
-
self._register_arg_parse_alias(
|
|
800
|
-
self._register_arg_parse_alias(
|
|
801
|
-
self._register_arg_parse_alias(
|
|
802
|
-
self._register_arg_parse_alias(
|
|
803
|
-
self._register_arg_parse_alias(
|
|
804
|
-
self._register_arg_parse_alias(
|
|
805
|
-
self._register_arg_parse_alias(
|
|
806
|
-
self._register_arg_parse_alias(
|
|
807
|
-
self._register_arg_parse_alias(
|
|
808
|
-
self._register_arg_parse_alias(
|
|
809
|
-
self._register_arg_parse_alias(
|
|
810
|
-
self._register_arg_parse_alias(
|
|
811
|
-
self._register_arg_parse_alias(
|
|
812
|
-
self._register_arg_parse_alias(
|
|
813
|
-
self._register_arg_parse_alias(
|
|
814
|
-
|
|
815
|
-
XSH.env[
|
|
816
|
-
XSH.env[
|
|
817
|
-
XSH.env[
|
|
841
|
+
self._register_rpc_command("ls", self.do_ls)
|
|
842
|
+
self._register_arg_parse_alias("pwd", self._do_pwd)
|
|
843
|
+
self._register_arg_parse_alias("link", self._do_link)
|
|
844
|
+
self._register_arg_parse_alias("cd", self._do_cd)
|
|
845
|
+
self._register_arg_parse_alias("cat", self._do_cat)
|
|
846
|
+
self._register_arg_parse_alias("rm", self._do_rm)
|
|
847
|
+
self._register_arg_parse_alias("pull", self._do_pull)
|
|
848
|
+
self._register_arg_parse_alias("push", self._do_push)
|
|
849
|
+
self._register_arg_parse_alias("walk", self._do_walk)
|
|
850
|
+
self._register_arg_parse_alias("head", self._do_head)
|
|
851
|
+
self._register_arg_parse_alias("hexdump", self._do_hexdump)
|
|
852
|
+
self._register_arg_parse_alias("mkdir", self._do_mkdir)
|
|
853
|
+
self._register_arg_parse_alias("info", self._do_info)
|
|
854
|
+
self._register_arg_parse_alias("mv", self._do_mv)
|
|
855
|
+
self._register_arg_parse_alias("stat", self._do_stat)
|
|
856
|
+
self._register_arg_parse_alias("show-help", self._do_show_help)
|
|
857
|
+
|
|
858
|
+
XSH.env["PROMPT"] = f"[{{BOLD_CYAN}}{self.afc.service_name}:{{afc_cwd}}{{RESET}}]{{prompt_end}} "
|
|
859
|
+
XSH.env["PROMPT_FIELDS"]["afc_cwd"] = self._afc_cwd
|
|
860
|
+
XSH.env["PROMPT_FIELDS"]["prompt_end"] = self._prompt
|
|
818
861
|
|
|
819
862
|
def _prompt(self) -> str:
|
|
820
863
|
if len(XSH.history) == 0 or XSH.history[-1].rtn == 0:
|
|
821
|
-
return
|
|
822
|
-
return
|
|
864
|
+
return "{BOLD_GREEN}${RESET}"
|
|
865
|
+
return "{BOLD_RED}${RESET}"
|
|
823
866
|
|
|
824
867
|
def _afc_cwd(self) -> str:
|
|
825
868
|
return self.cwd
|
|
@@ -831,9 +874,9 @@ class AfcShell:
|
|
|
831
874
|
"""
|
|
832
875
|
list all rpc commands
|
|
833
876
|
"""
|
|
834
|
-
buf =
|
|
835
|
-
for k,
|
|
836
|
-
buf += f
|
|
877
|
+
buf = ""
|
|
878
|
+
for k, _v in self._commands.items():
|
|
879
|
+
buf += f"👾 {k}\n"
|
|
837
880
|
print(buf)
|
|
838
881
|
|
|
839
882
|
def _do_pwd(self) -> None:
|
|
@@ -849,13 +892,13 @@ class AfcShell:
|
|
|
849
892
|
self.cwd = directory
|
|
850
893
|
self._update_prompt()
|
|
851
894
|
else:
|
|
852
|
-
print(f
|
|
895
|
+
print(f"[ERROR] {directory} does not exist")
|
|
853
896
|
|
|
854
897
|
def do_ls(self, args, stdin, stdout, stderr):
|
|
855
|
-
"""
|
|
898
|
+
"""list files"""
|
|
856
899
|
try:
|
|
857
|
-
with ls_cli.make_context(
|
|
858
|
-
files = list(map(self._relative_path, ctx.params.pop(
|
|
900
|
+
with ls_cli.make_context("ls", args) as ctx:
|
|
901
|
+
files = list(map(self._relative_path, ctx.params.pop("files")))
|
|
859
902
|
files = files if files else [self.cwd]
|
|
860
903
|
Ls(AfcLsStub(self, stdout))(*files, **ctx.params)
|
|
861
904
|
except Exit:
|
|
@@ -871,12 +914,17 @@ class AfcShell:
|
|
|
871
914
|
def _do_cat(self, filename: str):
|
|
872
915
|
print(try_decode(self.afc.get_file_contents(self.relative_path(filename))))
|
|
873
916
|
|
|
874
|
-
def _do_rm(self, file: Annotated[list[str], Arg(nargs=
|
|
917
|
+
def _do_rm(self, file: Annotated[list[str], Arg(nargs="+", completer=path_completer)]):
|
|
875
918
|
for filename in file:
|
|
876
919
|
self.afc.rm(self.relative_path(filename))
|
|
877
920
|
|
|
878
|
-
def _do_pull(
|
|
879
|
-
|
|
921
|
+
def _do_pull(
|
|
922
|
+
self,
|
|
923
|
+
remote_path: Annotated[str, Arg(completer=path_completer)],
|
|
924
|
+
local_path: str,
|
|
925
|
+
ignore_errors: bool = False,
|
|
926
|
+
progress_bar: bool = False,
|
|
927
|
+
) -> None:
|
|
880
928
|
"""
|
|
881
929
|
Pull a file or directory from device to local machine.
|
|
882
930
|
|
|
@@ -889,14 +937,20 @@ class AfcShell:
|
|
|
889
937
|
"""
|
|
890
938
|
|
|
891
939
|
def log(src, dst):
|
|
892
|
-
print(f
|
|
893
|
-
|
|
894
|
-
self.afc.pull(
|
|
895
|
-
|
|
940
|
+
print(f"{src} --> {dst}")
|
|
941
|
+
|
|
942
|
+
self.afc.pull(
|
|
943
|
+
remote_path,
|
|
944
|
+
local_path,
|
|
945
|
+
callback=log,
|
|
946
|
+
src_dir=self.cwd,
|
|
947
|
+
ignore_errors=ignore_errors,
|
|
948
|
+
progress_bar=progress_bar,
|
|
949
|
+
)
|
|
896
950
|
|
|
897
951
|
def _do_push(self, local_path: str, remote_path: Annotated[str, Arg(completer=path_completer)]):
|
|
898
952
|
def log(src, dst):
|
|
899
|
-
print(f
|
|
953
|
+
print(f"{src} --> {dst}")
|
|
900
954
|
|
|
901
955
|
self.afc.push(local_path, self.relative_path(remote_path), callback=log)
|
|
902
956
|
|
|
@@ -904,36 +958,41 @@ class AfcShell:
|
|
|
904
958
|
print(try_decode(self.afc.get_file_contents(self.relative_path(filename))[:32]))
|
|
905
959
|
|
|
906
960
|
def _do_hexdump(self, filename: Annotated[str, Arg(completer=path_completer)]):
|
|
907
|
-
print(hexdump.hexdump(self.afc.get_file_contents(self.relative_path(filename)), result=
|
|
961
|
+
print(hexdump.hexdump(self.afc.get_file_contents(self.relative_path(filename)), result="return"))
|
|
908
962
|
|
|
909
963
|
def _do_mkdir(self, filename: Annotated[str, Arg(completer=path_completer)]):
|
|
910
964
|
self.afc.makedirs(self.relative_path(filename))
|
|
911
965
|
|
|
912
966
|
def _do_info(self):
|
|
913
967
|
for k, v in self.afc.get_device_info().items():
|
|
914
|
-
print(f
|
|
968
|
+
print(f"{k}: {v}")
|
|
915
969
|
|
|
916
|
-
def _do_mv(
|
|
917
|
-
|
|
970
|
+
def _do_mv(
|
|
971
|
+
self, source: Annotated[str, Arg(completer=path_completer)], dest: Annotated[str, Arg(completer=path_completer)]
|
|
972
|
+
):
|
|
918
973
|
return self.afc.rename(self.relative_path(source), self.relative_path(dest))
|
|
919
974
|
|
|
920
975
|
def _do_stat(self, filename: Annotated[str, Arg(completer=path_completer)]):
|
|
921
976
|
for k, v in self.afc.stat(self.relative_path(filename)).items():
|
|
922
|
-
print(f
|
|
977
|
+
print(f"{k}: {v}")
|
|
923
978
|
|
|
924
979
|
def relative_path(self, filename: str) -> str:
|
|
925
980
|
return posixpath.join(self.cwd, filename)
|
|
926
981
|
|
|
927
982
|
def _update_prompt(self) -> None:
|
|
928
|
-
self.prompt = highlight(
|
|
929
|
-
|
|
983
|
+
self.prompt = highlight(
|
|
984
|
+
f"[{self.afc.service_name}:{self.cwd}]$ ",
|
|
985
|
+
lexers.BashSessionLexer(),
|
|
986
|
+
formatters.Terminal256Formatter(style="solarized-dark"),
|
|
987
|
+
).strip()
|
|
930
988
|
|
|
931
989
|
def _complete(self, text, line, begidx, endidx):
|
|
932
990
|
curdir_diff = posixpath.dirname(text)
|
|
933
991
|
dirname = posixpath.join(self.cwd, curdir_diff)
|
|
934
992
|
prefix = posixpath.basename(text)
|
|
935
993
|
return [
|
|
936
|
-
str(posixpath.join(curdir_diff, filename))
|
|
994
|
+
str(posixpath.join(curdir_diff, filename))
|
|
995
|
+
for filename in self.afc.listdir(dirname)
|
|
937
996
|
if filename.startswith(prefix)
|
|
938
997
|
]
|
|
939
998
|
|
|
@@ -973,9 +1032,9 @@ class AfcShell:
|
|
|
973
1032
|
|
|
974
1033
|
|
|
975
1034
|
if __name__ == str(pathlib.Path(__file__).absolute()):
|
|
976
|
-
rc = XSH.ctx[
|
|
1035
|
+
rc = XSH.ctx["_class"](XSH.ctx["_lockdown"], XSH.ctx["_service"])
|
|
977
1036
|
# fix fzf conflicts
|
|
978
|
-
XSH.env[
|
|
979
|
-
XSH.env[
|
|
980
|
-
XSH.env[
|
|
981
|
-
XSH.env[
|
|
1037
|
+
XSH.env["fzf_history_binding"] = "" # Ctrl+R
|
|
1038
|
+
XSH.env["fzf_ssh_binding"] = "" # Ctrl+S
|
|
1039
|
+
XSH.env["fzf_file_binding"] = "" # Ctrl+T
|
|
1040
|
+
XSH.env["fzf_dir_binding"] = "" # Ctrl+G
|