1 #include "rar.hpp"
2
3 #include "arccmt.cpp"
4
5
Archive(RAROptions * InitCmd)6 Archive::Archive(RAROptions *InitCmd)
7 {
8 Cmd=NULL; // Just in case we'll have an exception in 'new' below.
9
10 DummyCmd=(InitCmd==NULL);
11 Cmd=DummyCmd ? (new RAROptions):InitCmd;
12
13 OpenShared=Cmd->OpenShared;
14 Format=RARFMT15;
15 Solid=false;
16 Volume=false;
17 MainComment=false;
18 Locked=false;
19 Signed=false;
20 FirstVolume=false;
21 NewNumbering=false;
22 SFXSize=0;
23 LatestTime.Reset();
24 Protected=false;
25 Encrypted=false;
26 FailedHeaderDecryption=false;
27 BrokenHeader=false;
28 LastReadBlock=0;
29
30 CurBlockPos=0;
31 NextBlockPos=0;
32
33
34 memset(&MainHead,0,sizeof(MainHead));
35 memset(&CryptHead,0,sizeof(CryptHead));
36 memset(&EndArcHead,0,sizeof(EndArcHead));
37
38 VolNumber=0;
39 VolWrite=0;
40 AddingFilesSize=0;
41 AddingHeadersSize=0;
42 *FirstVolumeName=0;
43
44 Splitting=false;
45 NewArchive=false;
46
47 SilentOpen=false;
48
49 #ifdef USE_QOPEN
50 ProhibitQOpen=false;
51 #endif
52
53 }
54
55
~Archive()56 Archive::~Archive()
57 {
58 if (DummyCmd)
59 delete Cmd;
60 }
61
62
CheckArc(bool EnableBroken)63 void Archive::CheckArc(bool EnableBroken)
64 {
65 if (!IsArchive(EnableBroken))
66 {
67 // If FailedHeaderDecryption is set, we already reported that archive
68 // password is incorrect.
69 if (!FailedHeaderDecryption)
70 uiMsg(UIERROR_BADARCHIVE,FileName);
71 ErrHandler.Exit(RARX_FATAL);
72 }
73 }
74
75
76 #if !defined(SFX_MODULE)
CheckOpen(const wchar * Name)77 void Archive::CheckOpen(const wchar *Name)
78 {
79 TOpen(Name);
80 CheckArc(false);
81 }
82 #endif
83
84
WCheckOpen(const wchar * Name)85 bool Archive::WCheckOpen(const wchar *Name)
86 {
87 if (!WOpen(Name))
88 return false;
89 if (!IsArchive(false))
90 {
91 uiMsg(UIERROR_BADARCHIVE,FileName);
92 Close();
93 return false;
94 }
95 return true;
96 }
97
98
IsSignature(const byte * D,size_t Size)99 RARFORMAT Archive::IsSignature(const byte *D,size_t Size)
100 {
101 RARFORMAT Type=RARFMT_NONE;
102 if (Size>=1 && D[0]==0x52)
103 #ifndef SFX_MODULE
104 if (Size>=4 && D[1]==0x45 && D[2]==0x7e && D[3]==0x5e)
105 Type=RARFMT14;
106 else
107 #endif
108 if (Size>=7 && D[1]==0x61 && D[2]==0x72 && D[3]==0x21 && D[4]==0x1a && D[5]==0x07)
109 {
110 // We check the last signature byte, so we can return a sensible
111 // warning in case we'll want to change the archive format
112 // sometimes in the future.
113 if (D[6]==0)
114 Type=RARFMT15;
115 else
116 if (D[6]==1)
117 Type=RARFMT50;
118 else
119 if (D[6]>1 && D[6]<5)
120 Type=RARFMT_FUTURE;
121 }
122 return Type;
123 }
124
125
IsArchive(bool EnableBroken)126 bool Archive::IsArchive(bool EnableBroken)
127 {
128 Encrypted=false;
129 BrokenHeader=false; // Might be left from previous volume.
130
131 #ifndef SFX_MODULE
132 if (IsDevice())
133 {
134 uiMsg(UIERROR_INVALIDNAME,FileName,FileName);
135 return false;
136 }
137 #endif
138 if (Read(MarkHead.Mark,SIZEOF_MARKHEAD3)!=SIZEOF_MARKHEAD3)
139 return false;
140 SFXSize=0;
141
142 RARFORMAT Type;
143 if ((Type=IsSignature(MarkHead.Mark,SIZEOF_MARKHEAD3))!=RARFMT_NONE)
144 {
145 Format=Type;
146 if (Format==RARFMT14)
147 Seek(Tell()-SIZEOF_MARKHEAD3,SEEK_SET);
148 }
149 else
150 {
151 Array<char> Buffer(MAXSFXSIZE);
152 long CurPos=(long)Tell();
153 int ReadSize=Read(&Buffer[0],Buffer.Size()-16);
154 for (int I=0;I<ReadSize;I++)
155 if (Buffer[I]==0x52 && (Type=IsSignature((byte *)&Buffer[I],ReadSize-I))!=RARFMT_NONE)
156 {
157 Format=Type;
158 if (Format==RARFMT14 && I>0 && CurPos<28 && ReadSize>31)
159 {
160 char *D=&Buffer[28-CurPos];
161 if (D[0]!=0x52 || D[1]!=0x53 || D[2]!=0x46 || D[3]!=0x58)
162 continue;
163 }
164 SFXSize=CurPos+I;
165 Seek(SFXSize,SEEK_SET);
166 if (Format==RARFMT15 || Format==RARFMT50)
167 Read(MarkHead.Mark,SIZEOF_MARKHEAD3);
168 break;
169 }
170 if (SFXSize==0)
171 return false;
172 }
173 if (Format==RARFMT_FUTURE)
174 {
175 uiMsg(UIERROR_NEWRARFORMAT,FileName);
176 return false;
177 }
178 if (Format==RARFMT50) // RAR 5.0 signature is by one byte longer.
179 {
180 if (Read(MarkHead.Mark+SIZEOF_MARKHEAD3,1)!=1 || MarkHead.Mark[SIZEOF_MARKHEAD3]!=0)
181 return false;
182 MarkHead.HeadSize=SIZEOF_MARKHEAD5;
183 }
184 else
185 MarkHead.HeadSize=SIZEOF_MARKHEAD3;
186
187 #ifdef RARDLL
188 // If callback function is not set, we cannot get the password,
189 // so we skip the initial header processing for encrypted header archive.
190 // It leads to skipped archive comment, but the rest of archive data
191 // is processed correctly.
192 if (Cmd->Callback==NULL)
193 SilentOpen=true;
194 #endif
195
196 bool HeadersLeft; // Any headers left to read.
197 bool StartFound=false; // Main or encryption headers found.
198 // Skip the archive encryption header if any and read the main header.
199 while ((HeadersLeft=(ReadHeader()!=0))==true) // Additional parentheses to silence Clang.
200 {
201 SeekToNext();
202
203 HEADER_TYPE Type=GetHeaderType();
204 // In RAR 5.0 we need to quit after reading HEAD_CRYPT if we wish to
205 // avoid the password prompt.
206 StartFound=Type==HEAD_MAIN || SilentOpen && Type==HEAD_CRYPT;
207 if (StartFound)
208 break;
209 }
210
211
212 // We should not do it for EnableBroken or we'll get 'not RAR archive'
213 // messages when extracting encrypted archives with wrong password.
214 if (FailedHeaderDecryption && !EnableBroken)
215 return false;
216
217 if (BrokenHeader || !StartFound) // Main archive header is corrupt or missing.
218 {
219 if (!FailedHeaderDecryption) // If not reported a wrong password already.
220 uiMsg(UIERROR_MHEADERBROKEN,FileName);
221 if (!EnableBroken)
222 return false;
223 }
224
225 MainComment=MainHead.CommentInHeader;
226
227 // If we process non-encrypted archive or can request a password,
228 // we set 'first volume' flag based on file attributes below.
229 // It is necessary for RAR 2.x archives, which did not have 'first volume'
230 // flag in main header. Also for all RAR formats we need to scan until
231 // first file header to set "comment" flag when reading service header.
232 // Unless we are in silent mode, we need to know about presence of comment
233 // immediately after IsArchive call.
234 if (HeadersLeft && (!SilentOpen || !Encrypted))
235 {
236 int64 SavePos=Tell();
237 int64 SaveCurBlockPos=CurBlockPos,SaveNextBlockPos=NextBlockPos;
238 HEADER_TYPE SaveCurHeaderType=CurHeaderType;
239
240 while (ReadHeader()!=0)
241 {
242 HEADER_TYPE HeaderType=GetHeaderType();
243 if (HeaderType==HEAD_SERVICE)
244 {
245 // If we have a split service headers, it surely indicates non-first
246 // volume. But not split service header does not guarantee the first
247 // volume, because we can have split file after non-split archive
248 // comment. So we do not quit from loop here.
249 FirstVolume=Volume && !SubHead.SplitBefore;
250 }
251 else
252 if (HeaderType==HEAD_FILE)
253 {
254 FirstVolume=Volume && !FileHead.SplitBefore;
255 break;
256 }
257 else
258 if (HeaderType==HEAD_ENDARC) // Might happen if archive contains only a split service header.
259 break;
260 SeekToNext();
261 }
262 CurBlockPos=SaveCurBlockPos;
263 NextBlockPos=SaveNextBlockPos;
264 CurHeaderType=SaveCurHeaderType;
265 Seek(SavePos,SEEK_SET);
266 }
267 if (!Volume || FirstVolume)
268 wcsncpyz(FirstVolumeName,FileName,ASIZE(FirstVolumeName));
269
270 return true;
271 }
272
273
274
275
SeekToNext()276 void Archive::SeekToNext()
277 {
278 Seek(NextBlockPos,SEEK_SET);
279 }
280
281
282
283
284
285
286 // Calculate the block size including encryption fields and padding if any.
FullHeaderSize(size_t Size)287 uint Archive::FullHeaderSize(size_t Size)
288 {
289 if (Encrypted)
290 {
291 Size = ALIGN_VALUE(Size, CRYPT_BLOCK_SIZE); // Align to encryption block size.
292 if (Format == RARFMT50)
293 Size += SIZE_INITV;
294 else
295 Size += SIZE_SALT30;
296 }
297 return uint(Size);
298 }
299
300
301
302
303 #ifdef USE_QOPEN
Open(const wchar * Name,uint Mode)304 bool Archive::Open(const wchar *Name,uint Mode)
305 {
306 // Important if we reuse Archive object and it has virtual QOpen
307 // file position not matching real. For example, for 'l -v volname'.
308 QOpen.Unload();
309
310 return File::Open(Name,Mode);
311 }
312
313
Read(void * Data,size_t Size)314 int Archive::Read(void *Data,size_t Size)
315 {
316 size_t Result;
317 if (QOpen.Read(Data,Size,Result))
318 return (int)Result;
319 return File::Read(Data,Size);
320 }
321
322
Seek(int64 Offset,int Method)323 void Archive::Seek(int64 Offset,int Method)
324 {
325 if (!QOpen.Seek(Offset,Method))
326 File::Seek(Offset,Method);
327 }
328
329
Tell()330 int64 Archive::Tell()
331 {
332 int64 QPos;
333 if (QOpen.Tell(&QPos))
334 return QPos;
335 return File::Tell();
336 }
337 #endif
338
339