dfindexeddb 20241031__py3-none-any.whl → 20251109__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.
Files changed (33) hide show
  1. dfindexeddb/indexeddb/chromium/blink.py +116 -74
  2. dfindexeddb/indexeddb/chromium/definitions.py +152 -124
  3. dfindexeddb/indexeddb/chromium/record.py +536 -348
  4. dfindexeddb/indexeddb/chromium/v8.py +112 -141
  5. dfindexeddb/indexeddb/cli.py +125 -114
  6. dfindexeddb/indexeddb/firefox/definitions.py +7 -4
  7. dfindexeddb/indexeddb/firefox/gecko.py +103 -79
  8. dfindexeddb/indexeddb/firefox/record.py +66 -24
  9. dfindexeddb/indexeddb/safari/definitions.py +12 -10
  10. dfindexeddb/indexeddb/safari/record.py +68 -51
  11. dfindexeddb/indexeddb/safari/webkit.py +112 -189
  12. dfindexeddb/indexeddb/types.py +5 -2
  13. dfindexeddb/leveldb/cli.py +146 -131
  14. dfindexeddb/leveldb/definitions.py +6 -2
  15. dfindexeddb/leveldb/descriptor.py +75 -45
  16. dfindexeddb/leveldb/ldb.py +39 -30
  17. dfindexeddb/leveldb/log.py +44 -27
  18. dfindexeddb/leveldb/plugins/chrome_notifications.py +30 -18
  19. dfindexeddb/leveldb/plugins/interface.py +5 -6
  20. dfindexeddb/leveldb/plugins/manager.py +11 -10
  21. dfindexeddb/leveldb/record.py +71 -62
  22. dfindexeddb/leveldb/utils.py +21 -13
  23. dfindexeddb/utils.py +35 -30
  24. dfindexeddb/version.py +2 -2
  25. dfindexeddb-20251109.dist-info/METADATA +222 -0
  26. dfindexeddb-20251109.dist-info/RECORD +40 -0
  27. {dfindexeddb-20241031.dist-info → dfindexeddb-20251109.dist-info}/WHEEL +1 -1
  28. dfindexeddb-20241031.dist-info/AUTHORS +0 -12
  29. dfindexeddb-20241031.dist-info/METADATA +0 -424
  30. dfindexeddb-20241031.dist-info/RECORD +0 -41
  31. {dfindexeddb-20241031.dist-info → dfindexeddb-20251109.dist-info}/entry_points.txt +0 -0
  32. {dfindexeddb-20241031.dist-info → dfindexeddb-20251109.dist-info/licenses}/LICENSE +0 -0
  33. {dfindexeddb-20241031.dist-info → dfindexeddb-20251109.dist-info}/top_level.txt +0 -0
@@ -14,29 +14,28 @@
14
14
  # limitations under the License.
15
15
  """A CLI tool for leveldb files."""
16
16
  import argparse
17
+ import csv
17
18
  import dataclasses
18
- from datetime import datetime
19
19
  import json
20
20
  import pathlib
21
+ import sys
22
+ from datetime import datetime
23
+ from typing import Union
21
24
 
22
- from dfindexeddb import utils
23
- from dfindexeddb import version
24
- from dfindexeddb.leveldb import descriptor
25
- from dfindexeddb.leveldb import ldb
26
- from dfindexeddb.leveldb import log
27
- from dfindexeddb.leveldb import record
25
+ from dfindexeddb import utils, version
26
+ from dfindexeddb.leveldb import descriptor, ldb, log, record
28
27
  from dfindexeddb.leveldb.plugins import manager
29
28
 
30
-
31
29
  _VALID_PRINTABLE_CHARACTERS = (
32
- ' abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789' +
33
- '!"#$%&\'()*+,-./:;<=>?@[\\]^_`{|}~.')
30
+ " abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
31
+ + "!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~."
32
+ )
34
33
 
35
34
 
36
35
  class Encoder(json.JSONEncoder):
37
36
  """A JSON encoder class for dfleveldb fields."""
38
37
 
39
- def default(self, o):
38
+ def default(self, o): # type: ignore[no-untyped-def]
40
39
  """Returns a serializable object for o."""
41
40
  if dataclasses.is_dataclass(o):
42
41
  o_dict = utils.asdict(o)
@@ -45,10 +44,10 @@ class Encoder(json.JSONEncoder):
45
44
  out = []
46
45
  for x in o:
