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