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