1 // OpenArchive.cpp
2 
3 #include "StdAfx.h"
4 
5 #include "Common/Wildcard.h"
6 
7 #include "Windows/FileDir.h"
8 #include "Windows/PropVariant.h"
9 
10 #include "../../Common/FileStreams.h"
11 #include "../../Common/StreamUtils.h"
12 
13 #include "DefaultName.h"
14 #include "OpenArchive.h"
15 
16 using namespace NWindows;
17 
18 // Static-SFX (for Linux) can be big.
19 const UInt64 kMaxCheckStartPosition = 1 << 22;
20 
GetArchiveItemBoolProp(IInArchive * archive,UInt32 index,PROPID propID,bool & result)21 HRESULT GetArchiveItemBoolProp(IInArchive *archive, UInt32 index, PROPID propID, bool &result)
22 {
23   NCOM::CPropVariant prop;
24   result = false;
25   RINOK(archive->GetProperty(index, propID, &prop));
26   if (prop.vt == VT_BOOL)
27     result = VARIANT_BOOLToBool(prop.boolVal);
28   else if (prop.vt != VT_EMPTY)
29     return E_FAIL;
30   return S_OK;
31 }
32 
IsArchiveItemFolder(IInArchive * archive,UInt32 index,bool & result)33 HRESULT IsArchiveItemFolder(IInArchive *archive, UInt32 index, bool &result)
34 {
35   return GetArchiveItemBoolProp(archive, index, kpidIsDir, result);
36 }
37 
GetItemPath(UInt32 index,UString & result) const38 HRESULT CArc::GetItemPath(UInt32 index, UString &result) const
39 {
40   {
41     NCOM::CPropVariant prop;
42     RINOK(Archive->GetProperty(index, kpidPath, &prop));
43     if (prop.vt == VT_BSTR)
44       result = prop.bstrVal;
45     else if (prop.vt == VT_EMPTY)
46       result.Empty();
47     else
48       return E_FAIL;
49   }
50   if (result.IsEmpty())
51   {
52     result = DefaultName;
53     NCOM::CPropVariant prop;
54     RINOK(Archive->GetProperty(index, kpidExtension, &prop));
55     if (prop.vt == VT_BSTR)
56     {
57       result += L'.';
58       result += prop.bstrVal;
59     }
60     else if (prop.vt != VT_EMPTY)
61       return E_FAIL;
62   }
63   return S_OK;
64 }
65 
GetItemMTime(UInt32 index,FILETIME & ft,bool & defined) const66 HRESULT CArc::GetItemMTime(UInt32 index, FILETIME &ft, bool &defined) const
67 {
68   NCOM::CPropVariant prop;
69   defined = false;
70   ft.dwHighDateTime = ft.dwLowDateTime = 0;
71   RINOK(Archive->GetProperty(index, kpidMTime, &prop));
72   if (prop.vt == VT_FILETIME)
73   {
74     ft = prop.filetime;
75     defined = true;
76   }
77   else if (prop.vt != VT_EMPTY)
78     return E_FAIL;
79   else if (MTimeDefined)
80   {
81     ft = MTime;
82     defined = true;
83   }
84   return S_OK;
85 }
86 
87 #ifndef _SFX
TestSignature(const Byte * p1,const Byte * p2,size_t size)88 static inline bool TestSignature(const Byte *p1, const Byte *p2, size_t size)
89 {
90   for (size_t i = 0; i < size; i++)
91     if (p1[i] != p2[i])
92       return false;
93   return true;
94 }
95 #endif
96 
97 #ifdef UNDER_CE
98 static const int kNumHashBytes = 1;
99 #define HASH_VAL(buf, pos) ((buf)[pos])
100 #else
101 static const int kNumHashBytes = 2;
102 #define HASH_VAL(buf, pos) ((buf)[pos] | ((UInt32)(buf)[pos + 1] << 8))
103 #endif
104 
105 
OpenStream(CCodecs * codecs,int formatIndex,IInStream * stream,ISequentialInStream * seqStream,IArchiveOpenCallback * callback)106 HRESULT CArc::OpenStream(
107     CCodecs *codecs,
108     int formatIndex,
109     IInStream *stream,
110     ISequentialInStream *seqStream,
111     IArchiveOpenCallback *callback)
112 {
113   Archive.Release();
114   ErrorMessage.Empty();
115   const UString fileName = ExtractFileNameFromPath(Path);
116   UString extension;
117   {
118     int dotPos = fileName.ReverseFind(L'.');
119     if (dotPos >= 0)
120       extension = fileName.Mid(dotPos + 1);
121   }
122   CIntVector orderIndices;
123   if (formatIndex >= 0)
124     orderIndices.Add(formatIndex);
125   else
126   {
127 
128   int i;
129   int numFinded = 0;
130   for (i = 0; i < codecs->Formats.Size(); i++)
131     if (codecs->Formats[i].FindExtension(extension) >= 0)
132       orderIndices.Insert(numFinded++, i);
133     else
134       orderIndices.Add(i);
135 
136   if (!stream)
137   {
138     if (numFinded != 1)
139       return E_NOTIMPL;
140     orderIndices.DeleteFrom(1);
141   }
142 
143   #ifndef _SFX
144   if (orderIndices.Size() >= 2 && (numFinded == 0 || extension.CompareNoCase(L"exe") == 0))
145   {
146     CIntVector orderIndices2;
147     CByteBuffer byteBuffer;
148     const size_t kBufferSize = (1 << 21);
149     byteBuffer.SetCapacity(kBufferSize);
150     RINOK(stream->Seek(0, STREAM_SEEK_SET, NULL));
151     size_t processedSize = kBufferSize;
152     RINOK(ReadStream(stream, byteBuffer, &processedSize));
153     if (processedSize == 0)
154       return S_FALSE;
155 
156     const Byte *buf = byteBuffer;
157     CByteBuffer hashBuffer;
158     const UInt32 kNumVals = 1 << (kNumHashBytes * 8);
159     hashBuffer.SetCapacity(kNumVals);
160     Byte *hash = hashBuffer;
161     memset(hash, 0xFF, kNumVals);
162     Byte prevs[256];
163     if (orderIndices.Size() >= 256)
164       return S_FALSE;
165     int i;
166     for (i = 0; i < orderIndices.Size(); i++)
167     {
168       const CArcInfoEx &ai = codecs->Formats[orderIndices[i]];
169       const CByteBuffer &sig = ai.StartSignature;
170       if (sig.GetCapacity() < kNumHashBytes)
171         continue;
172       UInt32 v = HASH_VAL(sig, 0);
173       prevs[i] = hash[v];
174       hash[v] = (Byte)i;
175     }
176 
177     processedSize -= (kNumHashBytes - 1);
178     for (UInt32 pos = 0; pos < processedSize; pos++)
179     {
180       for (; pos < processedSize && hash[HASH_VAL(buf, pos)] == 0xFF; pos++);
181       if (pos == processedSize)
182         break;
183       UInt32 v = HASH_VAL(buf, pos);
184       Byte *ptr = &hash[v];
185       int i = *ptr;
186       do
187       {
188         int index = orderIndices[i];
189         const CArcInfoEx &ai = codecs->Formats[index];
190         const CByteBuffer &sig = ai.StartSignature;
191         if (sig.GetCapacity() != 0 && pos + sig.GetCapacity() <= processedSize + (kNumHashBytes - 1) &&
192             TestSignature(buf + pos, sig, sig.GetCapacity()))
193         {
194           orderIndices2.Add(index);
195           orderIndices[i] = 0xFF;
196           *ptr = prevs[i];
197         }
198         else
199           ptr = &prevs[i];
200         i = *ptr;
201       }
202       while (i != 0xFF);
203     }
204 
205     for (i = 0; i < orderIndices.Size(); i++)
206     {
207       int val = orderIndices[i];
208       if (val != 0xFF)
209         orderIndices2.Add(val);
210     }
211     orderIndices = orderIndices2;
212   }
213   else if (extension == L"000" || extension == L"001")
214   {
215     CByteBuffer byteBuffer;
216     const size_t kBufferSize = (1 << 10);
217     byteBuffer.SetCapacity(kBufferSize);
218     Byte *buffer = byteBuffer;
219     RINOK(stream->Seek(0, STREAM_SEEK_SET, NULL));
220     size_t processedSize = kBufferSize;
221     RINOK(ReadStream(stream, buffer, &processedSize));
222     if (processedSize >= 16)
223     {
224       Byte kRarHeader[] = {0x52 , 0x61, 0x72, 0x21, 0x1a, 0x07, 0x00};
225       if (TestSignature(buffer, kRarHeader, 7) && buffer[9] == 0x73 && (buffer[10] & 1) != 0)
226       {
227         for (int i = 0; i < orderIndices.Size(); i++)
228         {
229           int index = orderIndices[i];
230           const CArcInfoEx &ai = codecs->Formats[index];
231           if (ai.Name.CompareNoCase(L"rar") != 0)
232             continue;
233           orderIndices.Delete(i--);
234           orderIndices.Insert(0, index);
235           break;
236         }
237       }
238     }
239   }
240   if (orderIndices.Size() >= 2)
241   {
242     int isoIndex = codecs->FindFormatForArchiveType(L"iso");
243     int udfIndex = codecs->FindFormatForArchiveType(L"udf");
244     int iIso = -1;
245     int iUdf = -1;
246     for (int i = 0; i < orderIndices.Size(); i++)
247     {
248       if (orderIndices[i] == isoIndex) iIso = i;
249       if (orderIndices[i] == udfIndex) iUdf = i;
250     }
251     if (iUdf > iIso && iIso >= 0)
252     {
253       orderIndices[iUdf] = isoIndex;
254       orderIndices[iIso] = udfIndex;
255     }
256   }
257 
258   #endif
259   }
260 
261   for (int i = 0; i < orderIndices.Size(); i++)
262   {
263     if (stream)
264     {
265       RINOK(stream->Seek(0, STREAM_SEEK_SET, NULL));
266     }
267     CMyComPtr<IInArchive> archive;
268 
269     FormatIndex = orderIndices[i];
270     RINOK(codecs->CreateInArchive(FormatIndex, archive));
271     if (!archive)
272       continue;
273 
274     #ifdef EXTERNAL_CODECS
275     {
276       CMyComPtr<ISetCompressCodecsInfo> setCompressCodecsInfo;
277       archive.QueryInterface(IID_ISetCompressCodecsInfo, (void **)&setCompressCodecsInfo);
278       if (setCompressCodecsInfo)
279       {
280         RINOK(setCompressCodecsInfo->SetCompressCodecsInfo(codecs));
281       }
282     }
283     #endif
284 
285     // OutputDebugStringW(codecs->Formats[FormatIndex].Name);
286 
287     HRESULT result;
288     if (stream)
289       result = archive->Open(stream, &kMaxCheckStartPosition, callback);
290     else
291     {
292       CMyComPtr<IArchiveOpenSeq> openSeq;
293       archive.QueryInterface(IID_IArchiveOpenSeq, (void **)&openSeq);
294       if (!openSeq)
295         return E_NOTIMPL;
296       result = openSeq->OpenSeq(seqStream);
297     }
298 
299     if (result == S_FALSE)
300       continue;
301     RINOK(result);
302 
303     {
304       NCOM::CPropVariant prop;
305       archive->GetArchiveProperty(kpidError, &prop);
306       if (prop.vt != VT_EMPTY)
307         ErrorMessage = (prop.vt == VT_BSTR) ? prop.bstrVal : L"Unknown error";
308     }
309 
310     Archive = archive;
311     const CArcInfoEx &format = codecs->Formats[FormatIndex];
312     if (format.Exts.Size() == 0)
313       DefaultName = GetDefaultName2(fileName, L"", L"");
314     else
315     {
316       int subExtIndex = format.FindExtension(extension);
317       if (subExtIndex < 0)
318         subExtIndex = 0;
319       const CArcExtInfo &extInfo = format.Exts[subExtIndex];
320       DefaultName = GetDefaultName2(fileName, extInfo.Ext, extInfo.AddExt);
321     }
322     return S_OK;
323   }
324   return S_FALSE;
325 }
326 
OpenStreamOrFile(CCodecs * codecs,int formatIndex,bool stdInMode,IInStream * stream,IArchiveOpenCallback * callback)327 HRESULT CArc::OpenStreamOrFile(
328     CCodecs *codecs,
329     int formatIndex,
330     bool stdInMode,
331     IInStream *stream,
332     IArchiveOpenCallback *callback)
333 {
334   CMyComPtr<IInStream> fileStream;
335   CMyComPtr<ISequentialInStream> seqStream;
336   if (stdInMode)
337     seqStream = new CStdInFileStream;
338   else if (!stream)
339   {
340     CInFileStream *fileStreamSpec = new CInFileStream;
341     fileStream = fileStreamSpec;
342     if (!fileStreamSpec->Open(Path))
343       return GetLastError();
344     stream = fileStream;
345   }
346 
347   /*
348   if (callback)
349   {
350     UInt64 fileSize;
351     RINOK(stream->Seek(0, STREAM_SEEK_END, &fileSize));
352     RINOK(callback->SetTotal(NULL, &fileSize))
353   }
354   */
355 
356   return OpenStream(codecs, formatIndex, stream, seqStream, callback);
357 }
358 
Close()359 HRESULT CArchiveLink::Close()
360 {
361   for (int i = Arcs.Size() - 1;  i >= 0; i--)
362   {
363     RINOK(Arcs[i].Archive->Close());
364   }
365   IsOpen = false;
366   return S_OK;
367 }
368 
Release()369 void CArchiveLink::Release()
370 {
371   while (!Arcs.IsEmpty())
372     Arcs.DeleteBack();
373 }
374 
Open(CCodecs * codecs,const CIntVector & formatIndices,bool stdInMode,IInStream * stream,const UString & filePath,IArchiveOpenCallback * callback)375 HRESULT CArchiveLink::Open(
376     CCodecs *codecs,
377     const CIntVector &formatIndices,
378     bool stdInMode,
379     IInStream *stream,
380     const UString &filePath,
381     IArchiveOpenCallback *callback)
382 {
383   Release();
384   if (formatIndices.Size() >= 32)
385     return E_NOTIMPL;
386 
387   HRESULT resSpec;
388 
389   for (;;)
390   {
391     resSpec = S_OK;
392     int formatIndex = -1;
393     if (formatIndices.Size() >= 1)
394     {
395       if (Arcs.Size() >= formatIndices.Size())
396         break;
397       formatIndex = formatIndices[formatIndices.Size() - Arcs.Size() - 1];
398     }
399     else if (Arcs.Size() >= 32)
400       break;
401 
402     if (Arcs.IsEmpty())
403     {
404       CArc arc;
405       arc.Path = filePath;
406       arc.SubfileIndex = (UInt32)(Int32)-1;
407       RINOK(arc.OpenStreamOrFile(codecs, formatIndex, stdInMode, stream, callback));
408       Arcs.Add(arc);
409       continue;
410     }
411 
412     const CArc &arc = Arcs.Back();
413 
414     resSpec = (formatIndices.Size() == 0 ? S_OK : E_NOTIMPL);
415 
416     UInt32 mainSubfile;
417     {
418       NCOM::CPropVariant prop;
419       RINOK(arc.Archive->GetArchiveProperty(kpidMainSubfile, &prop));
420       if (prop.vt == VT_UI4)
421         mainSubfile = prop.ulVal;
422       else
423         break;
424       UInt32 numItems;
425       RINOK(arc.Archive->GetNumberOfItems(&numItems));
426       if (mainSubfile >= numItems)
427         break;
428     }
429 
430 
431     CMyComPtr<IInArchiveGetStream> getStream;
432     if (arc.Archive->QueryInterface(IID_IInArchiveGetStream, (void **)&getStream) != S_OK || !getStream)
433       break;
434 
435     CMyComPtr<ISequentialInStream> subSeqStream;
436     if (getStream->GetStream(mainSubfile, &subSeqStream) != S_OK || !subSeqStream)
437       break;
438 
439     CMyComPtr<IInStream> subStream;
440     if (subSeqStream.QueryInterface(IID_IInStream, &subStream) != S_OK || !subStream)
441       break;
442 
443     CArc arc2;
444     RINOK(arc.GetItemPath(mainSubfile, arc2.Path));
445 
446     CMyComPtr<IArchiveOpenSetSubArchiveName> setSubArchiveName;
447     callback->QueryInterface(IID_IArchiveOpenSetSubArchiveName, (void **)&setSubArchiveName);
448     if (setSubArchiveName)
449       setSubArchiveName->SetSubArchiveName(arc2.Path);
450 
451     arc2.SubfileIndex = mainSubfile;
452     HRESULT result = arc2.OpenStream(codecs, formatIndex, subStream, NULL, callback);
453     resSpec = (formatIndices.Size() == 0 ? S_OK : S_FALSE);
454     if (result == S_FALSE)
455       break;
456     RINOK(result);
457     RINOK(arc.GetItemMTime(mainSubfile, arc2.MTime, arc2.MTimeDefined));
458     Arcs.Add(arc2);
459   }
460   IsOpen = !Arcs.IsEmpty();
461   return S_OK;
462 }
463 
SetCallback(const UString & filePath,IOpenCallbackUI * callbackUI,IArchiveOpenCallback * reOpenCallback,CMyComPtr<IArchiveOpenCallback> & callback)464 static void SetCallback(const UString &filePath,
465     IOpenCallbackUI *callbackUI,
466     IArchiveOpenCallback *reOpenCallback,
467     CMyComPtr<IArchiveOpenCallback> &callback)
468 {
469   COpenCallbackImp *openCallbackSpec = new COpenCallbackImp;
470   callback = openCallbackSpec;
471   openCallbackSpec->Callback = callbackUI;
472   openCallbackSpec->ReOpenCallback = reOpenCallback;
473 
474   UString fullName;
475   int fileNamePartStartIndex;
476   NFile::NDirectory::MyGetFullPathName(filePath, fullName, fileNamePartStartIndex);
477   openCallbackSpec->Init(
478       fullName.Left(fileNamePartStartIndex),
479       fullName.Mid(fileNamePartStartIndex));
480 }
481 
Open2(CCodecs * codecs,const CIntVector & formatIndices,bool stdInMode,IInStream * stream,const UString & filePath,IOpenCallbackUI * callbackUI)482 HRESULT CArchiveLink::Open2(CCodecs *codecs,
483     const CIntVector &formatIndices,
484     bool stdInMode,
485     IInStream *stream,
486     const UString &filePath,
487     IOpenCallbackUI *callbackUI)
488 {
489   VolumesSize = 0;
490   COpenCallbackImp *openCallbackSpec = new COpenCallbackImp;
491   CMyComPtr<IArchiveOpenCallback> callback = openCallbackSpec;
492   openCallbackSpec->Callback = callbackUI;
493 
494   UString fullName, prefix, name;
495   if (!stream && !stdInMode)
496   {
497     int fileNamePartStartIndex;
498     if (!NFile::NDirectory::MyGetFullPathName(filePath, fullName, fileNamePartStartIndex))
499       return GetLastError();
500     prefix = fullName.Left(fileNamePartStartIndex);
501     name = fullName.Mid(fileNamePartStartIndex);
502     openCallbackSpec->Init(prefix, name);
503   }
504   else
505   {
506     openCallbackSpec->SetSubArchiveName(filePath);
507   }
508 
509   RINOK(Open(codecs, formatIndices, stdInMode, stream, filePath, callback));
510   VolumePaths.Add(prefix + name);
511   for (int i = 0; i < openCallbackSpec->FileNames.Size(); i++)
512     VolumePaths.Add(prefix + openCallbackSpec->FileNames[i]);
513   VolumesSize = openCallbackSpec->TotalSize;
514   return S_OK;
515 }
516 
ReOpen(CCodecs * codecs,const UString & filePath,IArchiveOpenCallback * callback)517 HRESULT CArchiveLink::ReOpen(CCodecs *codecs, const UString &filePath,
518     IArchiveOpenCallback *callback)
519 {
520   if (Arcs.Size() > 1)
521     return E_NOTIMPL;
522 
523   if (Arcs.Size() == 0)
524     return Open2(codecs, CIntVector(), false, NULL, filePath, 0);
525 
526   CMyComPtr<IArchiveOpenCallback> openCallbackNew;
527   SetCallback(filePath, NULL, callback, openCallbackNew);
528 
529   CInFileStream *fileStreamSpec = new CInFileStream;
530   CMyComPtr<IInStream> stream(fileStreamSpec);
531   if (!fileStreamSpec->Open(filePath))
532     return GetLastError();
533   HRESULT res = GetArchive()->Open(stream, &kMaxCheckStartPosition, callback);
534   IsOpen = (res == S_OK);
535   return res;
536 }
537