47
46
  if chr(x) not in _VALID_PRINTABLE_CHARACTERS:
48
- out.append(f'\\x{x:02X}')
47
+ out.append(f"\\x{x:02X}")
49
48
  else:
50
49
  out.append(chr(x))
51
- return ''.join(out)
50
+ return "".join(out)
52
51
  if isinstance(o, datetime):
53
52
  return o.isoformat()
54
53
  if isinstance(o, set):
@@ -56,19 +55,34 @@ class Encoder(json.JSONEncoder):
56
55
  return json.JSONEncoder.default(self, o)
57
56
 
58
57
 
59
- def _Output(structure, output):
58
+ def _Output(
59
+ structure: Union[
60
+ record.LevelDBRecord,
61
+ ldb.Block,
62
+ ldb.KeyValueRecord,
63
+ log.Block,
64
+ log.PhysicalRecord,
65
+ log.WriteBatch,
66
+ log.ParsedInternalKey,
67
+ descriptor.VersionEdit,
68
+ descriptor.LevelDBVersion,
69
+ ],
70
+ output: str,
71
+ ) -> None:
60
72
  """Helper method to output parsed structure to stdout."""
61
- if output == 'json':
73
+ if output == "csv":
74
+ csv.writer(sys.stdout).writerow(utils.asdict(structure).values())
75
+ elif output == "json":
62
76
  print(json.dumps(structure, indent=2, cls=Encoder))
63
- elif output == 'jsonl':
77
+ elif output == "jsonl":
64
78
  print(json.dumps(structure, cls=Encoder))
65
- elif output == 'repr':
79
+ elif output == "repr":
66
80
  print(structure)
67
81
 
68
82
 
69
- def DbCommand(args):
83
+ def DbCommand(args: argparse.Namespace) -> None:
70
84
  """The CLI for processing leveldb folders."""
71
- if args.plugin and args.plugin == 'list':
85
+ if args.plugin and args.plugin == "list":
72
86
  for plugin, _ in manager.LeveldbPluginManager.GetPlugins():
73
87
  print(plugin)
74
88
  return
@@ -78,10 +92,10 @@ def DbCommand(args):
78
92
  else:
79
93
  plugin_class = None
80
94
 
81
- for leveldb_record in record.FolderReader(
82
- args.source).GetRecords(
83
- use_manifest=args.use_manifest,
84
- use_sequence_number=args.use_sequence_number):
95
+ for leveldb_record in record.FolderReader(args.source).GetRecords(
96
+ use_manifest=args.use_manifest,
97
+ use_sequence_number=args.use_sequence_number,
98
+ ):
85
99
  if plugin_class:
86
100
  plugin_record = plugin_class.FromLevelDBRecord(leveldb_record)
87
101
  _Output(plugin_record, output=args.output)
@@ -89,9 +103,9 @@ def DbCommand(args):
89
103
  _Output(leveldb_record, output=args.output)
90
104
 
91
105
 
92
- def LdbCommand(args):
106
+ def LdbCommand(args: argparse.Namespace) -> None:
93
107
  """The CLI for processing ldb files."""
94
- if args.plugin and args.plugin == 'list':
108
+ if args.plugin and args.plugin == "list":
95
109
  for plugin, _ in manager.LeveldbPluginManager.GetPlugins():
96
110
  print(plugin)
97
111
  return
@@ -103,12 +117,12 @@ def LdbCommand(args):
103
117
 
104
118
  ldb_file = ldb.FileReader(args.source)
105
119
 
106
- if args.structure_type == 'blocks':
120
+ if args.structure_type == "blocks":
107
121
  # Prints block information.
108
122
  for block in ldb_file.GetBlocks():
109
123
  _Output(block, output=args.output)
110
124
 
111
- elif args.structure_type == 'records' or not args.structure_type:
125
+ elif args.structure_type == "records" or not args.structure_type:
112
126
  # Prints key value record information.
113
127
  for key_value_record in ldb_file.GetKeyValueRecords():
114
128
  if plugin_class:
@@ -118,12 +132,12 @@ def LdbCommand(args):
118
132
  _Output(key_value_record, output=args.output)
119
133
 
120
134
  else:
121
- print(f'{args.structure_type} is not supported for ldb files.')
135
+ print(f"{args.structure_type} is not supported for ldb files.")
122
136
 
123
137
 
124
- def LogCommand(args):
138
+ def LogCommand(args: argparse.Namespace) -> None:
125
139
  """The CLI for processing log files."""
