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