dfindexeddb 20240331__py3-none-any.whl → 20240402__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.
@@ -0,0 +1,1360 @@
1
+ # -*- coding: utf-8 -*-
2
+ # Copyright 2024 Google LLC
3
+ #
4
+ # Licensed under the Apache License, Version 2.0 (the "License");
5
+ # you may not use this file except in compliance with the License.
6
+ # You may obtain a copy of the License at
7
+ #
8
+ # https://www.apache.org/licenses/LICENSE-2.0
9
+ #
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS,
12
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ # See the License for the specific language governing permissions and
14
+ # limitations under the License.
15
+ """Parses Chromium IndexedDb structures."""
16
+ from __future__ import annotations
17
+ from dataclasses import dataclass, field
18
+ from datetime import datetime
19
+ import io
20
+ from typing import Any, BinaryIO, Optional, Tuple, Type, TypeVar, Union
21
+
22
+ from dfindexeddb import errors
23
+ from dfindexeddb.indexeddb.chromium import blink
24
+ from dfindexeddb.indexeddb.chromium import definitions
25
+ from dfindexeddb.leveldb import ldb
26
+ from dfindexeddb.leveldb import log
27
+ from dfindexeddb.leveldb import utils
28
+
29
+
30
+ T = TypeVar('T')
31
+
32
+
33
+ @dataclass
34
+ class KeyPrefix(utils.FromDecoderMixin):
35
+ """The IndexedDB key prefix.
36
+
37
+ Attributes:
38
+ offset: the offset of the key prefix.
39
+ database_id: the database ID.
40
+ object_store_id: the object store ID.
41
+ index_id: the index ID.
42
+ """
43
+ offset: int = field(compare=False)
44
+ database_id: int
45
+ object_store_id: int
46
+ index_id: int
47
+
48
+ @classmethod
49
+ def FromDecoder(
50
+ cls, decoder: utils.LevelDBDecoder, base_offset: int = 0) -> KeyPrefix:
51
+ """Decodes a KeyPrefix from the current position of a LevelDBDecoder.
52
+
53
+ Args:
54
+ decoder: the LevelDBDecoder.
55
+ base_offset: the base offset.
56
+
57
+ Returns:
58
+ the decoded KeyPrefix.
59
+
60
+ Raises:
61
+ ParserError: when there is an invalid database/object store/index ID
62
+ length.
63
+ """
64
+ offset, raw_prefix = decoder.ReadBytes(1)
65
+
66
+ database_id_length = (raw_prefix[0] & 0xE0 >> 5) + 1
67
+ object_store_id_length = (raw_prefix[0] & 0x1C >> 2) + 1
68
+ index_id_length = (raw_prefix[0] & 0x03) + 1
69
+
70
+ if database_id_length < 1 or database_id_length > 8:
71
+ raise errors.ParserError('Invalid database ID length')
72
+
73
+ if object_store_id_length < 1 or object_store_id_length > 8:
74
+ raise errors.ParserError('Invalid object store ID length')
75
+
76
+ if index_id_length < 1 or index_id_length > 4:
77
+ raise errors.ParserError('Invalid index ID length')
78
+
79
+ _, database_id = decoder.DecodeInt(database_id_length)
80
+ _, object_store_id = decoder.DecodeInt(object_store_id_length)
81
+ _, index_id = decoder.DecodeInt(index_id_length)
82
+
83
+ return cls(
84
+ offset=base_offset + offset,
85
+ database_id=database_id,
86
+ object_store_id=object_store_id,
87
+ index_id=index_id)
88
+
89
+ def GetKeyPrefixType(self) -> definitions.KeyPrefixType:
90
+ """Returns the KeyPrefixType.
91
+
92
+ The KeyPrefixType is based on the database/object store/index ID values.
93
+
94
+ Raises:
95
+ ParserError: if the key prefix is unknown.
96
+ """
97
+ if not self.database_id:
98
+ return definitions.KeyPrefixType.GLOBAL_METADATA
99
+ if not self.object_store_id:
100
+ return definitions.KeyPrefixType.DATABASE_METADATA
101
+ if self.index_id == 1:
102
+ return definitions.KeyPrefixType.OBJECT_STORE_DATA
103
+ if self.index_id == 2:
104
+ return definitions.KeyPrefixType.EXISTS_ENTRY
105
+ if self.index_id == 3:
106
+ return definitions.KeyPrefixType.BLOB_ENTRY
107
+ if self.index_id >= 30:
108
+ return definitions.KeyPrefixType.INDEX_DATA
109
+ raise errors.ParserError(
110
+ f'Unknown KeyPrefixType (index_id={self.index_id})')
111
+
112
+
113
+ @dataclass
114
+ class IDBKey(utils.FromDecoderMixin):
115
+ """An IDBKey.
116
+
117
+ Attributes:
118
+ offset: the offset of the IDBKey.
119
+ type: the type of the IDBKey.
120
+ value: the value of the IDBKey.
121
+ """
122
+ offset: int = field(compare=False)
123
+ type: definitions.IDBKeyType
124
+ value: Union[list, bytes, str, float, datetime, None]
125
+
126
+ _MAXIMUM_DEPTH = 2000
127
+
128
+ @classmethod
129
+ def FromDecoder(
130
+ cls,
131
+ decoder: utils.LevelDBDecoder,
132
+ base_offset: int = 0 #pylint: disable=unused-argument
133
+ ) -> IDBKey:
134
+ """Decodes an IDBKey from the current position of a LevelDBDecoder.
135
+
136
+ Args:
137
+ decoder: the LevelDBDecoder
138
+ base_offset: the base offset.
139
+
140
+ Returns:
141
+ An IDBKey
142
+
143
+ Raises:
144
+ ParserError: on invalid IDBKeyType or invalid array length during
145
+ parsing.
146
+ RecursionError: if maximum depth encountered during parsing.
147
+ """
148
+
149
+ def RecursiveParse(depth: int) -> Tuple[
150
+ int, definitions.IDBKeyType, Union[
151
+ list, bytes, str, float, datetime, None]]:
152
+ """Recursively parses IDBKeys.
153
+
154
+ Args:
155
+ depth: the current recursion depth.
156
+
157
+ Returns:
158
+ A tuple of the offset, the key type and the key value (where the value
159
+ can be bytes, str, float, datetime or a list of these types).
160
+
161
+ Raises:
162
+ ParserError: on invalid IDBKeyType or invalid array length during
163
+ parsing.
164
+ RecursionError: if maximum depth encountered during parsing.
165
+ """
166
+ if depth == cls._MAXIMUM_DEPTH:
167
+ raise RecursionError('Maximum recursion depth encountered during parse')
168
+ offset, key_type_value = decoder.DecodeInt(1)
169
+ key_type = definitions.IDBKeyType(key_type_value)
170
+
171
+ if key_type == definitions.IDBKeyType.NULL:
172
+ value = None
173
+ elif key_type == definitions.IDBKeyType.ARRAY:
174
+ _, length = decoder.DecodeVarint()
175
+ if length < 0:
176
+ raise errors.ParserError('Invalid length encountered')
177
+ value = []
178
+ while length:
179
+ entry = RecursiveParse(depth + 1)
180
+ value.append(entry[2])
181
+ length -= 1
182
+ elif key_type == definitions.IDBKeyType.BINARY:
183
+ _, value = decoder.DecodeBlobWithLength()
184
+ elif key_type == definitions.IDBKeyType.STRING:
185
+ _, value = decoder.DecodeStringWithLength()
186
+ elif key_type == definitions.IDBKeyType.DATE:
187
+ _, raw_value = decoder.DecodeDouble()
188
+ value = datetime.utcfromtimestamp(raw_value/1000.0)
189
+ elif key_type == definitions.IDBKeyType.NUMBER:
190
+ _, value = decoder.DecodeDouble()
191
+ elif key_type == definitions.IDBKeyType.MIN_KEY:
192
+ value = None
193
+ else:
194
+ raise errors.ParserError('Invalid IndexedDbKeyType')
195
+ return offset, key_type, value
196
+
197
+ offset, key_type, value = RecursiveParse(0)
198
+ return cls(base_offset + offset, key_type, value)
199
+
200
+
201
+ @dataclass
202
+ class IDBKeyPath(utils.FromDecoderMixin):
203
+ """An IDBKeyPath.
204
+
205
+ Arguments:
206
+ offset: the offset of the IDBKeyPath.
207
+ type: the IDBKeyPath type.
208
+ value: the IDBKeyPath value.
209
+ """
210
+ offset: int = field(compare=False)
211
+ type: definitions.IDBKeyPathType
212
+ value: Union[str, list[str], None]
213
+
214
+ @classmethod
215
+ def FromDecoder(cls, decoder: utils.LevelDBDecoder,
216
+ base_offset: int = 0) -> IDBKeyPath:
217
+ """Decodes an IDBKeyPath from the current position of a LevelDBDecoder.
218
+
219
+ Args:
220
+ decoder: the LevelDBDecoder.
221
+ base_offset: the base offset.
222
+
223
+ Returns:
224
+ An IDBKeyPath
225
+
226
+ Raises:
227
+ ParserError: on insufficient bytes or invalid array length during
228
+ parsing.
229
+ """
230
+ buffer = decoder.stream.getvalue() #pytype: disable=attribute-error
231
+ if len(buffer) < 3:
232
+ raise errors.ParserError('Insufficient bytes to parse.')
233
+
234
+ if buffer[0:2] != b'\x00\x00':
235
+ offset, value = decoder.DecodeString()
236
+ return IDBKeyPath(offset, definitions.IDBKeyPathType.STRING, value)
237
+
238
+ _, type_bytes = decoder.ReadBytes(3)
239
+ key_path_type_byte = type_bytes[2]
240
+ key_path_type = definitions.IDBKeyPathType(key_path_type_byte)
241
+
242
+ if key_path_type == definitions.IDBKeyPathType.NULL:
243
+ value = None
244
+ offset = decoder.stream.tell()
245
+ elif key_path_type == definitions.IDBKeyPathType.STRING:
246
+ offset, value = decoder.DecodeStringWithLength()
247
+ elif key_path_type == definitions.IDBKeyPathType.ARRAY:
248
+ value = []
249
+ offset, count = decoder.DecodeVarint()
250
+ if count < 0:
251
+ raise errors.ParserError(f'Invalid array length {count}')
252
+ for _ in range(count):
253
+ _, entry = decoder.DecodeStringWithLength()
254
+ value.append(entry)
255
+ return IDBKeyPath(base_offset + offset, key_path_type, value)
256
+
257
+
258
+ @dataclass
259
+ class BlobJournalEntry(utils.FromDecoderMixin):
260
+ """A blob journal entry.
261
+
262
+ Attributes:
263
+ offset (int): the offset.
264
+ database_id (int): the database ID.
265
+ blob_number (int): the blob number.
266
+ """
267
+ offset: int = field(compare=False)
268
+ database_id: int
269
+ blob_number: int
270
+
271
+ @classmethod
272
+ def FromDecoder(cls, decoder: utils.LevelDBDecoder,
273
+ base_offset: int = 0) -> BlobJournalEntry:
274
+ """Decodes a BlobJournalEntry from the current position of a LevelDBDecoder.
275
+
276
+ Args:
277
+ decoder: the LevelDBDecoder.
278
+ base_offset: the base offset.
279
+
280
+ Returns:
281
+ A BlobJournalEntry.
282
+ """
283
+ offset, database_id = decoder.DecodeUint64Varint()
284
+ _, blob_number = decoder.DecodeUint64Varint()
285
+ return cls(offset=base_offset + offset, database_id=database_id,
286
+ blob_number=blob_number)
287
+
288
+
289
+ @dataclass
290
+ class BlobJournal(utils.FromDecoderMixin):
291
+ """A BlobJournal.
292
+
293
+ Attributes:
294
+ offset (int): the offset.
295
+ entries (list[BlobJournalEntry]): the list of blob journal entries.
296
+ """
297
+ offset: int = field(compare=False)
298
+ entries: list[BlobJournalEntry]
299
+
300
+ @classmethod
301
+ def FromDecoder(cls, decoder: utils.LevelDBDecoder,
302
+ base_offset: int = 0) -> BlobJournal:
303
+ """Decodes a BlobJournal from the current position of a LevelDBDecoder.
304
+
305
+ Blob journals are zero-or-more instances of BlobJournalEntry. There is no
306
+ length prefix; just read until you run out of data.
307
+
308
+ Ref: indexeddb_db_leveldb_coding.cc#DecodeBlobJournal
309
+
310
+ Args:
311
+ decoder: the LevelDBDecoder
312
+ base_offset: the base offset.
313
+
314
+ Returns:
315
+ A BlobJournal
316
+ """
317
+ offset = decoder.stream.tell()
318
+ entries = []
319
+
320
+ # since there is no length prefix, consume the remaining buffer
321
+ while True:
322
+ try:
323
+ journal_entry = BlobJournalEntry.FromDecoder(decoder, base_offset)
324
+ except errors.DecoderError:
325
+ break
326
+ entries.append(journal_entry)
327
+
328
+ return cls(offset=base_offset + offset, entries=entries)
329
+
330
+
331
+ @dataclass
332
+ class BaseIndexedDBKey:
333
+ """A base class for IndexedDB coded leveldb keys and values.
334
+
335
+ This class provides an interface to parse the key (using from_bytes/
336
+ from_stream/from_decoder) and the value (using parse_value/decode_value).
337
+
338
+ Attributes:
339
+ offset: the offset of the key (after the key_prefix).
340
+ key_prefix: the key prefix.
341
+ """
342
+ offset: int
343
+ key_prefix: KeyPrefix
344
+
345
+ def DecodeValue(self, decoder: utils.LevelDBDecoder) -> Any:
346
+ """Decodes the value from the current position of the LevelDBDecoder.
347
+
348
+ To be implemented by subclasses.
349
+
350
+ Args:
351
+ decoder: the stream decoder
352
+
353
+ Raises:
354
+ NotImplementedError.
355
+ """
356
+ raise NotImplementedError(f'{self.__class__.__name__}.decode_value')
357
+
358
+ def ParseValue(self, value_data: bytes) -> Any:
359
+ """Parses the value from raw bytes.
360
+
361
+ Args:
362
+ value_data: the raw value bytes.
363
+
364
+ Returns:
365
+ The parsed value.
366
+ """
367
+ if not value_data:
368
+ return None
369
+ decoder = utils.LevelDBDecoder(io.BytesIO(value_data))
370
+ return self.DecodeValue(decoder)
371
+
372
+ @classmethod
373
+ def FromDecoder(
374
+ cls: T,
375
+ decoder: utils.LevelDBDecoder,
376
+ key_prefix: KeyPrefix,
377
+ base_offset: int = 0
378
+ ) -> T: #pylint: disable=unused-variable
379
+ """Decodes the remaining key data from the current decoder position.
380
+
381
+ Args:
382
+ decoder: the stream decoder.
383
+ key_prefix: the decoded key_prefix.
384
+ base_offset: the base offset.
385
+
386
+ Returns:
387
+ The decoded key.
388
+
389
+ Raises:
390
+ NotImplementedError.
391
+ """
392
+ raise NotImplementedError(f'{cls.__class__.__name__}.decode_key')
393
+
394
+ @classmethod
395
+ def FromStream(
396
+ cls: Type[T], stream: BinaryIO, base_offset: int = 0) -> T:
397
+ """Parses the key from the current position of the binary stream.
398
+
399
+ Args:
400
+ stream: the binary stream.
401
+ base_offset: the base offset of the stream.
402
+
403
+ Returns:
404
+ The decoded key.
405
+ """
406
+ decoder = utils.LevelDBDecoder(stream)
407
+ key_prefix = KeyPrefix.FromDecoder(decoder, base_offset=base_offset)
408
+ return cls.FromDecoder(
409
+ decoder=decoder, key_prefix=key_prefix, base_offset=base_offset)
410
+
411
+ @classmethod
412
+ def FromBytes(
413
+ cls: Type[T], raw_data: bytes, base_offset: int = 0) -> T:
414
+ """Parses the key from the raw key bytes.
415
+
416
+ Args:
417
+ raw_data: the raw key data.
418
+ base_offset: the base offset of the key data.
419
+
420
+ Returns:
421
+ The decoded key.
422
+ """
423
+ stream = io.BytesIO(raw_data)
424
+ return cls.FromStream(stream=stream, base_offset=base_offset)
425
+
426
+
427
+ @dataclass
428
+ class SchemaVersionKey(BaseIndexedDBKey):
429
+ """A schema version IndexedDb key."""
430
+
431
+ def DecodeValue(self, decoder: utils.LevelDBDecoder) -> int:
432
+ """Decodes the schema version value."""
433
+ return decoder.DecodeVarint()[1]
434
+
435
+ @classmethod
436
+ def FromDecoder(
437
+ cls, decoder: utils.LevelDBDecoder, key_prefix: KeyPrefix,
438
+ base_offset: int = 0
439
+ ) -> SchemaVersionKey:
440
+ """Decodes the schema version key."""
441
+ offset, key_type = decoder.DecodeUint8()
442
+ if key_type != definitions.GlobalMetadataKeyType.SCHEMA_VERSION:
443
+ raise errors.ParserError('Not a SchemaVersionKey')
444
+ return cls(offset=base_offset + offset, key_prefix=key_prefix)
445
+
446
+
447
+ @dataclass
448
+ class MaxDatabaseIdKey(BaseIndexedDBKey):
449
+ """A max database ID IndexedDB key."""
450
+
451
+ def DecodeValue(self, decoder: utils.LevelDBDecoder) -> int:
452
+ """Decodes the maximum database value."""
453
+ return decoder.DecodeVarint()[1]
454
+
455
+ @classmethod
456
+ def FromDecoder(
457
+ cls, decoder: utils.LevelDBDecoder, key_prefix: KeyPrefix,
458
+ base_offset: int = 0
459
+ ) -> MaxDatabaseIdKey:
460
+ """Decodes the maximum databse key."""
461
+ offset, key_type = decoder.DecodeUint8()
462
+ if key_type != definitions.GlobalMetadataKeyType.MAX_DATABASE_ID:
463
+ raise errors.ParserError('Not a MaxDatabaseIdKey')
464
+ return cls(offset=base_offset + offset, key_prefix=key_prefix)
465
+
466
+
467
+ @dataclass
468
+ class DataVersionKey(BaseIndexedDBKey):
469
+ """A data version IndexedDB key."""
470
+
471
+ def DecodeValue(self, decoder: utils.LevelDBDecoder) -> int:
472
+ """Decodes the data version value."""
473
+ return decoder.DecodeUint64Varint()[1]
474
+
475
+ @classmethod
476
+ def FromDecoder(
477
+ cls, decoder: utils.LevelDBDecoder, key_prefix: KeyPrefix,
478
+ base_offset: int = 0
479
+ ) -> DataVersionKey:
480
+ """Decodes the data version key."""
481
+ offset, key_type = decoder.DecodeUint8()
482
+ if key_type != definitions.GlobalMetadataKeyType.DATA_VERSION:
483
+ raise errors.ParserError('Not a DataVersionKey')
484
+ return cls(offset=base_offset + offset, key_prefix=key_prefix)
485
+
486
+
487
+ @dataclass
488
+ class RecoveryBlobJournalKey(BaseIndexedDBKey):
489
+ """A recovery blob journal IndexedDB key."""
490
+
491
+ def DecodeValue(self, decoder: utils.LevelDBDecoder) -> BlobJournal:
492
+ """Decodes the recovery blob journal value."""
493
+ return BlobJournal.FromDecoder(decoder)
494
+
495
+ @classmethod
496
+ def FromDecoder(
497
+ cls, decoder: utils.LevelDBDecoder, key_prefix: KeyPrefix,
498
+ base_offset: int = 0
499
+ ) -> RecoveryBlobJournalKey:
500
+ """Decodes the recovery blob journal key."""
501
+ offset, key_type = decoder.DecodeUint8()
502
+ if key_type != definitions.GlobalMetadataKeyType.RECOVERY_BLOB_JOURNAL:
503
+ raise errors.ParserError('Not a RecoveryBlobJournalKey')
504
+ return cls(offset=base_offset + offset, key_prefix=key_prefix)
505
+
506
+
507
+ @dataclass
508
+ class ActiveBlobJournalKey(BaseIndexedDBKey):
509
+ """An active blob journal IndexedDB key."""
510
+
511
+ def DecodeValue(self, decoder: utils.LevelDBDecoder) -> BlobJournal:
512
+ """Decodes the active blob journal value."""
513
+ return BlobJournal.FromDecoder(decoder)
514
+
515
+ @classmethod
516
+ def FromDecoder(
517
+ cls, decoder: utils.LevelDBDecoder, key_prefix: KeyPrefix,
518
+ base_offset: int = 0
519
+ ) -> ActiveBlobJournalKey:
520
+ """Decodes the active blob journal value."""
521
+ offset, key_type = decoder.DecodeUint8()
522
+ if key_type != definitions.GlobalMetadataKeyType.ACTIVE_BLOB_JOURNAL:
523
+ raise errors.ParserError('Not a ActiveBlobJournalKey')
524
+ return cls(offset=base_offset + offset, key_prefix=key_prefix)
525
+
526
+
527
+ @dataclass
528
+ class EarliestSweepKey(BaseIndexedDBKey):
529
+ """An earliest sweep IndexedDB key."""
530
+
531
+ def DecodeValue(self, decoder: utils.LevelDBDecoder) -> int:
532
+ """Decodes the earliest sweep value."""
533
+ _, data = decoder.ReadBytes()
534
+ return int.from_bytes(data, byteorder='little', signed=False)
535
+
536
+ @classmethod
537
+ def FromDecoder(
538
+ cls, decoder: utils.LevelDBDecoder, key_prefix: KeyPrefix,
539
+ base_offset: int = 0
540
+ ) -> EarliestSweepKey:
541
+ """Decodes the earliest sweep value."""
542
+ offset, key_type = decoder.DecodeUint8()
543
+ if key_type != definitions.GlobalMetadataKeyType.EARLIEST_SWEEP:
544
+ raise errors.ParserError('Not a EarliestSweepKey')
545
+ return cls(offset=base_offset + offset, key_prefix=key_prefix)
546
+
547
+
548
+ @dataclass
549
+ class EarlistCompactionTimeKey(BaseIndexedDBKey):
550
+ """An earliest compaction time IndexedDB key."""
551
+
552
+ def DecodeValue(self, decoder: utils.LevelDBDecoder) -> int:
553
+ """Decodes the earliest compaction time value."""
554
+ _, data = decoder.ReadBytes()
555
+ return int.from_bytes(data, byteorder='little', signed=False)
556
+
557
+ @classmethod
558
+ def FromDecoder(
559
+ cls, decoder: utils.LevelDBDecoder, key_prefix: KeyPrefix,
560
+ base_offset: int = 0
561
+ ) -> EarlistCompactionTimeKey:
562
+ """Decodes the earliest compaction time key."""
563
+ offset, key_type = decoder.DecodeUint8()
564
+ if key_type != definitions.GlobalMetadataKeyType.EARLIEST_COMPACTION_TIME:
565
+ raise errors.ParserError('Not a EarlistCompactionTimeKey')
566
+ return cls(offset=base_offset + offset, key_prefix=key_prefix)
567
+
568
+
569
+ @dataclass
570
+ class ScopesPrefixKey(BaseIndexedDBKey):
571
+ """A scopes prefix IndexedDB key."""
572
+
573
+ def DecodeValue(self, decoder: utils.LevelDBDecoder) -> Optional[bytes]:
574
+ """Decodes the scopes prefix value."""
575
+ if decoder.NumRemainingBytes:
576
+ return decoder.ReadBytes()[1]
577
+ return None
578
+
579
+ @classmethod
580
+ def FromDecoder(
581
+ cls, decoder: utils.LevelDBDecoder, key_prefix: KeyPrefix,
582
+ base_offset: int = 0
583
+ ) -> ScopesPrefixKey:
584
+ """Decodes the scopes prefix key."""
585
+ offset, key_type = decoder.DecodeUint8()
586
+ if key_type != definitions.GlobalMetadataKeyType.SCOPES_PREFIX:
587
+ raise errors.ParserError('Not a ScopesPrefixKey')
588
+ return cls(offset=base_offset + offset, key_prefix=key_prefix)
589
+
590
+
591
+ @dataclass
592
+ class DatabaseFreeListKey(BaseIndexedDBKey):
593
+ """A database free list IndexedDB key.
594
+
595
+ Attributes:
596
+ database_id: the database ID.
597
+ """
598
+ database_id: int
599
+
600
+ def DecodeValue(self, decoder: utils.LevelDBDecoder):
601
+ """Decodes the database free list value."""
602
+ raise NotImplementedError('DatabaseFreeListKey.decode_value')
603
+
604
+ @classmethod
605
+ def FromDecoder(
606
+ cls, decoder: utils.LevelDBDecoder, key_prefix: KeyPrefix,
607
+ base_offset: int = 0
608
+ ) -> DatabaseFreeListKey:
609
+ """Decodes the database free list key."""
610
+ offset, key_type = decoder.DecodeUint8()
611
+ if key_type != definitions.GlobalMetadataKeyType.DATABASE_FREE_LIST:
612
+ raise errors.ParserError('Not a DatabaseFreeListKey')
613
+ _, database_id = decoder.DecodeVarint()
614
+ return cls(
615
+ offset=base_offset + offset, key_prefix=key_prefix,
616
+ database_id=database_id)
617
+
618
+
619
+ @dataclass
620
+ class DatabaseNameKey(BaseIndexedDBKey):
621
+ """A database name key.
622
+
623
+ Attributes:
624
+ origin: the origin of the database.
625
+ database_name: the database name.
626
+ """
627
+ origin: str
628
+ database_name: str
629
+
630
+ def DecodeValue(self, decoder: utils.LevelDBDecoder) -> int:
631
+ """Decodes the database name value.
632
+
633
+ The value is the corresponding database ID.
634
+ """
635
+ return decoder.DecodeVarint()[1]
636
+
637
+ @classmethod
638
+ def FromDecoder(
639
+ cls, decoder: utils.LevelDBDecoder, key_prefix: KeyPrefix,
640
+ base_offset: int = 0
641
+ ) -> DatabaseNameKey:
642
+ """Decodes the database name key."""
643
+ offset, key_type = decoder.DecodeUint8()
644
+ if key_type != definitions.GlobalMetadataKeyType.DATABASE_NAME:
645
+ raise errors.ParserError('Not a DatabaseNameKey')
646
+ _, origin = decoder.DecodeStringWithLength()
647
+ _, database_name = decoder.DecodeStringWithLength()
648
+ return cls(
649
+ offset=base_offset + offset, key_prefix=key_prefix, origin=origin,
650
+ database_name=database_name)
651
+
652
+
653
+ @dataclass
654
+ class GlobalMetaDataKey(BaseIndexedDBKey):
655
+ """A GlobalMetaDataKey parser."""
656
+
657
+ METADATA_TYPE_TO_CLASS = {
658
+ definitions.GlobalMetadataKeyType
659
+ .SCHEMA_VERSION: SchemaVersionKey,
660
+ definitions.GlobalMetadataKeyType
661
+ .MAX_DATABASE_ID: MaxDatabaseIdKey,
662
+ definitions.GlobalMetadataKeyType
663
+ .DATA_VERSION: DataVersionKey,
664
+ definitions.GlobalMetadataKeyType
665
+ .RECOVERY_BLOB_JOURNAL: RecoveryBlobJournalKey,
666
+ definitions.GlobalMetadataKeyType
667
+ .ACTIVE_BLOB_JOURNAL: ActiveBlobJournalKey,
668
+ definitions.GlobalMetadataKeyType
669
+ .EARLIEST_SWEEP: EarliestSweepKey,
670
+ definitions.GlobalMetadataKeyType
671
+ .EARLIEST_COMPACTION_TIME: EarlistCompactionTimeKey,
672
+ definitions.GlobalMetadataKeyType
673
+ .SCOPES_PREFIX: ScopesPrefixKey,
674
+ definitions.GlobalMetadataKeyType
675
+ .DATABASE_FREE_LIST: DatabaseFreeListKey,
676
+ definitions.GlobalMetadataKeyType
677
+ .DATABASE_NAME: DatabaseNameKey}
678
+
679
+ def DecodeValue(self, decoder: utils.LevelDBDecoder) -> Any:
680
+ """Decodes the value from the current position of the LevelDBDecoder.
681
+
682
+ Args:
683
+ decoder: the stream decoder
684
+ """
685
+
686
+ @classmethod
687
+ def FromDecoder(
688
+ cls, decoder: utils.LevelDBDecoder, key_prefix: KeyPrefix,
689
+ base_offset: int = 0
690
+ ) -> Union[Type[ActiveBlobJournalKey],
691
+ Type[DataVersionKey],
692
+ Type[DatabaseFreeListKey],
693
+ Type[DatabaseNameKey],
694
+ Type[EarliestSweepKey],
695
+ Type[EarlistCompactionTimeKey],
696
+ Type[MaxDatabaseIdKey],
697
+ Type[RecoveryBlobJournalKey],
698
+ Type[SchemaVersionKey],
699
+ Type[ScopesPrefixKey]]:
700
+ """Decodes the global metadata key.
701
+
702
+ Raises:
703
+ ParserError: if the key contains an unknown metadata key type.
704
+ """
705
+ _, metadata_value = decoder.PeekBytes(1)
706
+ metadata_type = definitions.GlobalMetadataKeyType(metadata_value[0])
707
+
708
+ key_class = cls.METADATA_TYPE_TO_CLASS.get(metadata_type)
709
+ if not key_class:
710
+ raise errors.ParserError('Unknown metadata key type')
711
+ return key_class.FromDecoder(
712
+ decoder, key_prefix, base_offset) #pytype: disable=bad-return-type
713
+
714
+
715
+ @dataclass
716
+ class ObjectStoreFreeListKey(BaseIndexedDBKey):
717
+ """An IndexedDB object store free list key.
718
+
719
+ Attributes:
720
+ object_store_id: the ID of the object store containing the free list.
721
+ """
722
+ object_store_id: int
723
+
724
+ def DecodeValue(self, decoder: utils.LevelDBDecoder):
725
+ """Decodes the object store free list value."""
726
+ raise NotImplementedError('ObjectStoreFreeListKey.decode_value')
727
+
728
+ @classmethod
729
+ def FromDecoder(
730
+ cls, decoder: utils.LevelDBDecoder, key_prefix: KeyPrefix,
731
+ base_offset: int = 0
732
+ ) -> ObjectStoreFreeListKey:
733
+ """Decodes the object store free list key."""
734
+ offset, key_type = decoder.DecodeUint8()
735
+ if key_type != definitions.DatabaseMetaDataKeyType.OBJECT_STORE_FREE_LIST:
736
+ raise errors.ParserError('Not am ObjectStoreFreeListKey')
737
+ _, object_store_id = decoder.DecodeVarint()
738
+ return cls(
739
+ offset=base_offset + offset, key_prefix=key_prefix,
740
+ object_store_id=object_store_id)
741
+
742
+
743
+ @dataclass
744
+ class IndexFreeListKey(BaseIndexedDBKey):
745
+ """An IndexedDB index free list key.
746
+
747
+ Attributes:
748
+ object_store_id: the ID of the object store containing the free list.
749
+ """
750
+ object_store_id: int
751
+
752
+ def DecodeValue(self, decoder: utils.LevelDBDecoder):
753
+ """Decodes the index free list value."""
754
+ raise NotImplementedError('IndexFreeListKey.decode_value')
755
+
756
+ @classmethod
757
+ def FromDecoder(
758
+ cls, decoder: utils.LevelDBDecoder, key_prefix: KeyPrefix,
759
+ base_offset: int = 0
760
+ ) -> IndexFreeListKey:
761
+ """Decodes the index free list key."""
762
+ offset, key_type = decoder.DecodeUint8()
763
+ if key_type != definitions.DatabaseMetaDataKeyType.INDEX_FREE_LIST:
764
+ raise errors.ParserError('Not am IndexFreeListKey')
765
+ _, object_store_id = decoder.DecodeVarint()
766
+ return cls(
767
+ offset=base_offset + offset, key_prefix=key_prefix,
768
+ object_store_id=object_store_id)
769
+
770
+
771
+ @dataclass
772
+ class ObjectStoreNamesKey(BaseIndexedDBKey):
773
+ """An IndexedDB object store name key.
774
+
775
+ Attributes:
776
+ object_store_name: the name of the object store.
777
+ """
778
+ object_store_name: str
779
+
780
+ def DecodeValue(self, decoder: utils.LevelDBDecoder) -> int:
781
+ """Decodes the object store names value."""
782
+ return decoder.DecodeVarint()[1]
783
+
784
+ @classmethod
785
+ def FromDecoder(cls, decoder: utils.LevelDBDecoder, key_prefix: KeyPrefix,
786
+ base_offset: int = 0) -> ObjectStoreNamesKey:
787
+ """Decodes the object store names key."""
788
+ offset, key_type = decoder.DecodeUint8()
789
+ if key_type != definitions.DatabaseMetaDataKeyType.OBJECT_STORE_NAMES:
790
+ raise errors.ParserError('Not am ObjectStoreNamesKey')
791
+ _, object_store_name = decoder.DecodeStringWithLength()
792
+ return cls(key_prefix=key_prefix, offset=base_offset + offset,
793
+ object_store_name=object_store_name)
794
+
795
+
796
+ @dataclass
797
+ class IndexNamesKey(BaseIndexedDBKey):
798
+ """An IndexedDB index name key.
799
+
800
+ Attributes:
801
+ index_name: the name of the index.
802
+ """
803
+ index_name: str
804
+
805
+ def DecodeValue(self, decoder: utils.LevelDBDecoder) -> int:
806
+ """Decodes the index names value."""
807
+ return decoder.DecodeVarint()[1]
808
+
809
+ @classmethod
810
+ def FromDecoder(cls, decoder: utils.LevelDBDecoder, key_prefix: KeyPrefix,
811
+ base_offset: int = 0) -> IndexNamesKey:
812
+ """Decodes the index names key."""
813
+ offset, key_type = decoder.DecodeUint8()
814
+ if key_type != definitions.DatabaseMetaDataKeyType.INDEX_NAMES:
815
+ raise errors.ParserError('Not am IndexNamesKey')
816
+ _, index_name = decoder.DecodeStringWithLength()
817
+ return cls(key_prefix=key_prefix, offset=base_offset + offset,
818
+ index_name=index_name)
819
+
820
+
821
+ @dataclass
822
+ class DatabaseMetaDataKey(BaseIndexedDBKey):
823
+ """An IndexedDB database metadata key.
824
+
825
+ Attributes:
826
+ metadata_type: the type of metadata that the key value contains.
827
+ """
828
+ metadata_type: definitions.DatabaseMetaDataKeyType
829
+
830
+ def DecodeValue(
831
+ self, decoder: utils.LevelDBDecoder) -> Union[str, int]:
832
+ """Decodes the database metadata value."""
833
+ if (self.metadata_type ==
834
+ definitions.DatabaseMetaDataKeyType.ORIGIN_NAME):
835
+ return decoder.DecodeString()[1]
836
+ if (self.metadata_type ==
837
+ definitions.DatabaseMetaDataKeyType.DATABASE_NAME):
838
+ return decoder.DecodeString()[1]
839
+ if (self.metadata_type ==
840
+ definitions.DatabaseMetaDataKeyType.IDB_STRING_VERSION_DATA):
841
+ return decoder.DecodeString()[1]
842
+ if (self.metadata_type ==
843
+ definitions.DatabaseMetaDataKeyType.MAX_ALLOCATED_OBJECT_STORE_ID):
844
+ return decoder.DecodeVarint()[1]
845
+ if (self.metadata_type ==
846
+ definitions.DatabaseMetaDataKeyType.IDB_INTEGER_VERSION):
847
+ return decoder.DecodeVarint()[1]
848
+ if (self.metadata_type ==
849
+ definitions.DatabaseMetaDataKeyType
850
+ .BLOB_NUMBER_GENERATOR_CURRENT_NUMBER):
851
+ return decoder.DecodeVarint()[1]
852
+ raise errors.ParserError(
853
+ f'Unknown database metadata type {self.metadata_type}')
854
+
855
+ @classmethod
856
+ def FromDecoder(
857
+ cls,
858
+ decoder: utils.LevelDBDecoder,
859
+ key_prefix: KeyPrefix,
860
+ base_offset: int = 0
861
+ ) -> Union[
862
+ DatabaseMetaDataKey,
863
+ IndexFreeListKey,
864
+ IndexMetaDataKey,
865
+ IndexNamesKey,
866
+ ObjectStoreFreeListKey,
867
+ ObjectStoreMetaDataKey,
868
+ ObjectStoreNamesKey]:
869
+ """Decodes the database metadata key."""
870
+ offset, metadata_value = decoder.PeekBytes(1)
871
+ metadata_type = definitions.DatabaseMetaDataKeyType(metadata_value[0])
872
+
873
+ if metadata_type in (
874
+ definitions.DatabaseMetaDataKeyType.ORIGIN_NAME,
875
+ definitions.DatabaseMetaDataKeyType.DATABASE_NAME,
876
+ definitions.DatabaseMetaDataKeyType.IDB_STRING_VERSION_DATA,
877
+ definitions.DatabaseMetaDataKeyType.MAX_ALLOCATED_OBJECT_STORE_ID,
878
+ definitions.DatabaseMetaDataKeyType.IDB_INTEGER_VERSION,
879
+ definitions.DatabaseMetaDataKeyType
880
+ .BLOB_NUMBER_GENERATOR_CURRENT_NUMBER):
881
+ return cls(key_prefix=key_prefix, offset=base_offset + offset,
882
+ metadata_type=metadata_type)
883
+ if metadata_type == definitions.DatabaseMetaDataKeyType.INDEX_META_DATA:
884
+ return IndexMetaDataKey.FromDecoder(
885
+ decoder, key_prefix, base_offset)
886
+ if (metadata_type ==
887
+ definitions.DatabaseMetaDataKeyType.OBJECT_STORE_META_DATA):
888
+ return ObjectStoreMetaDataKey.FromDecoder(
889
+ decoder, key_prefix, base_offset)
890
+ if (metadata_type ==
891
+ definitions.DatabaseMetaDataKeyType.OBJECT_STORE_FREE_LIST):
892
+ return ObjectStoreFreeListKey.FromDecoder(
893
+ decoder, key_prefix, base_offset)
894
+ if metadata_type == definitions.DatabaseMetaDataKeyType.INDEX_FREE_LIST:
895
+ return IndexFreeListKey.FromDecoder(
896
+ decoder, key_prefix, base_offset)
897
+ if (metadata_type ==
898
+ definitions.DatabaseMetaDataKeyType.OBJECT_STORE_NAMES):
899
+ return ObjectStoreNamesKey.FromDecoder(
900
+ decoder, key_prefix, base_offset)
901
+ if metadata_type == definitions.DatabaseMetaDataKeyType.INDEX_NAMES:
902
+ return IndexNamesKey.FromDecoder(
903
+ decoder, key_prefix, base_offset)
904
+ raise errors.ParserError(f'unknown database metadata type {metadata_type}.')
905
+
906
+
907
+ @dataclass
908
+ class ObjectStoreMetaDataKey(BaseIndexedDBKey):
909
+ """An IndexedDB object store meta data key.
910
+
911
+ Attributes:
912
+ object_store_id: the ID of the object store.
913
+ metadata_type: the object store metadata type.
914
+ """
915
+ object_store_id: int
916
+ metadata_type: definitions.ObjectStoreMetaDataKeyType
917
+
918
+ def DecodeValue(
919
+ self, decoder: utils.LevelDBDecoder) -> Union[IDBKeyPath, str, bool, int]:
920
+ """Decodes the object store metadata value."""
921
+ if (self.metadata_type ==
922
+ definitions.ObjectStoreMetaDataKeyType.OBJECT_STORE_NAME):
923
+ return decoder.DecodeString()[1]
924
+ if self.metadata_type == definitions.ObjectStoreMetaDataKeyType.KEY_PATH:
925
+ return IDBKeyPath.FromDecoder(decoder)
926
+ if (self.metadata_type ==
927
+ definitions.ObjectStoreMetaDataKeyType.AUTO_INCREMENT_FLAG):
928
+ return decoder.DecodeBool()[1]
929
+ if (self.metadata_type ==
930
+ definitions.ObjectStoreMetaDataKeyType.IS_EVICTABLE):
931
+ return decoder.DecodeBool()[1]
932
+ if (self.metadata_type ==
933
+ definitions.ObjectStoreMetaDataKeyType.LAST_VERSION_NUMBER):
934
+ return decoder.DecodeInt(signed=False)[1]
935
+ if (self.metadata_type ==
936
+ definitions.ObjectStoreMetaDataKeyType.MAXIMUM_ALLOCATED_INDEX_ID):
937
+ return decoder.DecodeInt()[1]
938
+ if (self.metadata_type ==
939
+ definitions.ObjectStoreMetaDataKeyType.HAS_KEY_PATH):
940
+ return decoder.DecodeBool()[1]
941
+ if (self.metadata_type ==
942
+ definitions.ObjectStoreMetaDataKeyType.KEY_GENERATOR_CURRENT_NUMBER):
943
+ return decoder.DecodeInt()[1]
944
+ raise errors.ParserError(f'Unknown metadata type {self.metadata_type}')
945
+
946
+ @classmethod
947
+ def FromDecoder(
948
+ cls, decoder: utils.LevelDBDecoder, key_prefix: KeyPrefix,
949
+ base_offset: int = 0
950
+ ) -> ObjectStoreMetaDataKey:
951
+ """Decodes the object store metadata key."""
952
+ offset, metadata_value = decoder.DecodeUint8()
953
+ if (metadata_value !=
954
+ definitions.DatabaseMetaDataKeyType.OBJECT_STORE_META_DATA):
955
+ raise errors.ParserError('Not a ObjectStoreMetaDataKey')
956
+
957
+ _, object_store_id = decoder.DecodeVarint()
958
+ _, metadata_value = decoder.DecodeUint8()
959
+ metadata_type = definitions.ObjectStoreMetaDataKeyType(metadata_value)
960
+ return cls(
961
+ offset=base_offset + offset, key_prefix=key_prefix,
962
+ object_store_id=object_store_id, metadata_type=metadata_type)
963
+
964
+ @dataclass
965
+ class ObjectStoreDataValue:
966
+ """The parsed values from an ObjectStoreDataKey.
967
+
968
+ Attributes:
969
+ unknown: an unknown integer (possibly a sequence number?).
970
+ is_wrapped: True if the value was wrapped.
971
+ blob_size: the blob size, only valid if wrapped.
972
+ blob_offset: the blob offset, only valid if wrapped.
973
+ value: the blink serialized value, only valid if not wrapped.
974
+ """
975
+ unkown: int
976
+ is_wrapped: bool
977
+ blob_size: Optional[int]
978
+ blob_offset: Optional[int]
979
+ value: Any
980
+
981
+
982
+ @dataclass
983
+ class ObjectStoreDataKey(BaseIndexedDBKey):
984
+ """An IndexedDB object store data key.
985
+
986
+ Attributes:
987
+ encoded_user_key: the encoded user key.
988
+ """
989
+ encoded_user_key: IDBKey
990
+
991
+ def DecodeValue(
992
+ self, decoder: utils.LevelDBDecoder) -> ObjectStoreDataValue:
993
+ """Decodes the object store data value."""
994
+ _, unknown_integer = decoder.DecodeVarint()
995
+
996
+ _, wrapped_header_bytes = decoder.PeekBytes(3)
997
+ if len(wrapped_header_bytes) != 3:
998
+ raise errors.DecoderError('Insufficient bytes')
999
+
1000
+ if (wrapped_header_bytes[0] == definitions.BlinkSerializationTag.VERSION and
1001
+ wrapped_header_bytes[1] == 0x11 and
1002
+ wrapped_header_bytes[2] == 0x01):
1003
+ _, blob_size = decoder.DecodeVarint()
1004
+ _, blob_offset = decoder.DecodeVarint()
1005
+ return ObjectStoreDataValue(
1006
+ unkown=unknown_integer,
1007
+ is_wrapped=True,
1008
+ blob_size=blob_size,
1009
+ blob_offset=blob_offset,
1010
+ value=None)
1011
+ _, blink_bytes = decoder.ReadBytes()
1012
+ blink_value = blink.V8ScriptValueDecoder.FromBytes(blink_bytes)
1013
+ return ObjectStoreDataValue(
1014
+ unkown=unknown_integer,
1015
+ is_wrapped=False,
1016
+ blob_size=None,
1017
+ blob_offset=None,
1018
+ value=blink_value)
1019
+
1020
+ @classmethod
1021
+ def FromDecoder(
1022
+ cls, decoder: utils.LevelDBDecoder, key_prefix: KeyPrefix,
1023
+ base_offset: int = 0
1024
+ ) -> ObjectStoreDataKey:
1025
+ """Decodes the object store data key."""
1026
+ if (key_prefix.GetKeyPrefixType() !=
1027
+ definitions.KeyPrefixType.OBJECT_STORE_DATA):
1028
+ raise errors.ParserError('Invalid KeyPrefix for ObjectStoreDataKey')
1029
+ offset = decoder.stream.tell()
1030
+ encoded_user_key = IDBKey.FromDecoder(decoder, offset)
1031
+ return cls(
1032
+ offset=base_offset + offset,
1033
+ key_prefix=key_prefix, encoded_user_key=encoded_user_key)
1034
+
1035
+
1036
+ @dataclass
1037
+ class ExistsEntryKey(BaseIndexedDBKey):
1038
+ """An IndexedDB exists entry key.
1039
+
1040
+ Attributes:
1041
+ encoded_user_key: the encoded user key.
1042
+ """
1043
+ encoded_user_key: IDBKey
1044
+
1045
+ def DecodeValue(self, decoder: utils.LevelDBDecoder) -> int:
1046
+ """Decodes the exists entry value."""
1047
+ _, data = decoder.ReadBytes()
1048
+ return int.from_bytes(data, byteorder='little', signed=False)
1049
+
1050
+ @classmethod
1051
+ def FromDecoder(
1052
+ cls, decoder: utils.LevelDBDecoder, key_prefix: KeyPrefix,
1053
+ base_offset: int = 0
1054
+ ) -> ExistsEntryKey:
1055
+ """Decodes the exists entry key."""
1056
+ offset = decoder.stream.tell()
1057
+ encoded_user_key = IDBKey.FromDecoder(decoder, offset)
1058
+
1059
+ return cls(
1060
+ offset=base_offset + offset,
1061
+ key_prefix=key_prefix, encoded_user_key=encoded_user_key)
1062
+
1063
+
1064
+ @dataclass
1065
+ class IndexDataKey(BaseIndexedDBKey):
1066
+ """An IndexedDB data key.
1067
+
1068
+ Attributes:
1069
+ encoded_user_key: the encoded user key.
1070
+ sequence_number: the sequence number of the data key.
1071
+ encoded_primary_key: the encoded primary key.
1072
+ """
1073
+ encoded_user_key: IDBKey
1074
+ sequence_number: Optional[int]
1075
+ encoded_primary_key: Optional[IDBKey]
1076
+
1077
+ def DecodeValue(self, decoder: utils.LevelDBDecoder) -> Tuple[int, IDBKey]:
1078
+ """Decodes the index data key value."""
1079
+ _, version = decoder.DecodeVarint()
1080
+ idb_key = IDBKey.FromDecoder(decoder)
1081
+ return version, idb_key
1082
+
1083
+ @classmethod
1084
+ def FromDecoder(cls, decoder: utils.LevelDBDecoder, key_prefix: KeyPrefix,
1085
+ base_offset: int = 0) -> IndexDataKey:
1086
+ """Decodes the index data key."""
1087
+ offset = decoder.stream.tell()
1088
+ encoded_user_key = IDBKey.FromDecoder(decoder, offset)
1089
+
1090
+ if decoder.NumRemainingBytes() > 0:
1091
+ _, sequence_number = decoder.DecodeVarint()
1092
+ else:
1093
+ sequence_number = None
1094
+
1095
+ if decoder.NumRemainingBytes() > 0:
1096
+ encoded_primary_key_offset = decoder.stream.tell()
1097
+ encoded_primary_key = IDBKey.FromDecoder(
1098
+ decoder, encoded_primary_key_offset)
1099
+ else:
1100
+ encoded_primary_key = None
1101
+
1102
+ return cls(
1103
+ key_prefix=key_prefix,
1104
+ encoded_user_key=encoded_user_key,
1105
+ sequence_number=sequence_number,
1106
+ encoded_primary_key=encoded_primary_key,
1107
+ offset=base_offset + offset)
1108
+
1109
+
1110
+ @dataclass
1111
+ class BlobEntryKey(BaseIndexedDBKey):
1112
+ """An IndexedDB blob entry key.
1113
+
1114
+ Attributes:
1115
+ user_key: the user/primary key.
1116
+ """
1117
+ user_key: IDBKey
1118
+
1119
+ def DecodeValue(
1120
+ self, decoder: utils.LevelDBDecoder) -> IndexedDBExternalObject:
1121
+ """Decodes the blob entry value."""
1122
+ return IndexedDBExternalObject.FromDecoder(decoder)
1123
+
1124
+ @classmethod
1125
+ def FromDecoder(
1126
+ cls, decoder: utils.LevelDBDecoder, key_prefix: KeyPrefix,
1127
+ base_offset: int = 0
1128
+ ) -> BlobEntryKey:
1129
+ """Decodes the blob entry key."""
1130
+ offset = decoder.stream.tell()
1131
+ user_key = IDBKey.FromDecoder(decoder, offset)
1132
+
1133
+ return cls(key_prefix=key_prefix, user_key=user_key,
1134
+ offset=base_offset + offset)
1135
+
1136
+
1137
+ @dataclass
1138
+ class IndexedDbKey(BaseIndexedDBKey):
1139
+ """An IndexedDB key.
1140
+
1141
+ A factory class for parsing IndexedDB keys.
1142
+ """
1143
+
1144
+ METADATA_TYPE_TO_CLASS = {
1145
+ definitions.KeyPrefixType.GLOBAL_METADATA: GlobalMetaDataKey,
1146
+ definitions.KeyPrefixType.DATABASE_METADATA: DatabaseMetaDataKey,
1147
+ definitions.KeyPrefixType.OBJECT_STORE_DATA: ObjectStoreDataKey,
1148
+ definitions.KeyPrefixType.EXISTS_ENTRY: ExistsEntryKey,
1149
+ definitions.KeyPrefixType.INDEX_DATA: IndexDataKey,
1150
+ definitions.KeyPrefixType.BLOB_ENTRY: BlobEntryKey,
1151
+ definitions.KeyPrefixType.INVALID_TYPE: None
1152
+ }
1153
+
1154
+ def DecodeValue(self, decoder: utils.LevelDBDecoder) -> Any:
1155
+ """Decodes the value from the current position of the LevelDBDecoder.
1156
+
1157
+ Args:
1158
+ decoder: the stream decoder
1159
+ """
1160
+
1161
+ @classmethod
1162
+ def FromDecoder(
1163
+ cls,
1164
+ decoder: utils.LevelDBDecoder,
1165
+ key_prefix: KeyPrefix,
1166
+ base_offset: int = 0
1167
+ ) -> Union[Type[DatabaseMetaDataKey], Type[ExistsEntryKey],
1168
+ Type[BlobEntryKey], Type[GlobalMetaDataKey],
1169
+ Type[IndexDataKey], Type[ObjectStoreDataKey]]:
1170
+ """Decodes the IndexedDB key."""
1171
+ key_type = key_prefix.GetKeyPrefixType()
1172
+ key_class = cls.METADATA_TYPE_TO_CLASS.get(key_type)
1173
+ if not key_class:
1174
+ raise errors.ParserError('Unknown KeyPrefixType')
1175
+ return key_class.FromDecoder(
1176
+ decoder=decoder,
1177
+ key_prefix=key_prefix, #pytype: disable=bad-return-type
1178
+ base_offset=base_offset)
1179
+
1180
+
1181
+ @dataclass
1182
+ class IndexMetaDataKey(BaseIndexedDBKey):
1183
+ """An IndexedDB meta data key.
1184
+
1185
+ Attributes:
1186
+ object_store_id: the ID of the object store.
1187
+ index_id: the index ID.
1188
+ metadata_type: the metadata key type.
1189
+ """
1190
+ object_store_id: int
1191
+ index_id: int
1192
+ metadata_type: definitions.IndexMetaDataKeyType
1193
+
1194
+ def DecodeValue(
1195
+ self, decoder: utils.LevelDBDecoder) -> Union[bool, IDBKeyPath, str]:
1196
+ """Decodes the index metadata value."""
1197
+ if self.metadata_type == definitions.IndexMetaDataKeyType.INDEX_NAME:
1198
+ return decoder.DecodeString()[1]
1199
+ if self.metadata_type == definitions.IndexMetaDataKeyType.KEY_PATH:
1200
+ return IDBKeyPath.FromDecoder(decoder)
1201
+ if (self.metadata_type ==
1202
+ definitions.IndexMetaDataKeyType.MULTI_ENTRY_FLAG):
1203
+ return decoder.DecodeBool()[1]
1204
+ if self.metadata_type == definitions.IndexMetaDataKeyType.UNIQUE_FLAG:
1205
+ return decoder.DecodeBool()[1]
1206
+ raise errors.ParserError(
1207
+ f'Unknown index metadata type {self.metadata_type}')
1208
+
1209
+ @classmethod
1210
+ def FromDecoder(
1211
+ cls,
1212
+ decoder: utils.LevelDBDecoder,
1213
+ key_prefix: KeyPrefix, base_offset: int = 0
1214
+ ) -> IndexMetaDataKey:
1215
+ """Decodes the index metadata key."""
1216
+ offset, key_type = decoder.DecodeUint8()
1217
+ if key_type != definitions.DatabaseMetaDataKeyType.INDEX_META_DATA:
1218
+ raise errors.ParserError('Not an IndexMetaDataKey.')
1219
+ _, object_store_id = decoder.DecodeVarint()
1220
+ _, index_id = decoder.DecodeVarint()
1221
+ _, metadata_bytes = decoder.ReadBytes(1)
1222
+ metadata_type = definitions.IndexMetaDataKeyType(metadata_bytes[0])
1223
+ return cls(offset=base_offset + offset, key_prefix=key_prefix,
1224
+ object_store_id=object_store_id, index_id=index_id,
1225
+ metadata_type=metadata_type)
1226
+
1227
+
1228
+ @dataclass
1229
+ class ExternalObjectEntry(utils.FromDecoderMixin):
1230
+ """An IndexedDB external object entry.
1231
+
1232
+ Args:
1233
+ offset: the offset of the ExternalObjectEntry.
1234
+ object_type: the external object type.
1235
+ blob_number: the blob number if the object type is a blob or file,
1236
+ None otherwise.
1237
+ mime_type: the mime type if the object type is a blob or file, None
1238
+ otherwise.
1239
+ size: the size if the object type is a blob or file, None otherwise.
1240
+ filename: the filename if the object is a blob or file, None otherwise.
1241
+ last_modified: the last modified time if a blob or file, None otherwise.
1242
+ token: the token if a filesystem access handle, None otherwise.
1243
+ """
1244
+ offset: int = field(compare=False)
1245
+ object_type: definitions.ExternalObjectType
1246
+ blob_number: Optional[int]
1247
+ mime_type: Optional[str]
1248
+ size: Optional[int]
1249
+ filename: Optional[str]
1250
+ last_modified: Optional[int] # microseconds
1251
+ token: Optional[bytes]
1252
+
1253
+ @classmethod
1254
+ def FromDecoder(
1255
+ cls,
1256
+ decoder: utils.LevelDBDecoder,
1257
+ base_offset: int = 0
1258
+ ) -> ExternalObjectEntry:
1259
+ """Decodes the external object entry."""
1260
+ offset, object_type_value = decoder.DecodeUint8()
1261
+ object_type = definitions.ExternalObjectType(object_type_value)
1262
+ if (object_type in
1263
+ (definitions.ExternalObjectType.BLOB,
1264
+ definitions.ExternalObjectType.FILE)):
1265
+ _, blob_number = decoder.DecodeVarint()
1266
+ _, mime_type = decoder.DecodeStringWithLength()
1267
+ _, size = decoder.DecodeVarint()
1268
+ if object_type == definitions.ExternalObjectType.FILE:
1269
+ _, filename = decoder.DecodeStringWithLength()
1270
+ _, last_modified = decoder.DecodeVarint()
1271
+ else:
1272
+ filename = None
1273
+ last_modified = None
1274
+ token = None
1275
+ elif (object_type ==
1276
+ definitions.ExternalObjectType.FILE_SYSTEM_ACCESS_HANDLE):
1277
+ blob_number = None
1278
+ mime_type = None
1279
+ size = None
1280
+ filename = None
1281
+ last_modified = None
1282
+ _, token = decoder.DecodeBlobWithLength()
1283
+
1284
+ return cls(offset=base_offset + offset, object_type=object_type,
1285
+ blob_number=blob_number, mime_type=mime_type, size=size,
1286
+ filename=filename, last_modified=last_modified, token=token)
1287
+
1288
+
1289
+ @dataclass
1290
+ class IndexedDBExternalObject(utils.FromDecoderMixin):
1291
+ """An IndexedDB external object.
1292
+
1293
+ Args:
1294
+ offset: the offset of the IndexedDBExternalObject.
1295
+ entries: a list of external objects.
1296
+ """
1297
+ offset: int = field(compare=False)
1298
+ entries: list[ExternalObjectEntry]
1299
+
1300
+ @classmethod
1301
+ def FromDecoder(
1302
+ cls,
1303
+ decoder: utils.LevelDBDecoder,
1304
+ base_offset: int = 0
1305
+ ) -> IndexedDBExternalObject:
1306
+ """Decodes the external object."""
1307
+ entries = []
1308
+ offset = decoder.stream.tell()
1309
+ while decoder.NumRemainingBytes():
1310
+ entry = ExternalObjectEntry.FromDecoder(decoder, base_offset)
1311
+ entries.append(entry)
1312
+
1313
+ return cls(offset=base_offset + offset, entries=entries)
1314
+
1315
+
1316
+ @dataclass
1317
+ class ObjectStore:
1318
+ """An IndexedDB Object Store.
1319
+
1320
+ Attributes:
1321
+ id: the object store ID.
1322
+ name: the object store name.
1323
+ records: the records in the object store.
1324
+ """
1325
+ id: int
1326
+ name: str
1327
+ records: list = field(default_factory=list, repr=False)
1328
+
1329
+
1330
+ @dataclass
1331
+ class IndexedDBRecord:
1332
+ """An IndexedDB Record.
1333
+
1334
+ Attributes:
1335
+ offset: the offset of the record.
1336
+ key: the key of the record.
1337
+ value: the value of the record.
1338
+ sequence_number: if available, the sequence number of the record.
1339
+ type: the type of the record.
1340
+ """
1341
+ offset: int
1342
+ key: Any
1343
+ value: Any
1344
+ sequence_number: int
1345
+ type: int
1346
+
1347
+ @classmethod
1348
+ def FromLevelDBRecord(
1349
+ cls, record: Union[ldb.KeyValueRecord, log.ParsedInternalKey]
1350
+ ) -> IndexedDBRecord:
1351
+ """Returns an IndexedDBRecord from a ParsedInternalKey."""
1352
+ idb_key = IndexedDbKey.FromBytes(record.key, base_offset=record.offset)
1353
+ idb_value = idb_key.ParseValue(record.value)
1354
+ return cls(
1355
+ offset=record.offset,
1356
+ key=idb_key,
1357
+ value=idb_value,
1358
+ sequence_number=record.sequence_number if hasattr(
1359
+ record, 'sequence_number') else None,
1360
+ type=record.record_type)