126
- if args.plugin and args.plugin == 'list':
140
+ if args.plugin and args.plugin == "list":
127
141
  for plugin, _ in manager.LeveldbPluginManager.GetPlugins():
128
142
  print(plugin)
129
143
  return
@@ -135,23 +149,25 @@ def LogCommand(args):
135
149
 
136
150
  log_file = log.FileReader(args.source)
137
151
 
138
- if args.structure_type == 'blocks':
152
+ if args.structure_type == "blocks":
139
153
  # Prints block information.
140
154
  for block in log_file.GetBlocks():
141
155
  _Output(block, output=args.output)
142
156
 
143
- elif args.structure_type == 'physical_records':
157
+ elif args.structure_type == "physical_records":
144
158
  # Prints log file physical record information.
145
159
  for log_file_record in log_file.GetPhysicalRecords():
146
160
  _Output(log_file_record, output=args.output)
147
161
 
148
- elif args.structure_type == 'write_batches':
162
+ elif args.structure_type == "write_batches":
149
163
  # Prints log file batch information.
150
164
  for batch in log_file.GetWriteBatches():
151
165
  _Output(batch, output=args.output)
152
166
 
153
- elif (args.structure_type in ('parsed_internal_key', 'records')
154
- or not args.structure_type):
167
+ elif (
168
+ args.structure_type in ("parsed_internal_key", "records")
169
+ or not args.structure_type
170
+ ):
155
171
  # Prints key value record information.
156
172
  for internal_key_record in log_file.GetParsedInternalKeys():
157
173
  if plugin_class:
@@ -161,10 +177,10 @@ def LogCommand(args):
161
177
  _Output(internal_key_record, output=args.output)
162
178
 
163
179
  else:
164
- print(f'{args.structure_type} is not supported for log files.')
180
+ print(f"{args.structure_type} is not supported for log files.")
165
181
 
166
182
 
167
- def DescriptorCommand(args):
183
+ def DescriptorCommand(args: argparse.Namespace) -> None:
168
184
  """The CLI for processing descriptor (MANIFEST) files."""
169
185
  manifest_file = descriptor.FileReader(args.source)
170
186
 
@@ -172,157 +188,156 @@ def DescriptorCommand(args):
172
188
  for levels in manifest_file.GetVersions():
173
189
  _Output(levels, output=args.output)
174
190
 
175
- elif args.structure_type == 'blocks':
191
+ elif args.structure_type == "blocks":
176
192
  # Prints block information.
177
193
  for block in manifest_file.GetBlocks():
178
194
  _Output(block, output=args.output)
179
195
 
180
- elif args.structure_type == 'physical_records':
196
+ elif args.structure_type == "physical_records":
181
197
  # Prints log file physical record information.
182
198
  for log_file_record in manifest_file.GetPhysicalRecords():
183
199
  _Output(log_file_record, output=args.output)
184
200
 
185
- elif (args.structure_type == 'versionedit'
186
- or not args.structure_type):
201
+ elif args.structure_type == "versionedit" or not args.structure_type:
187
202
  for version_edit in manifest_file.GetVersionEdits():
188
203
  _Output(version_edit, output=args.output)
189
204
 
190
205
  else:
191
- print(f'{args.structure_type} is not supported for descriptor files.')
206
+ print(f"{args.structure_type} is not supported for descriptor files.")
192
207
 
193
208
 
194
- def App():
209
+ def App() -> None:
195
210
  """The CLI app entrypoint for parsing leveldb files."""
196
211
  parser = argparse.ArgumentParser(
197
- prog='dfleveldb',
198
- description='A cli tool for parsing leveldb files',
199
- epilog=f'Version {version.GetVersion()}')
212
+ prog="dfleveldb",
213
+ description="A cli tool for parsing leveldb files",
214
+ epilog=f"Version {version.GetVersion()}",
215
+ )
200
216
 
201
217
  subparsers = parser.add_subparsers()
202
218
 
203
- parser_db = subparsers.add_parser(
204
- 'db', help='Parse a directory as leveldb.')
219
+ parser_db = subparsers.add_parser("db", help="Parse a directory as leveldb.")
205
220
  parser_db.add_argument(
206
- '-s', '--source',
221
+ "-s",
222
+ "--source",
207
223
  required=True,
208
224
  type=pathlib.Path,
209
- help='The source leveldb directory')
225
+ help="The source leveldb directory.",
226
+ )
210
227
  recover_group = parser_db.add_mutually_exclusive_group()
