1 // Extract.cpp
2 
3 #include "StdAfx.h"
4 
5 #include <stdio.h>
6 
7 #include "Windows/FileDir.h"
8 #include "Windows/PropVariant.h"
9 #include "Windows/PropVariantConversions.h"
10 
11 #include "../Common/ExtractingFilePath.h"
12 
13 #include "Extract.h"
14 #include "SetProperties.h"
15 
16 using namespace NWindows;
17 
DecompressArchive(const CArc & arc,UInt64 packSize,const NWildcard::CCensorNode & wildcardCensor,const CExtractOptions & options,IExtractCallbackUI * callback,CArchiveExtractCallback * extractCallbackSpec,UString & errorMessage,UInt64 & stdInProcessed)18 static HRESULT DecompressArchive(
19     const CArc &arc,
20     UInt64 packSize,
21     const NWildcard::CCensorNode &wildcardCensor,
22     const CExtractOptions &options,
23     IExtractCallbackUI *callback,
24     CArchiveExtractCallback *extractCallbackSpec,
25     UString &errorMessage,
26     UInt64 &stdInProcessed)
27 {
28   stdInProcessed = 0;
29   IInArchive *archive = arc.Archive;
30   CRecordVector<UInt32> realIndices;
31   if (!options.StdInMode)
32   {
33     UInt32 numItems;
34     RINOK(archive->GetNumberOfItems(&numItems));
35 
36     for (UInt32 i = 0; i < numItems; i++)
37     {
38       UString filePath;
39       RINOK(arc.GetItemPath(i, filePath));
40       bool isFolder;
41       RINOK(IsArchiveItemFolder(archive, i, isFolder));
42       if (!wildcardCensor.CheckPath(filePath, !isFolder))
43         continue;
44       realIndices.Add(i);
45     }
46     if (realIndices.Size() == 0)
47     {
48       callback->ThereAreNoFiles();
49       return S_OK;
50     }
51   }
52 
53   UStringVector removePathParts;
54 
55   UString outDir = options.OutputDir;
56   outDir.Replace(L"*", GetCorrectFsPath(arc.DefaultName));
57   #ifdef _WIN32
58   // GetCorrectFullFsPath doesn't like "..".
59   // outDir.TrimRight();
60   // outDir = GetCorrectFullFsPath(outDir);
61   #endif
62 
63   if (!outDir.IsEmpty())
64     if (!NFile::NDirectory::CreateComplexDirectory(outDir))
65     {
66       HRESULT res = ::GetLastError();
67       if (res == S_OK)
68         res = E_FAIL;
69       errorMessage = ((UString)L"Can not create output directory ") + outDir;
70       return res;
71     }
72 
73   extractCallbackSpec->Init(
74       options.StdInMode ? &wildcardCensor : NULL,
75       &arc,
76       callback,
77       options.StdOutMode, options.TestMode, options.CalcCrc,
78       outDir,
79       removePathParts,
80       packSize);
81 
82   #if !defined(_7ZIP_ST) && !defined(_SFX)
83   RINOK(SetProperties(archive, options.Properties));
84   #endif
85 
86   HRESULT result;
87   Int32 testMode = (options.TestMode && !options.CalcCrc) ? 1: 0;
88   if (options.StdInMode)
89   {
90     result = archive->Extract(NULL, (UInt32)(Int32)-1, testMode, extractCallbackSpec);
91     NCOM::CPropVariant prop;
92     if (archive->GetArchiveProperty(kpidPhySize, &prop) == S_OK)
93       if (prop.vt == VT_UI8 || prop.vt == VT_UI4)
94         stdInProcessed = ConvertPropVariantToUInt64(prop);
95   }
96   else
97     result = archive->Extract(&realIndices.Front(), realIndices.Size(), testMode, extractCallbackSpec);
98 
99   return callback->ExtractResult(result);
100 }
101 
DecompressArchives(CCodecs * codecs,const CIntVector & formatIndices,UStringVector & arcPaths,UStringVector & arcPathsFull,const NWildcard::CCensorNode & wildcardCensor,const CExtractOptions & options,IOpenCallbackUI * openCallback,IExtractCallbackUI * extractCallback,UString & errorMessage,CDecompressStat & stat)102 HRESULT DecompressArchives(
103     CCodecs *codecs, const CIntVector &formatIndices,
104     UStringVector &arcPaths, UStringVector &arcPathsFull,
105     const NWildcard::CCensorNode &wildcardCensor,
106     const CExtractOptions &options,
107     IOpenCallbackUI *openCallback,
108     IExtractCallbackUI *extractCallback,
109     UString &errorMessage,
110     CDecompressStat &stat)
111 {
112   stat.Clear();
113   int i;
114   UInt64 totalPackSize = 0;
115   CRecordVector<UInt64> archiveSizes;
116 
117   int numArcs = options.StdInMode ? 1 : arcPaths.Size();
118 
119   for (i = 0; i < numArcs; i++)
120   {
121     NFile::NFind::CFileInfoW fi;
122     fi.Size = 0;
123     if (!options.StdInMode)
124     {
125       const UString &arcPath = arcPaths[i];
126       if (!fi.Find(arcPath))
127         throw "there is no such archive";
128       if (fi.IsDir())
129         throw "can't decompress folder";
130     }
131     archiveSizes.Add(fi.Size);
132     totalPackSize += fi.Size;
133   }
134   CArchiveExtractCallback *extractCallbackSpec = new CArchiveExtractCallback;
135   CMyComPtr<IArchiveExtractCallback> ec(extractCallbackSpec);
136   bool multi = (numArcs > 1);
137   extractCallbackSpec->InitForMulti(multi, options.PathMode, options.OverwriteMode);
138   if (multi)
139   {
140     RINOK(extractCallback->SetTotal(totalPackSize));
141   }
142   for (i = 0; i < numArcs; i++)
143   {
144     const UString &arcPath = arcPaths[i];
145     NFile::NFind::CFileInfoW fi;
146     if (options.StdInMode)
147     {
148       fi.Size = 0;
149       fi.Attrib = 0;
150     }
151     else
152     {
153       if (!fi.Find(arcPath) || fi.IsDir())
154         throw "there is no such archive";
155     }
156 
157     #ifndef _NO_CRYPTO
158     openCallback->Open_ClearPasswordWasAskedFlag();
159     #endif
160 
161     RINOK(extractCallback->BeforeOpen(arcPath));
162     CArchiveLink archiveLink;
163 
164     CIntVector formatIndices2 = formatIndices;
165     #ifndef _SFX
166     if (formatIndices.IsEmpty())
167     {
168       int pos = arcPath.ReverseFind(L'.');
169       if (pos >= 0)
170       {
171         UString s = arcPath.Mid(pos + 1);
172         int index = codecs->FindFormatForExtension(s);
173         if (index >= 0 && s == L"001")
174         {
175           s = arcPath.Left(pos);
176           pos = s.ReverseFind(L'.');
177           if (pos >= 0)
178           {
179             int index2 = codecs->FindFormatForExtension(s.Mid(pos + 1));
180             if (index2 >= 0 && s.CompareNoCase(L"rar") != 0)
181             {
182               formatIndices2.Add(index2);
183               formatIndices2.Add(index);
184             }
185           }
186         }
187       }
188     }
189     #endif
190     HRESULT result = archiveLink.Open2(codecs, formatIndices2, options.StdInMode, NULL, arcPath, openCallback);
191     if (result == E_ABORT)
192       return result;
193 
194     bool crypted = false;
195     #ifndef _NO_CRYPTO
196     crypted = openCallback->Open_WasPasswordAsked();
197     #endif
198 
199     RINOK(extractCallback->OpenResult(arcPath, result, crypted));
200     if (result != S_OK)
201       continue;
202 
203     if (!options.StdInMode)
204     for (int v = 0; v < archiveLink.VolumePaths.Size(); v++)
205     {
206       int index = arcPathsFull.FindInSorted(archiveLink.VolumePaths[v]);
207       if (index >= 0 && index > i)
208       {
209         arcPaths.Delete(index);
210         arcPathsFull.Delete(index);
211         totalPackSize -= archiveSizes[index];
212         archiveSizes.Delete(index);
213         numArcs = arcPaths.Size();
214       }
215     }
216     if (archiveLink.VolumePaths.Size() != 0)
217     {
218       totalPackSize += archiveLink.VolumesSize;
219       RINOK(extractCallback->SetTotal(totalPackSize));
220     }
221 
222     #ifndef _NO_CRYPTO
223     UString password;
224     RINOK(openCallback->Open_GetPasswordIfAny(password));
225     if (!password.IsEmpty())
226     {
227       RINOK(extractCallback->SetPassword(password));
228     }
229     #endif
230 
231     for (int v = 0; v < archiveLink.Arcs.Size(); v++)
232     {
233       const UString &s = archiveLink.Arcs[v].ErrorMessage;
234       if (!s.IsEmpty())
235       {
236         RINOK(extractCallback->MessageError(s));
237       }
238     }
239 
240     CArc &arc = archiveLink.Arcs.Back();
241     arc.MTimeDefined = (!options.StdInMode && !fi.IsDevice);
242     arc.MTime = fi.MTime;
243 
244     UInt64 packProcessed;
245     RINOK(DecompressArchive(arc,
246         fi.Size + archiveLink.VolumesSize,
247         wildcardCensor, options, extractCallback, extractCallbackSpec, errorMessage, packProcessed));
248     if (!options.StdInMode)
249       packProcessed = fi.Size + archiveLink.VolumesSize;
250     extractCallbackSpec->LocalProgressSpec->InSize += packProcessed;
251     extractCallbackSpec->LocalProgressSpec->OutSize = extractCallbackSpec->UnpackSize;
252     if (!errorMessage.IsEmpty())
253       return E_FAIL;
254   }
255   stat.NumFolders = extractCallbackSpec->NumFolders;
256   stat.NumFiles = extractCallbackSpec->NumFiles;
257   stat.UnpackSize = extractCallbackSpec->UnpackSize;
258   stat.CrcSum = extractCallbackSpec->CrcSum;
259 
260   stat.NumArchives = arcPaths.Size();
261   stat.PackSize = extractCallbackSpec->LocalProgressSpec->InSize;
262   return S_OK;
263 }
264