1"""RAR handler.
2
3RAR Version 4.x
4https://codedread.github.io/bitjs/docs/unrar.html.
5
6RAR Version 5.x
7https://www.rarlab.com/technote.htm#rarsign
8"""
9
10import rarfile
11from structlog import get_logger
12
13from unblob.extractors import Command
14from unblob.report import EncryptionMetadataReport
15
16from ...models import (
17 File,
18 Handler,
19 HandlerDoc,
20 HandlerType,
21 HexString,
22 Reference,
23 ValidChunk,
24)
25
26logger = get_logger()
27
28
29class RarHandler(Handler):
30 NAME = "rar"
31
32 PATTERNS = [
33 HexString(
34 """
35 // RAR v4.x ends with 00, RAR v5.x ends with 01 00
36 52 61 72 21 1A 07 ( 00 | 01 00 )
37 """
38 )
39 ]
40 EXTRACTOR = Command("unar", "-no-directory", "-p", "", "{inpath}", "-o", "{outdir}")
41
42 DOC = HandlerDoc(
43 name="RAR",
44 description="RAR archive files are commonly used for compressed data storage. They can contain multiple files and directories, and support various compression methods.",
45 handler_type=HandlerType.ARCHIVE,
46 vendor=None,
47 references=[
48 Reference(
49 title="RAR 4.x File Format Documentation",
50 url="https://codedread.github.io/bitjs/docs/unrar.html",
51 ),
52 Reference(
53 title="RAR 5.x File Format Documentation",
54 url="https://www.rarlab.com/technote.htm#rarsign",
55 ),
56 ],
57 limitations=["Does not support encrypted RAR files."],
58 )
59
60 def calculate_chunk(self, file: File, start_offset: int) -> ValidChunk | None:
61 try:
62 rar_file = rarfile.RarFile(file)
63 except (rarfile.Error, ValueError):
64 return None
65
66 # RarFile has the side effect of moving the file pointer
67 rar_end_offset = file.tell()
68
69 return ValidChunk(
70 start_offset=start_offset,
71 end_offset=rar_end_offset,
72 metadata_reports=[
73 EncryptionMetadataReport(is_encrypted=rar_file.needs_password())
74 ],
75 )