211
228
  recover_group.add_argument(
212
- '--use_manifest',
213
- action='store_true',
214
- help='Use manifest file to determine active/deleted records.')
229
+ "--use_manifest",
230
+ action="store_true",
231
+ help="Use manifest file to determine active/deleted records.",
232
+ )
215
233
  recover_group.add_argument(
216
- '--use_sequence_number',
217
- action='store_true',
234
+ "--use_sequence_number",
235
+ action="store_true",
218
236
  help=(
219
- 'Use sequence number and file offset to determine active/deleted '
220
- 'records.'))
237
+ "Use sequence number and file offset to determine active/deleted "
238
+ "records."
239
+ ),
240
+ )
221
241
  parser_db.add_argument(
222
- '-o',
223
- '--output',
224
- choices=[
225
- 'json',
226
- 'jsonl',
227
- 'repr'],
228
- default='json',
229
- help='Output format. Default is json')
230
- parser_db.add_argument(
231
- '--plugin',
232
- help='Use plugin to parse records.')
242
+ "-o",
243
+ "--output",
244
+ choices=["csv", "json", "jsonl", "repr"],
245
+ default="json",
246
+ help="Output format. Default is json.",
247
+ )
248
+ parser_db.add_argument("--plugin", help="Use plugin to parse records.")
233
249
  parser_db.set_defaults(func=DbCommand)
234
250
 
235
- parser_log = subparsers.add_parser(
236
- 'log', help='Parse a leveldb log file.')
251
+ parser_log = subparsers.add_parser("log", help="Parse a leveldb log file.")
237
252
  parser_log.add_argument(
238
- '-s', '--source',
253
+ "-s",
254
+ "--source",
239
255
  required=True,
240
256
  type=pathlib.Path,
241
- help='The source leveldb file')
242
- parser_log.add_argument(
243
- '-o',
244
- '--output',
245
- choices=[
246
- 'json',
247
- 'jsonl',
248
- 'repr'],
249
- default='json',
250
- help='Output format. Default is json')
257
+ help="The source leveldb file.",
258
+ )
251
259
  parser_log.add_argument(
252
- '--plugin',
253
- help='Use plugin to parse records.')
260
+ "-o",
261
+ "--output",
262
+ choices=["csv", "json", "jsonl", "repr"],
263
+ default="json",
264
+ help="Output format. Default is json.",
265
+ )
266
+ parser_log.add_argument("--plugin", help="Use plugin to parse records.")
254
267
  parser_log.add_argument(
255
- '-t',
256
- '--structure_type',
268
+ "-t",
269
+ "--structure_type",
257
270
  choices=[
258
- 'blocks',
259
- 'physical_records',
260
- 'write_batches',
261
- 'parsed_internal_key'],
262
- help='Parses the specified structure. Default is parsed_internal_key.')
271
+ "blocks",
272
+ "physical_records",
273
+ "write_batches",
274
+ "parsed_internal_key",
275
+ ],
276
+ help="Parses the specified structure. Default is parsed_internal_key.",
277
+ )
263
278
  parser_log.set_defaults(func=LogCommand)
264
279
 
265
280
  parser_ldb = subparsers.add_parser(
266
- 'ldb', help='Parse a leveldb table (.ldb) file.')
281
+ "ldb", help="Parse a leveldb table (.ldb) file."
282
+ )
267
283
  parser_ldb.add_argument(
268
- '-s', '--source',
284
+ "-s",
285
+ "--source",
269
286
  required=True,
270
287
  type=pathlib.Path,
271
- help='The source leveldb file')
272
- parser_ldb.add_argument(
273
- '-o',
274
- '--output',
275
- choices=[
276
- 'json',
277
- 'jsonl',
278
- 'repr'],
279
- default='json',
280
- help='Output format. Default is json')
288
+ help="The source leveldb file",
289
+ )
281
290
  parser_ldb.add_argument(
282
- '--plugin',
283
- help='Use plugin to parse records.')
291
+ "-o",
292
+ "--output",
293
+ choices=["csv", "json", "jsonl", "repr"],
294
+ default="json",
295
+ help="Output format. Default is json.",
296
+ )
297
+ parser_ldb.add_argument("--plugin", help="Use plugin to parse records.")
284
298
  parser_ldb.add_argument(
285
- '-t',
286
- '--structure_type',
287
- choices=[
288
- 'blocks',
289
- 'records'],
290
- help='Parses the specified structure. Default is records.')
299
+ "-t",
300
+ "--structure_type",
301
+ choices=["blocks", "records"],
302
+ help="Parses the specified structure. Default is records.",
303
+ )
291
304
  parser_ldb.set_defaults(func=LdbCommand)
