pymobiledevice3 6.1.4__py3-none-any.whl → 6.1.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.
@@ -28,7 +28,7 @@ version_tuple: VERSION_TUPLE
28
28
  commit_id: COMMIT_ID
29
29
  __commit_id__: COMMIT_ID
30
30
 
31
- __version__ = version = '6.1.4'
32
- __version_tuple__ = version_tuple = (6, 1, 4)
31
+ __version__ = version = '6.1.6'
32
+ __version_tuple__ = version_tuple = (6, 1, 6)
33
33
 
34
34
  __commit_id__ = commit_id = None
@@ -177,7 +177,7 @@ def syslog_live(
177
177
  @click.option("include_label", "--label", is_flag=True, help="should include label")
178
178
  @click.option("-e", "--regex", multiple=True, help="filter only lines matching given regex")
179
179
  @click.option("-ei", "--insensitive-regex", multiple=True, help="filter only lines matching given regex (insensitive)")
180
- @click.option("-im", "--image-offset", is_flag=True, help="Include image offset in log line")
180
+ @click.option("-io", "--image-offset", is_flag=True, help="Include image offset in log line")
181
181
  def cli_syslog_live(
182
182
  service_provider: LockdownServiceProvider,
183
183
  out: Optional[TextIO],
@@ -18,13 +18,15 @@ import struct
18
18
  import sys
19
19
  import warnings
20
20
  from collections import namedtuple
21
+ from dataclasses import dataclass, field
21
22
  from datetime import datetime
22
23
  from re import Pattern
23
24
  from typing import Callable, Optional, Union
24
25
 
25
26
  import hexdump
26
27
  from click.exceptions import Exit
27
- from construct import Const, Container, CString, Enum, GreedyRange, Int64ul, Struct, Tell
28
+ from construct import Const, CString, GreedyRange, Int64ul, Tell
29
+ from construct_typed import DataclassMixin, EnumBase, TEnum, TStruct, csfield
28
30
  from parameter_decorators import path_to_str
29
31
  from pygments import formatters, highlight, lexers
30
32
  from pygnuutils.cli.ls import ls as ls_cli
@@ -63,188 +65,210 @@ StatResult = namedtuple(
63
65
  ],
64
66
  )
65
67
 
66
- afc_opcode_t = Enum(
67
- Int64ul,
68
- STATUS=0x00000001,
69
- DATA=0x00000002, # Data */
70
- READ_DIR=0x00000003, # ReadDir */
71
- READ_FILE=0x00000004, # ReadFile */
72
- WRITE_FILE=0x00000005, # WriteFile */
73
- WRITE_PART=0x00000006, # WritePart */
74
- TRUNCATE=0x00000007, # TruncateFile */
75
- REMOVE_PATH=0x00000008, # RemovePath */
76
- MAKE_DIR=0x00000009, # MakeDir */
77
- GET_FILE_INFO=0x0000000A, # GetFileInfo */
78
- GET_DEVINFO=0x0000000B, # GetDeviceInfo */
79
- WRITE_FILE_ATOM=0x0000000C, # WriteFileAtomic (tmp file+rename) */
80
- FILE_OPEN=0x0000000D, # FileRefOpen */
81
- FILE_OPEN_RES=0x0000000E, # FileRefOpenResult */
82
- READ=0x0000000F, # FileRefRead */
83
- WRITE=0x00000010, # FileRefWrite */
84
- FILE_SEEK=0x00000011, # FileRefSeek */
85
- FILE_TELL=0x00000012, # FileRefTell */
86
- FILE_TELL_RES=0x00000013, # FileRefTellResult */
87
- FILE_CLOSE=0x00000014, # FileRefClose */
88
- FILE_SET_SIZE=0x00000015, # FileRefSetFileSize (ftruncate) */
89
- GET_CON_INFO=0x00000016, # GetConnectionInfo */
90
- SET_CON_OPTIONS=0x00000017, # SetConnectionOptions */
91
- RENAME_PATH=0x00000018, # RenamePath */
92
- SET_FS_BS=0x00000019, # SetFSBlockSize (0x800000) */
93
- SET_SOCKET_BS=0x0000001A, # SetSocketBlockSize (0x800000) */
94
- FILE_LOCK=0x0000001B, # FileRefLock */
95
- MAKE_LINK=0x0000001C, # MakeLink */
96
- SET_FILE_TIME=0x0000001E, # set st_mtime */
97
- )
98
-
99
- afc_error_t = Enum(
100
- Int64ul,
101
- SUCCESS=0,
102
- UNKNOWN_ERROR=1,
103
- OP_HEADER_INVALID=2,
104
- NO_RESOURCES=3,
105
- READ_ERROR=4,
106
- WRITE_ERROR=5,
107
- UNKNOWN_PACKET_TYPE=6,
108
- INVALID_ARG=7,
109
- OBJECT_NOT_FOUND=8,
110
- OBJECT_IS_DIR=9,
111
- PERM_DENIED=10,
112
- SERVICE_NOT_CONNECTED=11,
113
- OP_TIMEOUT=12,
114
- TOO_MUCH_DATA=13,
115
- END_OF_DATA=14,
116
- OP_NOT_SUPPORTED=15,
117
- OBJECT_EXISTS=16,
118
- OBJECT_BUSY=17,
119
- NO_SPACE_LEFT=18,
120
- OP_WOULD_BLOCK=19,
121
- IO_ERROR=20,
122
- OP_INTERRUPTED=21,
123
- OP_IN_PROGRESS=22,
124
- INTERNAL_ERROR=23,
125
- MUX_ERROR=30,
126
- NO_MEM=31,
127
- NOT_ENOUGH_DATA=32,
128
- DIR_NOT_EMPTY=33,
129
- )
130
-
131
- afc_link_type_t = Enum(
132
- Int64ul,
133
- HARDLINK=1,
134
- SYMLINK=2,
135
- )
136
68
 
137
- afc_fopen_mode_t = Enum(
138
- Int64ul,
139
- RDONLY=0x00000001, # /**< r O_RDONLY */
140
- RW=0x00000002, # /**< r+ O_RDWR | O_CREAT */
141
- WRONLY=0x00000003, # /**< w O_WRONLY | O_CREAT | O_TRUNC */
142
- WR=0x00000004, # /**< w+ O_RDWR | O_CREAT | O_TRUNC */
143
- APPEND=0x00000005, # /**< a O_WRONLY | O_APPEND | O_CREAT */
144
- RDAPPEND=0x00000006, # /**< a+ O_RDWR | O_APPEND | O_CREAT */
145
- )
69
+ class AfcOpcode(EnumBase):
70
+ STATUS = 0x00000001
71
+ DATA = 0x00000002 # Data
72
+ READ_DIR = 0x00000003 # ReadDir
73
+ READ_FILE = 0x00000004 # ReadFile
74
+ WRITE_FILE = 0x00000005 # WriteFile
75
+ WRITE_PART = 0x00000006 # WritePart
76
+ TRUNCATE = 0x00000007 # TruncateFile
77
+ REMOVE_PATH = 0x00000008 # RemovePath
78
+ MAKE_DIR = 0x00000009 # MakeDir
79
+ GET_FILE_INFO = 0x0000000A # GetFileInfo
80
+ GET_DEVINFO = 0x0000000B # GetDeviceInfo
81
+ WRITE_FILE_ATOM = 0x0000000C # WriteFileAtomic (tmp file+rename)
82
+ FILE_OPEN = 0x0000000D # FileRefOpen
83
+ FILE_OPEN_RES = 0x0000000E # FileRefOpenResult
84
+ READ = 0x0000000F # FileRefRead
85
+ WRITE = 0x00000010 # FileRefWrite
86
+ FILE_SEEK = 0x00000011 # FileRefSeek
87
+ FILE_TELL = 0x00000012 # FileRefTell
88
+ FILE_TELL_RES = 0x00000013 # FileRefTellResult
89
+ FILE_CLOSE = 0x00000014 # FileRefClose
90
+ FILE_SET_SIZE = 0x00000015 # FileRefSetFileSize (ftruncate)
91
+ GET_CON_INFO = 0x00000016 # GetConnectionInfo
92
+ SET_CON_OPTIONS = 0x00000017 # SetConnectionOptions
93
+ RENAME_PATH = 0x00000018 # RenamePath
94
+ SET_FS_BS = 0x00000019 # SetFSBlockSize (0x800000)
95
+ SET_SOCKET_BS = 0x0000001A # SetSocketBlockSize (0x800000)
96
+ FILE_LOCK = 0x0000001B # FileRefLock
97
+ MAKE_LINK = 0x0000001C # MakeLink
98
+ SET_FILE_TIME = 0x0000001E # set st_mtime
99
+
100
+
101
+ class AfcError(EnumBase):
102
+ SUCCESS = 0
103
+ UNKNOWN_ERROR = 1
104
+ OP_HEADER_INVALID = 2
105
+ NO_RESOURCES = 3
106
+ READ_ERROR = 4
107
+ WRITE_ERROR = 5
108
+ UNKNOWN_PACKET_TYPE = 6
109
+ INVALID_ARG = 7
110
+ OBJECT_NOT_FOUND = 8
111
+ OBJECT_IS_DIR = 9
112
+ PERM_DENIED = 10
113
+ SERVICE_NOT_CONNECTED = 11
114
+ OP_TIMEOUT = 12
115
+ TOO_MUCH_DATA = 13
116
+ END_OF_DATA = 14
117
+ OP_NOT_SUPPORTED = 15
118
+ OBJECT_EXISTS = 16
119
+ OBJECT_BUSY = 17
120
+ NO_SPACE_LEFT = 18
121
+ OP_WOULD_BLOCK = 19
122
+ IO_ERROR = 20
123
+ OP_INTERRUPTED = 21
124
+ OP_IN_PROGRESS = 22
125
+ INTERNAL_ERROR = 23
126
+ MUX_ERROR = 30
127
+ NO_MEM = 31
128
+ NOT_ENOUGH_DATA = 32
129
+ DIR_NOT_EMPTY = 33
130
+
131
+
132
+ class AfcLinkType(EnumBase):
133
+ HARDLINK = 1
134
+ SYMLINK = 2
135
+
136
+
137
+ class AfcFopenMode(EnumBase):
138
+ RDONLY = 0x00000001 # r O_RDONLY
139
+ RW = 0x00000002 # r+ O_RDWR | O_CREAT
140
+ WRONLY = 0x00000003 # w O_WRONLY | O_CREAT | O_TRUNC
141
+ WR = 0x00000004 # w+ O_RDWR | O_CREAT | O_TRUNC
142
+ APPEND = 0x00000005 # a O_WRONLY | O_APPEND | O_CREAT
143
+ RDAPPEND = 0x00000006 # a+ O_RDWR | O_APPEND | O_CREAT
144
+
145
+
146
+ # typed construct adapters for the enums
147
+ afc_opcode_t = TEnum(Int64ul, AfcOpcode)
148
+ afc_error_construct = TEnum(Int64ul, AfcError)
149
+ afc_link_type_construct = TEnum(Int64ul, AfcLinkType)
150
+ afc_fopen_mode_construct = TEnum(Int64ul, AfcFopenMode)
146
151
 
147
152
  AFC_FOPEN_TEXTUAL_MODES = {
148
- "r": afc_fopen_mode_t.RDONLY,
149
- "r+": afc_fopen_mode_t.RW,
150
- "w": afc_fopen_mode_t.WRONLY,
151
- "w+": afc_fopen_mode_t.WR,
152
- "a": afc_fopen_mode_t.APPEND,
153
- "a+": afc_fopen_mode_t.RDAPPEND,
153
+ "r": AfcFopenMode.RDONLY,
154
+ "r+": AfcFopenMode.RW,
155
+ "w": AfcFopenMode.WRONLY,
156
+ "w+": AfcFopenMode.WR,
157
+ "a": AfcFopenMode.APPEND,
158
+ "a+": AfcFopenMode.RDAPPEND,
154
159
  }
155
160
 
156
- AFC_LOCK_SH = 1 | 4 # /**< shared lock */
157
- AFC_LOCK_EX = 2 | 4 # /**< exclusive lock */
158
- AFC_LOCK_UN = 8 | 4 # /**< unlock */
161
+ AFC_LOCK_SH = 1 | 4 # shared lock
162
+ AFC_LOCK_EX = 2 | 4 # exclusive lock
163
+ AFC_LOCK_UN = 8 | 4 # unlock
159
164
 
160
165
  MAXIMUM_WRITE_SIZE = 1 << 30
161
166
 
162
167
  AFCMAGIC = b"CFA6LPAA"
163
168
 
164
- afc_header_t = Struct(
165
- "magic" / Const(AFCMAGIC),
166
- "entire_length" / Int64ul,
167
- "this_length" / Int64ul,
168
- "packet_num" / Int64ul,
169
- "operation" / afc_opcode_t,
170
- "_data_offset" / Tell,
171
- )
172
169
 
173
- afc_read_dir_req_t = Struct(
174
- "filename" / CString("utf8"),
175
- )
170
+ @dataclass
171
+ class AfcHeader(DataclassMixin):
172
+ magic: bytes = csfield(Const(AFCMAGIC))
173
+ entire_length: int = csfield(Int64ul)
174
+ this_length: int = csfield(Int64ul)
175
+ packet_num: int = csfield(Int64ul)
176
+ operation: AfcOpcode = csfield(afc_opcode_t)
177
+ _data_offset: int = field(default=0, init=False, metadata={"subcon": Tell})
176
178
 
177
- afc_read_dir_resp_t = Struct(
178
- "filenames" / GreedyRange(CString("utf8")),
179
- )
180
179
 
181
- afc_mkdir_req_t = Struct(
182
- "filename" / CString("utf8"),
183
- )
180
+ @dataclass
181
+ class AfcReadDirRequest(DataclassMixin):
182
+ filename: str = csfield(CString("utf8"))
184
183
 
185
- afc_stat_t = Struct(
186
- "filename" / CString("utf8"),
187
- )
188
184
 
189
- afc_make_link_req_t = Struct(
190
- "type" / afc_link_type_t,
191
- "target" / CString("utf8"),
192
- "source" / CString("utf8"),
193
- )
185
+ @dataclass
186
+ class AfcReadDirResponse(DataclassMixin):
187
+ filenames: list[str] = csfield(GreedyRange(CString("utf8")))
194
188
 
195
- afc_fopen_req_t = Struct(
196
- "mode" / afc_fopen_mode_t,
197
- "filename" / CString("utf8"),
198
- )
199
189
 
200
- afc_fopen_resp_t = Struct(
201
- "handle" / Int64ul,
202
- )
190
+ @dataclass
191
+ class AfcMkdirRequest(DataclassMixin):
192
+ filename: str = csfield(CString("utf8"))
203
193
 
204
- afc_fclose_req_t = Struct(
205
- "handle" / Int64ul,
206
- )
207
194
 
208
- afc_rm_req_t = Struct(
209
- "filename" / CString("utf8"),
210
- )
195
+ @dataclass
196
+ class AfcStatRequest(DataclassMixin):
197
+ filename: str = csfield(CString("utf8"))
211
198
 
212
- afc_rename_req_t = Struct(
213
- "source" / CString("utf8"),
214
- "target" / CString("utf8"),
215
- )
216
199
 
217
- afc_fread_req_t = Struct(
218
- "handle" / Int64ul,
219
- "size" / Int64ul,
220
- )
200
+ @dataclass
201
+ class AfcMakeLinkRequest(DataclassMixin):
202
+ type: AfcLinkType = csfield(afc_link_type_construct)
203
+ target: str = csfield(CString("utf8"))
204
+ source: str = csfield(CString("utf8"))
221
205
 
222
- afc_lock_t = Struct(
223
- "handle" / Int64ul,
224
- "op" / Int64ul,
225
- )
206
+
207
+ @dataclass
208
+ class AfcFopenRequest(DataclassMixin):
209
+ mode: AfcFopenMode = csfield(afc_fopen_mode_construct)
210
+ filename: str = csfield(CString("utf8"))
226
211
 
227
212
 
228
- def list_to_dict(d):
213
+ @dataclass
214
+ class AfcFopenResponse(DataclassMixin):
215
+ handle: int = csfield(Int64ul)
216
+
217
+
218
+ @dataclass
219
+ class AfcFcloseRequest(DataclassMixin):
220
+ handle: int = csfield(Int64ul)
221
+
222
+
223
+ @dataclass
224
+ class AfcRmRequest(DataclassMixin):
225
+ filename: str = csfield(CString("utf8"))
226
+
227
+
228
+ @dataclass
229
+ class AfcRenameRequest(DataclassMixin):
230
+ source: str = csfield(CString("utf8"))
231
+ target: str = csfield(CString("utf8"))
232
+
233
+
234
+ @dataclass
235
+ class AfcFreadRequest(DataclassMixin):
236
+ handle: int = csfield(Int64ul)
237
+ size: int = csfield(Int64ul)
238
+
239
+
240
+ @dataclass
241
+ class AfcLockRequest(DataclassMixin):
242
+ handle: int = csfield(Int64ul)
243
+ op: int = csfield(Int64ul)
244
+
245
+
246
+ afc_header_t = TStruct(AfcHeader)
247
+ afc_read_dir_req_t = TStruct(AfcReadDirRequest)
248
+ afc_read_dir_resp_t = TStruct(AfcReadDirResponse)
249
+ afc_mkdir_req_t = TStruct(AfcMkdirRequest)
250
+ afc_stat_t = TStruct(AfcStatRequest)
251
+ afc_make_link_req_t = TStruct(AfcMakeLinkRequest)
252
+ afc_fopen_req_t = TStruct(AfcFopenRequest)
253
+ afc_fopen_resp_t = TStruct(AfcFopenResponse)
254
+ afc_fclose_req_t = TStruct(AfcFcloseRequest)
255
+ afc_rm_req_t = TStruct(AfcRmRequest)
256
+ afc_rename_req_t = TStruct(AfcRenameRequest)
257
+ afc_fread_req_t = TStruct(AfcFreadRequest)
258
+ afc_lock_t = TStruct(AfcLockRequest)
259
+
260
+
261
+ def list_to_dict(raw: bytes) -> dict[str, str]:
229
262
  """
230
263
  Convert a null-terminated key-value list to a dictionary.
231
264
 
232
265
  The input is expected to be a byte string with alternating keys and values,
233
- each separated by null bytes (\x00).
234
-
235
- :param d: Byte string containing null-terminated key-value pairs
236
- :return: Dictionary mapping keys to values
237
- :raises: AssertionError if the list doesn't contain an even number of elements
266
+ each separated by null bytes (``\\x00``).
238
267
  """
239
- d = d.decode("utf-8")
240
- t = d.split("\x00")
241
- t = t[:-1]
242
-
243
- assert len(t) % 2 == 0
244
- res = {}
245
- for i in range(int(len(t) / 2)):
246
- res[t[i * 2]] = t[i * 2 + 1]
247
- return res
268
+ parts = raw.decode("utf-8").split("\x00")[:-1]
269
+ if len(parts) % 2:
270
+ raise ValueError("AFC list is not key/value aligned")
271
+ return dict(zip(parts[::2], parts[1::2]))
248
272
 
249
273
 
250
274
  class AfcService(LockdownService):
@@ -314,15 +338,13 @@ class AfcService(LockdownService):
314
338
  if src_size <= MAXIMUM_READ_SIZE:
315
339
  f.write(self.get_file_contents(src))
316
340
  else:
317
- left_size = src_size
318
341
  handle = self.fopen(src)
342
+ iterator = range(0, src_size, MAXIMUM_READ_SIZE)
319
343
  if progress_bar:
320
- pb = trange(src_size // MAXIMUM_READ_SIZE + 1)
321
- else:
322
- pb = range(src_size // MAXIMUM_READ_SIZE + 1)
323
- for _ in pb:
324
- f.write(self.fread(handle, min(MAXIMUM_READ_SIZE, left_size)))
325
- left_size -= MAXIMUM_READ_SIZE
344
+ iterator = trange(0, src_size, MAXIMUM_READ_SIZE)
345
+ for offset in iterator:
346
+ to_read = min(MAXIMUM_READ_SIZE, src_size - offset)
347
+ f.write(self.fread(handle, to_read))
326
348
  self.fclose(handle)
327
349
  os.utime(dst, (os.stat(dst).st_atime, self.stat(src)["st_mtime"].timestamp()))
328
350
  if callback is not None:
@@ -464,7 +486,7 @@ class AfcService(LockdownService):
464
486
  :rtype: bool
465
487
  """
466
488
  try:
467
- self._do_operation(afc_opcode_t.REMOVE_PATH, afc_rm_req_t.build({"filename": filename}), filename)
489
+ self._do_operation(AfcOpcode.REMOVE_PATH, afc_rm_req_t.build(AfcRmRequest(filename=filename)), filename)
468
490
  except AfcException:
469
491
  if force:
470
492
  return False
@@ -532,7 +554,7 @@ class AfcService(LockdownService):
532
554
 
533
555
  :return: Dictionary containing device file system information
534
556
  """
535
- return list_to_dict(self._do_operation(afc_opcode_t.GET_DEVINFO))
557
+ return list_to_dict(self._do_operation(AfcOpcode.GET_DEVINFO))
536
558
 
537
559
  @path_to_str()
538
560
  def listdir(self, filename: str):
@@ -543,7 +565,7 @@ class AfcService(LockdownService):
543
565
  :return: List of filenames in the directory (excluding '.' and '..')
544
566
  :raises: AfcException if the path is not a directory or doesn't exist
545
567
  """
546
- data = self._do_operation(afc_opcode_t.READ_DIR, afc_read_dir_req_t.build({"filename": filename}))
568
+ data = self._do_operation(AfcOpcode.READ_DIR, afc_read_dir_req_t.build(AfcReadDirRequest(filename=filename)))
547
569
  return afc_read_dir_resp_t.parse(data).filenames[2:] # skip the . and ..
548
570
 
549
571
  @path_to_str()
@@ -556,7 +578,7 @@ class AfcService(LockdownService):
556
578
  :param filename: Path of the directory to create
557
579
  :return: Response data from the operation
558
580
  """
559
- return self._do_operation(afc_opcode_t.MAKE_DIR, afc_mkdir_req_t.build({"filename": filename}))
581
+ return self._do_operation(AfcOpcode.MAKE_DIR, afc_mkdir_req_t.build(AfcMkdirRequest(filename=filename)))
560
582
 
561
583
  @path_to_str()
562
584
  def isdir(self, filename: str) -> bool:
@@ -580,10 +602,12 @@ class AfcService(LockdownService):
580
602
  """
581
603
  try:
582
604
  stat = list_to_dict(
583
- self._do_operation(afc_opcode_t.GET_FILE_INFO, afc_stat_t.build({"filename": filename}), filename)
605
+ self._do_operation(
606
+ AfcOpcode.GET_FILE_INFO, afc_stat_t.build(AfcStatRequest(filename=filename)), filename
607
+ )
584
608
  )
585
609
  except AfcException as e:
586
- if e.status != afc_error_t.READ_ERROR:
610
+ if e.status != AfcError.READ_ERROR:
587
611
  raise
588
612
  raise AfcFileNotFoundError(e.args[0], e.status) from e
589
613
 
@@ -629,7 +653,7 @@ class AfcService(LockdownService):
629
653
  )
630
654
 
631
655
  @path_to_str()
632
- def link(self, target: str, source: str, type_=afc_link_type_t.SYMLINK):
656
+ def link(self, target: str, source: str, type_=AfcLinkType.SYMLINK):
633
657
  """
634
658
  Create a symbolic or hard link on the device.
635
659
 
@@ -639,7 +663,8 @@ class AfcService(LockdownService):
639
663
  :return: Response data from the operation
640
664
  """
641
665
  return self._do_operation(
642
- afc_opcode_t.MAKE_LINK, afc_make_link_req_t.build({"type": type_, "target": target, "source": source})
666
+ AfcOpcode.MAKE_LINK,
667
+ afc_make_link_req_t.build(AfcMakeLinkRequest(type=type_, target=target, source=source)),
643
668
  )
644
669
 
645
670
  @path_to_str()
@@ -656,7 +681,10 @@ class AfcService(LockdownService):
656
681
  raise ArgumentError(f"mode can be only one of: {AFC_FOPEN_TEXTUAL_MODES.keys()}")
657
682
 
658
683
  data = self._do_operation(
659
- afc_opcode_t.FILE_OPEN, afc_fopen_req_t.build({"mode": AFC_FOPEN_TEXTUAL_MODES[mode], "filename": filename})
684
+ AfcOpcode.FILE_OPEN,
685
+ afc_fopen_req_t.build(
686
+ AfcFopenRequest(mode=AFC_FOPEN_TEXTUAL_MODES[mode], filename=filename),
687
+ ),
660
688
  )
661
689
  return afc_fopen_resp_t.parse(data).handle
662
690
 
@@ -667,7 +695,7 @@ class AfcService(LockdownService):
667
695
  :param handle: File handle returned from fopen
668
696
  :return: Response data from the operation
669
697
  """
670
- return self._do_operation(afc_opcode_t.FILE_CLOSE, afc_fclose_req_t.build({"handle": handle}))
698
+ return self._do_operation(AfcOpcode.FILE_CLOSE, afc_fclose_req_t.build(AfcFcloseRequest(handle=handle)))
671
699
 
672
700
  @path_to_str()
673
701
  def rename(self, source: str, target: str) -> None:
@@ -680,8 +708,8 @@ class AfcService(LockdownService):
680
708
  """
681
709
  try:
682
710
  self._do_operation(
683
- afc_opcode_t.RENAME_PATH,
684
- afc_rename_req_t.build({"source": source, "target": target}, filename=f"{source}->{target}"),
711
+ AfcOpcode.RENAME_PATH,
712
+ afc_rename_req_t.build(AfcRenameRequest(source=source, target=target)),
685
713
  )
686
714
  except AfcException as e:
687
715
  if self.exists(source):
@@ -690,7 +718,7 @@ class AfcService(LockdownService):
690
718
  f"Failed to rename {source} into {target}. Got status: {e.status}", e.args[0], str(e.status)
691
719
  ) from e
692
720
 
693
- def fread(self, handle: int, sz: bytes) -> bytes:
721
+ def fread(self, handle: int, sz: int) -> bytes:
694
722
  """
695
723
  Read data from an open file handle.
696
724
 
@@ -704,15 +732,15 @@ class AfcService(LockdownService):
704
732
  data = b""
705
733
  while sz > 0:
706
734
  to_read = MAXIMUM_READ_SIZE if sz > MAXIMUM_READ_SIZE else sz
707
- self._dispatch_packet(afc_opcode_t.READ, afc_fread_req_t.build({"handle": handle, "size": to_read}))
735
+ self._dispatch_packet(AfcOpcode.READ, afc_fread_req_t.build(AfcFreadRequest(handle=handle, size=to_read)))
708
736
  status, chunk = self._receive_data()
709
- if status != afc_error_t.SUCCESS:
737
+ if status != AfcError.SUCCESS:
710
738
  raise AfcException("fread error", status)
711
739
  sz -= to_read
712
740
  data += chunk
713
741
  return data
714
742
 
715
- def fwrite(self, handle, data, chunk_size=MAXIMUM_WRITE_SIZE):
743
+ def fwrite(self, handle: int, data: bytes, chunk_size: int = MAXIMUM_WRITE_SIZE) -> None:
716
744
  """
717
745
  Write data to an open file handle.
718
746
 
@@ -725,24 +753,20 @@ class AfcService(LockdownService):
725
753
  """
726
754
  file_handle = struct.pack("<Q", handle)
727
755
  chunks_count = len(data) // chunk_size
728
- b = b""
729
756
  for i in range(chunks_count):
730
757
  chunk = data[i * chunk_size : (i + 1) * chunk_size]
731
- self._dispatch_packet(afc_opcode_t.WRITE, file_handle + chunk, this_length=48)
732
- b += chunk
758
+ self._dispatch_packet(AfcOpcode.WRITE, file_handle + chunk, this_length=48)
733
759
 
734
760
  status, _response = self._receive_data()
735
- if status != afc_error_t.SUCCESS:
761
+ if status != AfcError.SUCCESS:
736
762
  raise AfcException(f"failed to write chunk: {status}", status)
737
763
 
738
764
  if len(data) % chunk_size:
739
765
  chunk = data[chunks_count * chunk_size :]
740
- self._dispatch_packet(afc_opcode_t.WRITE, file_handle + chunk, this_length=48)
741
-
742
- b += chunk
766
+ self._dispatch_packet(AfcOpcode.WRITE, file_handle + chunk, this_length=48)
743
767
 
744
768
  status, _response = self._receive_data()
745
- if status != afc_error_t.SUCCESS:
769
+ if status != AfcError.SUCCESS:
746
770
  raise AfcException(f"failed to write last chunk: {status}", status)
747
771
 
748
772
  @path_to_str()
@@ -777,7 +801,7 @@ class AfcService(LockdownService):
777
801
  info = self.stat(filename)
778
802
 
779
803
  if info["st_ifmt"] != "S_IFREG":
780
- raise AfcException(f"{filename} isn't a file", afc_error_t.INVALID_ARG)
804
+ raise AfcException(f"{filename} isn't a file", AfcError.INVALID_ARG)
781
805
 
782
806
  h = self.fopen(filename)
783
807
  if not h:
@@ -855,9 +879,9 @@ class AfcService(LockdownService):
855
879
  :param operation: Lock operation (AFC_LOCK_SH, AFC_LOCK_EX, or AFC_LOCK_UN)
856
880
  :return: Response data from the operation
857
881
  """
858
- return self._do_operation(afc_opcode_t.FILE_LOCK, afc_lock_t.build({"handle": handle, "op": operation}))
882
+ return self._do_operation(AfcOpcode.FILE_LOCK, afc_lock_t.build(AfcLockRequest(handle=handle, op=operation)))
859
883
 
860
- def _dispatch_packet(self, operation, data, this_length=0):
884
+ def _dispatch_packet(self, operation: AfcOpcode, data: bytes, this_length: int = 0) -> None:
861
885
  """
862
886
  Send an AFC protocol packet to the device.
863
887
 
@@ -865,16 +889,15 @@ class AfcService(LockdownService):
865
889
  :param data: Packet payload data
866
890
  :param this_length: Override for the packet length field (0 for auto-calculation)
867
891
  """
868
- afcpack = Container(
869
- magic=AFCMAGIC,
870
- entire_length=afc_header_t.sizeof() + len(data),
871
- this_length=afc_header_t.sizeof() + len(data),
872
- packet_num=self.packet_num,
873
- operation=operation,
892
+ entire_length = afc_header_t.sizeof() + len(data)
893
+ header = afc_header_t.build(
894
+ AfcHeader(
895
+ entire_length=entire_length,
896
+ this_length=this_length or entire_length,
897
+ packet_num=self.packet_num,
898
+ operation=operation,
899
+ )
874
900
  )
875
- if this_length:
876
- afcpack.this_length = this_length
877
- header = afc_header_t.build(afcpack)
878
901
  self.packet_num += 1
879
902
  self.service.sendall(header + data)
880
903
 
@@ -884,23 +907,23 @@ class AfcService(LockdownService):
884
907
 
885
908
  :return: Tuple of (status_code, response_data)
886
909
  """
887
- res = self.service.recvall(afc_header_t.sizeof())
888
- status = afc_error_t.SUCCESS
889
- data = ""
890
- if res:
891
- res = afc_header_t.parse(res)
892
- assert res["entire_length"] >= afc_header_t.sizeof()
893
- length = res["entire_length"] - afc_header_t.sizeof()
910
+ header_bytes = self.service.recvall(afc_header_t.sizeof())
911
+ status = AfcError.SUCCESS
912
+ data = b""
913
+ if header_bytes:
914
+ header = afc_header_t.parse(header_bytes)
915
+ assert header.entire_length >= afc_header_t.sizeof()
916
+ length = header.entire_length - afc_header_t.sizeof()
894
917
  data = self.service.recvall(length)
895
- if res.operation == afc_opcode_t.STATUS:
918
+ if header.operation == AfcOpcode.STATUS:
896
919
  if length != 8:
897
920
  self.logger.error("Status length != 8")
898
- status = afc_error_t.parse(data)
899
- elif res.operation != afc_opcode_t.DATA:
900
- pass
921
+ status = afc_error_construct.parse(data)
922
+ elif header.operation != AfcOpcode.DATA:
923
+ self.logger.debug("Unexpected AFC opcode %s", header.operation)
901
924
  return status, data
902
925
 
903
- def _do_operation(self, opcode: int, data: bytes = b"", filename: Optional[str] = None) -> bytes:
926
+ def _do_operation(self, opcode: AfcOpcode, data: bytes = b"", filename: Optional[str] = None) -> bytes:
904
927
  """
905
928
  Performs a low-level operation using the specified opcode and additional data.
906
929
 
@@ -924,11 +947,12 @@ class AfcService(LockdownService):
924
947
  status, data = self._receive_data()
925
948
 
926
949
  exception = AfcException
927
- if status != afc_error_t.SUCCESS:
928
- if status == afc_error_t.OBJECT_NOT_FOUND:
950
+ if status != AfcError.SUCCESS:
951
+ if status == AfcError.OBJECT_NOT_FOUND:
929
952
  exception = AfcFileNotFoundError
930
953
 
931
- message = f"Opcode: {opcode} failed with status: {status}"
954
+ opcode_name = opcode.name if isinstance(opcode, AfcOpcode) else opcode
955
+ message = f"Opcode: {opcode_name} failed with status: {status}"
932
956
  if filename is not None:
933
957
  message += f" for file: {filename}"
934
958
  raise exception(message, status, filename)
@@ -1257,7 +1281,7 @@ class AfcShell:
1257
1281
  :param target: Target path that the link will point to
1258
1282
  :param source: Path where the link will be created
1259
1283
  """
1260
- self.afc.link(self.relative_path(target), self.relative_path(source), afc_link_type_t.SYMLINK)
1284
+ self.afc.link(self.relative_path(target), self.relative_path(source), AfcLinkType.SYMLINK)
1261
1285
 
1262
1286
  def _do_cd(self, directory: Annotated[str, Arg(completer=dir_completer)]) -> None:
1263
1287
  """
@@ -1320,8 +1344,8 @@ class AfcShell:
1320
1344
  self,
1321
1345
  remote_path: Annotated[str, Arg(completer=path_completer)],
1322
1346
  local_path: str,
1323
- ignore_errors: bool = False,
1324
- progress_bar: bool = False,
1347
+ ignore_errors: Annotated[bool, Arg("--ignore-errors", action="store_true")] = False,
1348
+ progress_bar: Annotated[bool, Arg("--progress-bar", action="store_true")] = False,
1325
1349
  ) -> None:
1326
1350
  """
1327
1351
  Pull a file or directory from device to local machine.
@@ -16,7 +16,7 @@ from pymobiledevice3.exceptions import (
16
16
  )
17
17
  from pymobiledevice3.lockdown import LockdownClient
18
18
  from pymobiledevice3.lockdown_service_provider import LockdownServiceProvider
19
- from pymobiledevice3.services.afc import AFC_LOCK_EX, AFC_LOCK_UN, AfcService, afc_error_t
19
+ from pymobiledevice3.services.afc import AFC_LOCK_EX, AFC_LOCK_UN, AfcError, AfcService
20
20
  from pymobiledevice3.services.device_link import DeviceLink
21
21
  from pymobiledevice3.services.installation_proxy import InstallationProxyService
22
22
  from pymobiledevice3.services.lockdown_service import LockdownService
@@ -386,7 +386,7 @@ class Mobilebackup2Service(LockdownService):
386
386
  try:
387
387
  afc.lock(lockfile, AFC_LOCK_EX)
388
388
  except AfcException as e:
389
- if e.status == afc_error_t.OP_WOULD_BLOCK:
389
+ if e.status == AfcError.OP_WOULD_BLOCK:
390
390
  time.sleep(0.2)
391
391
  else:
392
392
  afc.fclose(lockfile)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pymobiledevice3
3
- Version: 6.1.4
3
+ Version: 6.1.6
4
4
  Summary: Pure python3 implementation for working with iDevices (iPhone, etc...)
5
5
  Author-email: doronz88 <doron88@gmail.com>, matan <matan1008@gmail.com>
6
6
  Maintainer-email: doronz88 <doron88@gmail.com>, matan <matan1008@gmail.com>
@@ -21,6 +21,7 @@ Requires-Python: >=3.9
21
21
  Description-Content-Type: text/markdown
22
22
  License-File: LICENSE
23
23
  Requires-Dist: construct>=2.9.29
24
+ Requires-Dist: construct-typing>=0.7.0
24
25
  Requires-Dist: asn1
25
26
  Requires-Dist: click
26
27
  Requires-Dist: coloredlogs
@@ -8,7 +8,7 @@ misc/understanding_idevice_protocol_layers.md,sha256=8tEqRXWOUPoxOJLZVh7C7H9JGCh
8
8
  misc/usbmux_sniff.sh,sha256=iWtbucOEQ9_UEFXk9x-2VNt48Jg5zrPsnUbZ_LfZxwA,212
9
9
  pymobiledevice3/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
10
10
  pymobiledevice3/__main__.py,sha256=hpLgWH1Pmwkf1pX-JvYkfJUkphpEb4YhoRV3qQUwbOs,12718
11
- pymobiledevice3/_version.py,sha256=6URFpVH8xgjxdzQeg-tvWY2998pp7Jv3TRL2bsOIZrM,704
11
+ pymobiledevice3/_version.py,sha256=Ec6zPeP28xAN98SxhNS4Y3DF-kiPUdlSd205c6mn9wo,704
12
12
  pymobiledevice3/bonjour.py,sha256=y1Zd-__GnvK2ShxmvqFpNfi5NGF6PEWGcuKp8mJVFNA,13419
13
13
  pymobiledevice3/ca.py,sha256=5_Y4F-zDFX_KeDL-M_TRCKKyrRRb9h1lBE8MGTWv91o,10606
14
14
  pymobiledevice3/common.py,sha256=FZzF0BQYV5fCEUPbLo6jbt2Ig9s5YwR8AvX_iR124Ew,329
@@ -47,7 +47,7 @@ pymobiledevice3/cli/provision.py,sha256=SofHAYdmxb54n9KSRyPKol6jOMUXN1hBACr2VBQr
47
47
  pymobiledevice3/cli/remote.py,sha256=xkIVR4bu1Vdpw-sswH1b-pekBM6rV6AS7YUeYQU5UyY,12375
48
48
  pymobiledevice3/cli/restore.py,sha256=xwiUIofoyCMxDSgRq4bndAn5GNUq1P-RcC446M_6I7Y,8085
49
49
  pymobiledevice3/cli/springboard.py,sha256=hVoLqLU_gHxXa6q17ar61lc2ovmWejeu-rvxQpHXWD0,3094
50
- pymobiledevice3/cli/syslog.py,sha256=MV3fsg3lFKha2gYr6L_-7oiLXrk9QB_hH1nxNNbm3Gw,8112
50
+ pymobiledevice3/cli/syslog.py,sha256=uAr2pqHq8GTuH9uMTPYV0RQFMOv50zq4NpmGWtzcXss,8112
51
51
  pymobiledevice3/cli/usbmux.py,sha256=Bx-5OSxEbHBhLeYaRt9LvZzp3RIazn49Q86pFi-EtpQ,2354
52
52
  pymobiledevice3/cli/version.py,sha256=N7UY328WNR6FpQ2w7aACyL74XQxkRmi53WV5SVlCgCo,345
53
53
  pymobiledevice3/cli/webinspector.py,sha256=dUsaaZZBV2OQPaYCj8C809ElO0O-G-NSSLvj7x26L94,16705
@@ -100,7 +100,7 @@ pymobiledevice3/restore/restored_client.py,sha256=tv1hyIV7UBDb_OUwj_w6qJsH_x36oB
100
100
  pymobiledevice3/restore/tss.py,sha256=EY8XpUaexHyDTtUCuXQnOgLVIFAikcs9p0ysNSG1Q0U,30818
101
101
  pymobiledevice3/services/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
102
102
  pymobiledevice3/services/accessibilityaudit.py,sha256=7AvGqfAqvsSULib06mpxP94-P-Zr4SwSZohgmbXLuog,15474
103
- pymobiledevice3/services/afc.py,sha256=5hoyY8D5dSa7oxduI3OY4bAFLP4D93VwupZ-c7mKNIs,53504
103
+ pymobiledevice3/services/afc.py,sha256=cIjOyeVDRkE1iKVLpSNt2UGI6jVbOiPoS-siLTeDyWw,55008
104
104
  pymobiledevice3/services/amfi.py,sha256=SWGh5UQtf3YhD_4cT8M3dhH1sPE-mnIzkmiZjJ8d2x4,2522
105
105
  pymobiledevice3/services/companion.py,sha256=6rvL1KF2-Gflv77rL9txINMoiQplVouL_nnGKNq-Maw,2612
106
106
  pymobiledevice3/services/crash_reports.py,sha256=ODsgT3WgpOHIFM-ht9za_-xI9AAh7dKiq50NsRB5q3I,10945
@@ -119,7 +119,7 @@ pymobiledevice3/services/misagent.py,sha256=CGh1EhN_f1rV9RhZfP53OBirXdY0elX_nZS-
119
119
  pymobiledevice3/services/mobile_activation.py,sha256=0zvQn2CMmf47--OtDqv3eOK5Ofu-eBw-ab1TxZSrOlY,9230
120
120
  pymobiledevice3/services/mobile_config.py,sha256=2UmdqYNikznxI6bA2lkyIWS3NcHg3pTb5i6yLl8yZAY,18170
121
121
  pymobiledevice3/services/mobile_image_mounter.py,sha256=PZEN9O7XtLW9VH1qNTJ19zo_tFZhX68bTX0O-Uy9sck,15034
122
- pymobiledevice3/services/mobilebackup2.py,sha256=M2sVY-vXl6f25QFom-z5bUkeTdA4Twbiw1tH-4Tb79M,18303
122
+ pymobiledevice3/services/mobilebackup2.py,sha256=7hB0tNpNoLoneDNbMUmCpg0UhahT5QnF8fxSPwaqVWc,18297
123
123
  pymobiledevice3/services/notification_proxy.py,sha256=mk4kxLRi6aDTXKOazgldliZG4q0bME7jBcxPyJsSpDw,2125
124
124
  pymobiledevice3/services/os_trace.py,sha256=nTODlyWvb10fpqWHDU3m7i9sADIMF0ezfyo9Ql2BZa8,7422
125
125
  pymobiledevice3/services/pcapd.py,sha256=Gd6xN9eBHnLIQ5M2LM-y4Z-eCquxVEnSKuyBECwZlRs,11960
@@ -167,9 +167,9 @@ pymobiledevice3/services/web_protocol/switch_to.py,sha256=TCdVrMfsvd18o-vZ0owVrE
167
167
  pymobiledevice3/tunneld/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
168
168
  pymobiledevice3/tunneld/api.py,sha256=Lwl1OdhPTgX6Zqezy8T4dEcXRfaEPwyGNClioTx3fUc,2338
169
169
  pymobiledevice3/tunneld/server.py,sha256=dMEZAv_X-76l0vSalpq4x0IVkbE-MNGR77T-u1TiHuE,25752
170
- pymobiledevice3-6.1.4.dist-info/licenses/LICENSE,sha256=jOtLnuWt7d5Hsx6XXB2QxzrSe2sWWh3NgMfFRetluQM,35147
171
- pymobiledevice3-6.1.4.dist-info/METADATA,sha256=OUgbCc7yCBdqVKR7fclV7SmSmthm7qj0yP5BB7hkfiI,17416
172
- pymobiledevice3-6.1.4.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
173
- pymobiledevice3-6.1.4.dist-info/entry_points.txt,sha256=jJMlOanHlVwUxcY__JwvKeWPrvBJr_wJyEq4oHIZNKE,66
174
- pymobiledevice3-6.1.4.dist-info/top_level.txt,sha256=MjZoRqcWPOh5banG-BbDOnKEfsS3kCxqV9cv-nzyg2Q,21
175
- pymobiledevice3-6.1.4.dist-info/RECORD,,
170
+ pymobiledevice3-6.1.6.dist-info/licenses/LICENSE,sha256=jOtLnuWt7d5Hsx6XXB2QxzrSe2sWWh3NgMfFRetluQM,35147
171
+ pymobiledevice3-6.1.6.dist-info/METADATA,sha256=0M0UURfJmr_HkuOMOQO8Dqq6A8zPIR8C4iMcFkeqyvI,17455
172
+ pymobiledevice3-6.1.6.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
173
+ pymobiledevice3-6.1.6.dist-info/entry_points.txt,sha256=jJMlOanHlVwUxcY__JwvKeWPrvBJr_wJyEq4oHIZNKE,66
174
+ pymobiledevice3-6.1.6.dist-info/top_level.txt,sha256=MjZoRqcWPOh5banG-BbDOnKEfsS3kCxqV9cv-nzyg2Q,21
175
+ pymobiledevice3-6.1.6.dist-info/RECORD,,