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