292
305
 
293
306
  parser_descriptor = subparsers.add_parser(
294
- 'descriptor', help='Parse a leveldb descriptor (MANIFEST) file.')
307
+ "descriptor", help="Parse a leveldb descriptor (MANIFEST) file."
308
+ )
295
309
  parser_descriptor.add_argument(
296
- '-s', '--source',
310
+ "-s",
311
+ "--source",
297
312
  required=True,
298
313
  type=pathlib.Path,
299
- help='The source leveldb file')
314
+ help="The source leveldb file",
315
+ )
300
316
  parser_descriptor.add_argument(
301
- '-o',
302
- '--output',
303
- choices=[
304
- 'json',
305
- 'jsonl',
306
- 'repr'],
307
- default='json',
308
- help='Output format. Default is json')
317
+ "-o",
318
+ "--output",
319
+ choices=["csv", "json", "jsonl", "repr"],
320
+ default="json",
321
+ help="Output format. Default is json.",
322
+ )
309
323
  db_group = parser_descriptor.add_mutually_exclusive_group()
310
324
  db_group.add_argument(
311
- '-t',
312
- '--structure_type',
313
- choices=[
314
- 'blocks', 'physical_records', 'versionedit'],
315
- help='Parses the specified structure. Default is versionedit.')
325
+ "-t",
326
+ "--structure_type",
327
+ choices=["blocks", "physical_records", "versionedit"],
328
+ help="Parses the specified structure. Default is versionedit.",
329
+ )
316
330
  db_group.add_argument(
317
- '-v',
318
- '--version_history',
319
- action='store_true',
320
- help='Parses the leveldb version history.')
331
+ "-v",
332
+ "--version_history",
333
+ action="store_true",
334
+ help="Parses the leveldb version history.",
335
+ )
321
336
  parser_descriptor.set_defaults(func=DescriptorCommand)
322
337
 
323
338
  args = parser.parse_args()
324
339
 
325
- if not hasattr(args, 'func'):
340
+ if not hasattr(args, "func"):
326
341
  parser.print_usage()
327
342
  else:
328
343
  args.func(args)
@@ -19,23 +19,25 @@ import enum
19
19
  BLOCK_RESTART_ENTRY_LENGTH = 4
20
20
  BLOCK_TRAILER_SIZE = 5
21
21
  TABLE_FOOTER_SIZE = 48
22
- TABLE_MAGIC = b'\x57\xfb\x80\x8b\x24\x75\x47\xdb'
22
+ TABLE_MAGIC = b"\x57\xfb\x80\x8b\x24\x75\x47\xdb"
23
23
 
24
24
  PACKED_SEQUENCE_AND_TYPE_LENGTH = 8
25
25
  SEQUENCE_LENGTH = 7
26
26
  TYPE_LENGTH = 1
27
27
 
28
- MANIFEST_FILENAME_PATTERN = r'MANIFEST-[0-9]{6}'
28
+ MANIFEST_FILENAME_PATTERN = r"MANIFEST-[0-9]{6}"
29
29
 
30
30
 
31
31
  class BlockCompressionType(enum.IntEnum):
32
32
  """Block compression types."""
33
+
33
34
  SNAPPY = 1
34
35
  ZSTD = 2
35
36
 
36
37
 
37
38
  class VersionEditTags(enum.IntEnum):
38
39
  """VersionEdit tags."""
40
+
39
41
  COMPARATOR = 1
40
42
  LOG_NUMBER = 2
41
43
  NEXT_FILE_NUMBER = 3
@@ -49,6 +51,7 @@ class VersionEditTags(enum.IntEnum):
49
51
 
50
52
  class LogFilePhysicalRecordType(enum.IntEnum):
51
53
  """Log file physical record types."""
54
+
52
55
  FULL = 1
53
56
  FIRST = 2
54
57
  MIDDLE = 3
@@ -57,5 +60,6 @@ class LogFilePhysicalRecordType(enum.IntEnum):
57
60
 
58
61
  class InternalRecordType(enum.IntEnum):
59
62
  """Internal record types."""
63
+
60
64
  DELETED = 0
61
65
  VALUE = 1