Imported Upstream version 9.20
[platform/upstream/7zip.git] / CPP / 7zip / UI / Common / ArchiveExtractCallback.cpp
1 // ArchiveExtractCallback.cpp\r
2 \r
3 #include "StdAfx.h"\r
4 \r
5 #include "Common/ComTry.h"\r
6 #include "Common/Wildcard.h"\r
7 \r
8 #include "Windows/FileDir.h"\r
9 #include "Windows/FileFind.h"\r
10 #include "Windows/PropVariant.h"\r
11 #include "Windows/PropVariantConversions.h"\r
12 \r
13 #include "../../Common/FilePathAutoRename.h"\r
14 \r
15 #include "../Common/ExtractingFilePath.h"\r
16 \r
17 #include "ArchiveExtractCallback.h"\r
18 \r
19 using namespace NWindows;\r
20 \r
21 static const wchar_t *kCantAutoRename = L"ERROR: Can not create file with auto name";\r
22 static const wchar_t *kCantRenameFile = L"ERROR: Can not rename existing file ";\r
23 static const wchar_t *kCantDeleteOutputFile = L"ERROR: Can not delete output file ";\r
24 \r
25 void CArchiveExtractCallback::Init(\r
26     const NWildcard::CCensorNode *wildcardCensor,\r
27     const CArc *arc,\r
28     IFolderArchiveExtractCallback *extractCallback2,\r
29     bool stdOutMode, bool testMode, bool crcMode,\r
30     const UString &directoryPath,\r
31     const UStringVector &removePathParts,\r
32     UInt64 packSize)\r
33 {\r
34   _wildcardCensor = wildcardCensor;\r
35 \r
36   _stdOutMode = stdOutMode;\r
37   _testMode = testMode;\r
38   _crcMode = crcMode;\r
39   _unpTotal = 1;\r
40   _packTotal = packSize;\r
41 \r
42   _extractCallback2 = extractCallback2;\r
43   _compressProgress.Release();\r
44   _extractCallback2.QueryInterface(IID_ICompressProgressInfo, &_compressProgress);\r
45 \r
46   LocalProgressSpec->Init(extractCallback2, true);\r
47   LocalProgressSpec->SendProgress = false;\r
48 \r
49  \r
50   _removePathParts = removePathParts;\r
51   _arc = arc;\r
52   _directoryPath = directoryPath;\r
53   NFile::NName::NormalizeDirPathPrefix(_directoryPath);\r
54 }\r
55 \r
56 STDMETHODIMP CArchiveExtractCallback::SetTotal(UInt64 size)\r
57 {\r
58   COM_TRY_BEGIN\r
59   _unpTotal = size;\r
60   if (!_multiArchives && _extractCallback2)\r
61     return _extractCallback2->SetTotal(size);\r
62   return S_OK;\r
63   COM_TRY_END\r
64 }\r
65 \r
66 static void NormalizeVals(UInt64 &v1, UInt64 &v2)\r
67 {\r
68   const UInt64 kMax = (UInt64)1 << 31;\r
69   while (v1 > kMax)\r
70   {\r
71     v1 >>= 1;\r
72     v2 >>= 1;\r
73   }\r
74 }\r
75 \r
76 static UInt64 MyMultDiv64(UInt64 unpCur, UInt64 unpTotal, UInt64 packTotal)\r
77 {\r
78   NormalizeVals(packTotal, unpTotal);\r
79   NormalizeVals(unpCur, unpTotal);\r
80   if (unpTotal == 0)\r
81     unpTotal = 1;\r
82   return unpCur * packTotal / unpTotal;\r
83 }\r
84 \r
85 STDMETHODIMP CArchiveExtractCallback::SetCompleted(const UInt64 *completeValue)\r
86 {\r
87   COM_TRY_BEGIN\r
88   if (!_extractCallback2)\r
89     return S_OK;\r
90 \r
91   if (_multiArchives)\r
92   {\r
93     if (completeValue != NULL)\r
94     {\r
95       UInt64 packCur = LocalProgressSpec->InSize + MyMultDiv64(*completeValue, _unpTotal, _packTotal);\r
96       return _extractCallback2->SetCompleted(&packCur);\r
97     }\r
98   }\r
99   return _extractCallback2->SetCompleted(completeValue);\r
100   COM_TRY_END\r
101 }\r
102 \r
103 STDMETHODIMP CArchiveExtractCallback::SetRatioInfo(const UInt64 *inSize, const UInt64 *outSize)\r
104 {\r
105   COM_TRY_BEGIN\r
106   return _localProgress->SetRatioInfo(inSize, outSize);\r
107   COM_TRY_END\r
108 }\r
109 \r
110 void CArchiveExtractCallback::CreateComplexDirectory(const UStringVector &dirPathParts, UString &fullPath)\r
111 {\r
112   fullPath = _directoryPath;\r
113   for (int i = 0; i < dirPathParts.Size(); i++)\r
114   {\r
115     if (i > 0)\r
116       fullPath += wchar_t(NFile::NName::kDirDelimiter);\r
117     fullPath += dirPathParts[i];\r
118     NFile::NDirectory::MyCreateDirectory(fullPath);\r
119   }\r
120 }\r
121 \r
122 HRESULT CArchiveExtractCallback::GetTime(int index, PROPID propID, FILETIME &filetime, bool &filetimeIsDefined)\r
123 {\r
124   filetimeIsDefined = false;\r
125   NCOM::CPropVariant prop;\r
126   RINOK(_arc->Archive->GetProperty(index, propID, &prop));\r
127   if (prop.vt == VT_FILETIME)\r
128   {\r
129     filetime = prop.filetime;\r
130     filetimeIsDefined = (filetime.dwHighDateTime != 0 || filetime.dwLowDateTime != 0);\r
131   }\r
132   else if (prop.vt != VT_EMPTY)\r
133     return E_FAIL;\r
134   return S_OK;\r
135 }\r
136 \r
137 HRESULT CArchiveExtractCallback::GetUnpackSize()\r
138 {\r
139   NCOM::CPropVariant prop;\r
140   RINOK(_arc->Archive->GetProperty(_index, kpidSize, &prop));\r
141   _curSizeDefined = (prop.vt != VT_EMPTY);\r
142   if (_curSizeDefined)\r
143     _curSize = ConvertPropVariantToUInt64(prop);\r
144   return S_OK;\r
145 }\r
146 \r
147 STDMETHODIMP CArchiveExtractCallback::GetStream(UInt32 index, ISequentialOutStream **outStream, Int32 askExtractMode)\r
148 {\r
149   COM_TRY_BEGIN\r
150   _crcStream.Release();\r
151   *outStream = 0;\r
152   _outFileStream.Release();\r
153 \r
154   _encrypted = false;\r
155   _isSplit = false;\r
156   _curSize = 0;\r
157   _curSizeDefined = false;\r
158   _index = index;\r
159 \r
160   UString fullPath;\r
161 \r
162   IInArchive *archive = _arc->Archive;\r
163   RINOK(_arc->GetItemPath(index, fullPath));\r
164   RINOK(IsArchiveItemFolder(archive, index, _fi.IsDir));\r
165 \r
166   _filePath = fullPath;\r
167 \r
168   {\r
169     NCOM::CPropVariant prop;\r
170     RINOK(archive->GetProperty(index, kpidPosition, &prop));\r
171     if (prop.vt != VT_EMPTY)\r
172     {\r
173       if (prop.vt != VT_UI8)\r
174         return E_FAIL;\r
175       _position = prop.uhVal.QuadPart;\r
176       _isSplit = true;\r
177     }\r
178   }\r
179     \r
180   RINOK(GetArchiveItemBoolProp(archive, index, kpidEncrypted, _encrypted));\r
181 \r
182   RINOK(GetUnpackSize());\r
183 \r
184   if (_wildcardCensor)\r
185   {\r
186     if (!_wildcardCensor->CheckPath(fullPath, !_fi.IsDir))\r
187       return S_OK;\r
188   }\r
189 \r
190   if (askExtractMode == NArchive::NExtract::NAskMode::kExtract && !_testMode)\r
191   {\r
192     if (_stdOutMode)\r
193     {\r
194       CMyComPtr<ISequentialOutStream> outStreamLoc = new CStdOutFileStream;\r
195       *outStream = outStreamLoc.Detach();\r
196       return S_OK;\r
197     }\r
198 \r
199     {\r
200       NCOM::CPropVariant prop;\r
201       RINOK(archive->GetProperty(index, kpidAttrib, &prop));\r
202       if (prop.vt == VT_UI4)\r
203       {\r
204         _fi.Attrib = prop.ulVal;\r
205         _fi.AttribDefined = true;\r
206       }\r
207       else if (prop.vt == VT_EMPTY)\r
208         _fi.AttribDefined = false;\r
209       else\r
210         return E_FAIL;\r
211     }\r
212 \r
213     RINOK(GetTime(index, kpidCTime, _fi.CTime, _fi.CTimeDefined));\r
214     RINOK(GetTime(index, kpidATime, _fi.ATime, _fi.ATimeDefined));\r
215     RINOK(GetTime(index, kpidMTime, _fi.MTime, _fi.MTimeDefined));\r
216 \r
217     bool isAnti = false;\r
218     RINOK(_arc->IsItemAnti(index, isAnti));\r
219 \r
220     UStringVector pathParts;\r
221     SplitPathToParts(fullPath, pathParts);\r
222     \r
223     if (pathParts.IsEmpty())\r
224       return E_FAIL;\r
225     int numRemovePathParts = 0;\r
226     switch(_pathMode)\r
227     {\r
228       case NExtract::NPathMode::kFullPathnames:\r
229         break;\r
230       case NExtract::NPathMode::kCurrentPathnames:\r
231       {\r
232         numRemovePathParts = _removePathParts.Size();\r
233         if (pathParts.Size() <= numRemovePathParts)\r
234           return E_FAIL;\r
235         for (int i = 0; i < numRemovePathParts; i++)\r
236           if (_removePathParts[i].CompareNoCase(pathParts[i]) != 0)\r
237             return E_FAIL;\r
238         break;\r
239       }\r
240       case NExtract::NPathMode::kNoPathnames:\r
241       {\r
242         numRemovePathParts = pathParts.Size() - 1;\r
243         break;\r
244       }\r
245     }\r
246     pathParts.Delete(0, numRemovePathParts);\r
247     MakeCorrectPath(pathParts);\r
248     UString processedPath = MakePathNameFromParts(pathParts);\r
249     if (!isAnti)\r
250     {\r
251       if (!_fi.IsDir)\r
252       {\r
253         if (!pathParts.IsEmpty())\r
254           pathParts.DeleteBack();\r
255       }\r
256     \r
257       if (!pathParts.IsEmpty())\r
258       {\r
259         UString fullPathNew;\r
260         CreateComplexDirectory(pathParts, fullPathNew);\r
261         if (_fi.IsDir)\r
262           NFile::NDirectory::SetDirTime(fullPathNew,\r
263             (WriteCTime && _fi.CTimeDefined) ? &_fi.CTime : NULL,\r
264             (WriteATime && _fi.ATimeDefined) ? &_fi.ATime : NULL,\r
265             (WriteMTime && _fi.MTimeDefined) ? &_fi.MTime : (_arc->MTimeDefined ? &_arc->MTime : NULL));\r
266       }\r
267     }\r
268 \r
269 \r
270     UString fullProcessedPath = _directoryPath + processedPath;\r
271 \r
272     if (_fi.IsDir)\r
273     {\r
274       _diskFilePath = fullProcessedPath;\r
275       if (isAnti)\r
276         NFile::NDirectory::MyRemoveDirectory(_diskFilePath);\r
277       return S_OK;\r
278     }\r
279 \r
280     if (!_isSplit)\r
281     {\r
282     NFile::NFind::CFileInfoW fileInfo;\r
283     if (fileInfo.Find(fullProcessedPath))\r
284     {\r
285       switch(_overwriteMode)\r
286       {\r
287         case NExtract::NOverwriteMode::kSkipExisting:\r
288           return S_OK;\r
289         case NExtract::NOverwriteMode::kAskBefore:\r
290         {\r
291           Int32 overwiteResult;\r
292           RINOK(_extractCallback2->AskOverwrite(\r
293               fullProcessedPath, &fileInfo.MTime, &fileInfo.Size, fullPath,\r
294               _fi.MTimeDefined ? &_fi.MTime : NULL,\r
295               _curSizeDefined ? &_curSize : NULL,\r
296               &overwiteResult))\r
297 \r
298           switch(overwiteResult)\r
299           {\r
300             case NOverwriteAnswer::kCancel:\r
301               return E_ABORT;\r
302             case NOverwriteAnswer::kNo:\r
303               return S_OK;\r
304             case NOverwriteAnswer::kNoToAll:\r
305               _overwriteMode = NExtract::NOverwriteMode::kSkipExisting;\r
306               return S_OK;\r
307             case NOverwriteAnswer::kYesToAll:\r
308               _overwriteMode = NExtract::NOverwriteMode::kWithoutPrompt;\r
309               break;\r
310             case NOverwriteAnswer::kYes:\r
311               break;\r
312             case NOverwriteAnswer::kAutoRename:\r
313               _overwriteMode = NExtract::NOverwriteMode::kAutoRename;\r
314               break;\r
315             default:\r
316               return E_FAIL;\r
317           }\r
318         }\r
319       }\r
320       if (_overwriteMode == NExtract::NOverwriteMode::kAutoRename)\r
321       {\r
322         if (!AutoRenamePath(fullProcessedPath))\r
323         {\r
324           UString message = UString(kCantAutoRename) + fullProcessedPath;\r
325           RINOK(_extractCallback2->MessageError(message));\r
326           return E_FAIL;\r
327         }\r
328       }\r
329       else if (_overwriteMode == NExtract::NOverwriteMode::kAutoRenameExisting)\r
330       {\r
331         UString existPath = fullProcessedPath;\r
332         if (!AutoRenamePath(existPath))\r
333         {\r
334           UString message = kCantAutoRename + fullProcessedPath;\r
335           RINOK(_extractCallback2->MessageError(message));\r
336           return E_FAIL;\r
337         }\r
338         if (!NFile::NDirectory::MyMoveFile(fullProcessedPath, existPath))\r
339         {\r
340           UString message = UString(kCantRenameFile) + fullProcessedPath;\r
341           RINOK(_extractCallback2->MessageError(message));\r
342           return E_FAIL;\r
343         }\r
344       }\r
345       else\r
346         if (!NFile::NDirectory::DeleteFileAlways(fullProcessedPath))\r
347         {\r
348           UString message = UString(kCantDeleteOutputFile) +  fullProcessedPath;\r
349           RINOK(_extractCallback2->MessageError(message));\r
350           return S_OK;\r
351           // return E_FAIL;\r
352         }\r
353     }\r
354     }\r
355     if (!isAnti)\r
356     {\r
357       _outFileStreamSpec = new COutFileStream;\r
358       CMyComPtr<ISequentialOutStream> outStreamLoc(_outFileStreamSpec);\r
359       if (!_outFileStreamSpec->Open(fullProcessedPath, _isSplit ? OPEN_ALWAYS: CREATE_ALWAYS))\r
360       {\r
361         // if (::GetLastError() != ERROR_FILE_EXISTS || !isSplit)\r
362         {\r
363           UString message = L"can not open output file " + fullProcessedPath;\r
364           RINOK(_extractCallback2->MessageError(message));\r
365           return S_OK;\r
366         }\r
367       }\r
368       if (_isSplit)\r
369       {\r
370         RINOK(_outFileStreamSpec->Seek(_position, STREAM_SEEK_SET, NULL));\r
371       }\r
372       _outFileStream = outStreamLoc;\r
373       *outStream = outStreamLoc.Detach();\r
374     }\r
375     _diskFilePath = fullProcessedPath;\r
376   }\r
377   else\r
378   {\r
379     *outStream = NULL;\r
380   }\r
381   if (_crcMode)\r
382   {\r
383     _crcStreamSpec = new COutStreamWithCRC;\r
384     _crcStream = _crcStreamSpec;\r
385     CMyComPtr<ISequentialOutStream> crcStream = _crcStreamSpec;\r
386     _crcStreamSpec->SetStream(*outStream);\r
387     if (*outStream)\r
388       (*outStream)->Release();\r
389     *outStream = crcStream.Detach();\r
390     _crcStreamSpec->Init(true);\r
391   }\r
392   return S_OK;\r
393   COM_TRY_END\r
394 }\r
395 \r
396 STDMETHODIMP CArchiveExtractCallback::PrepareOperation(Int32 askExtractMode)\r
397 {\r
398   COM_TRY_BEGIN\r
399   _extractMode = false;\r
400   switch (askExtractMode)\r
401   {\r
402     case NArchive::NExtract::NAskMode::kExtract:\r
403       if (_testMode)\r
404         askExtractMode = NArchive::NExtract::NAskMode::kTest;\r
405       else\r
406         _extractMode = true;\r
407       break;\r
408   };\r
409   return _extractCallback2->PrepareOperation(_filePath, _fi.IsDir,\r
410       askExtractMode, _isSplit ? &_position: 0);\r
411   COM_TRY_END\r
412 }\r
413 \r
414 STDMETHODIMP CArchiveExtractCallback::SetOperationResult(Int32 operationResult)\r
415 {\r
416   COM_TRY_BEGIN\r
417   switch(operationResult)\r
418   {\r
419     case NArchive::NExtract::NOperationResult::kOK:\r
420     case NArchive::NExtract::NOperationResult::kUnSupportedMethod:\r
421     case NArchive::NExtract::NOperationResult::kCRCError:\r
422     case NArchive::NExtract::NOperationResult::kDataError:\r
423       break;\r
424     default:\r
425       _outFileStream.Release();\r
426       return E_FAIL;\r
427   }\r
428   if (_crcStream)\r
429   {\r
430     CrcSum += _crcStreamSpec->GetCRC();\r
431     _curSize = _crcStreamSpec->GetSize();\r
432     _curSizeDefined = true;\r
433     _crcStream.Release();\r
434   }\r
435   if (_outFileStream)\r
436   {\r
437     _outFileStreamSpec->SetTime(\r
438         (WriteCTime && _fi.CTimeDefined) ? &_fi.CTime : NULL,\r
439         (WriteATime && _fi.ATimeDefined) ? &_fi.ATime : NULL,\r
440         (WriteMTime && _fi.MTimeDefined) ? &_fi.MTime : (_arc->MTimeDefined ? &_arc->MTime : NULL));\r
441     _curSize = _outFileStreamSpec->ProcessedSize;\r
442     _curSizeDefined = true;\r
443     RINOK(_outFileStreamSpec->Close());\r
444     _outFileStream.Release();\r
445   }\r
446   if (!_curSizeDefined)\r
447     GetUnpackSize();\r
448   if (_curSizeDefined)\r
449     UnpackSize += _curSize;\r
450   if (_fi.IsDir)\r
451     NumFolders++;\r
452   else\r
453     NumFiles++;\r
454 \r
455   if (_extractMode && _fi.AttribDefined)\r
456     NFile::NDirectory::MySetFileAttributes(_diskFilePath, _fi.Attrib);\r
457   RINOK(_extractCallback2->SetOperationResult(operationResult, _encrypted));\r
458   return S_OK;\r
459   COM_TRY_END\r
460 }\r
461 \r
462 /*\r
463 STDMETHODIMP CArchiveExtractCallback::GetInStream(\r
464     const wchar_t *name, ISequentialInStream **inStream)\r
465 {\r
466   COM_TRY_BEGIN\r
467   CInFileStream *inFile = new CInFileStream;\r
468   CMyComPtr<ISequentialInStream> inStreamTemp = inFile;\r
469   if (!inFile->Open(_srcDirectoryPrefix + name))\r
470     return ::GetLastError();\r
471   *inStream = inStreamTemp.Detach();\r
472   return S_OK;\r
473   COM_TRY_END\r
474 }\r
475 */\r
476 \r
477 STDMETHODIMP CArchiveExtractCallback::CryptoGetTextPassword(BSTR *password)\r
478 {\r
479   COM_TRY_BEGIN\r
480   if (!_cryptoGetTextPassword)\r
481   {\r
482     RINOK(_extractCallback2.QueryInterface(IID_ICryptoGetTextPassword,\r
483         &_cryptoGetTextPassword));\r
484   }\r
485   return _cryptoGetTextPassword->CryptoGetTextPassword(password);\r
486   COM_TRY_END\r
487 }\r
488 \r