1 // ArchiveExtractCallback.cpp
\r
5 #include "Common/ComTry.h"
\r
6 #include "Common/Wildcard.h"
\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
13 #include "../../Common/FilePathAutoRename.h"
\r
15 #include "../Common/ExtractingFilePath.h"
\r
17 #include "ArchiveExtractCallback.h"
\r
19 using namespace NWindows;
\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
25 void CArchiveExtractCallback::Init(
\r
26 const NWildcard::CCensorNode *wildcardCensor,
\r
28 IFolderArchiveExtractCallback *extractCallback2,
\r
29 bool stdOutMode, bool testMode, bool crcMode,
\r
30 const UString &directoryPath,
\r
31 const UStringVector &removePathParts,
\r
34 _wildcardCensor = wildcardCensor;
\r
36 _stdOutMode = stdOutMode;
\r
37 _testMode = testMode;
\r
40 _packTotal = packSize;
\r
42 _extractCallback2 = extractCallback2;
\r
43 _compressProgress.Release();
\r
44 _extractCallback2.QueryInterface(IID_ICompressProgressInfo, &_compressProgress);
\r
46 LocalProgressSpec->Init(extractCallback2, true);
\r
47 LocalProgressSpec->SendProgress = false;
\r
50 _removePathParts = removePathParts;
\r
52 _directoryPath = directoryPath;
\r
53 NFile::NName::NormalizeDirPathPrefix(_directoryPath);
\r
56 STDMETHODIMP CArchiveExtractCallback::SetTotal(UInt64 size)
\r
60 if (!_multiArchives && _extractCallback2)
\r
61 return _extractCallback2->SetTotal(size);
\r
66 static void NormalizeVals(UInt64 &v1, UInt64 &v2)
\r
68 const UInt64 kMax = (UInt64)1 << 31;
\r
76 static UInt64 MyMultDiv64(UInt64 unpCur, UInt64 unpTotal, UInt64 packTotal)
\r
78 NormalizeVals(packTotal, unpTotal);
\r
79 NormalizeVals(unpCur, unpTotal);
\r
82 return unpCur * packTotal / unpTotal;
\r
85 STDMETHODIMP CArchiveExtractCallback::SetCompleted(const UInt64 *completeValue)
\r
88 if (!_extractCallback2)
\r
93 if (completeValue != NULL)
\r
95 UInt64 packCur = LocalProgressSpec->InSize + MyMultDiv64(*completeValue, _unpTotal, _packTotal);
\r
96 return _extractCallback2->SetCompleted(&packCur);
\r
99 return _extractCallback2->SetCompleted(completeValue);
\r
103 STDMETHODIMP CArchiveExtractCallback::SetRatioInfo(const UInt64 *inSize, const UInt64 *outSize)
\r
106 return _localProgress->SetRatioInfo(inSize, outSize);
\r
110 void CArchiveExtractCallback::CreateComplexDirectory(const UStringVector &dirPathParts, UString &fullPath)
\r
112 fullPath = _directoryPath;
\r
113 for (int i = 0; i < dirPathParts.Size(); i++)
\r
116 fullPath += wchar_t(NFile::NName::kDirDelimiter);
\r
117 fullPath += dirPathParts[i];
\r
118 NFile::NDirectory::MyCreateDirectory(fullPath);
\r
122 HRESULT CArchiveExtractCallback::GetTime(int index, PROPID propID, FILETIME &filetime, bool &filetimeIsDefined)
\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
129 filetime = prop.filetime;
\r
130 filetimeIsDefined = (filetime.dwHighDateTime != 0 || filetime.dwLowDateTime != 0);
\r
132 else if (prop.vt != VT_EMPTY)
\r
137 HRESULT CArchiveExtractCallback::GetUnpackSize()
\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
147 STDMETHODIMP CArchiveExtractCallback::GetStream(UInt32 index, ISequentialOutStream **outStream, Int32 askExtractMode)
\r
150 _crcStream.Release();
\r
152 _outFileStream.Release();
\r
154 _encrypted = false;
\r
157 _curSizeDefined = false;
\r
162 IInArchive *archive = _arc->Archive;
\r
163 RINOK(_arc->GetItemPath(index, fullPath));
\r
164 RINOK(IsArchiveItemFolder(archive, index, _fi.IsDir));
\r
166 _filePath = fullPath;
\r
169 NCOM::CPropVariant prop;
\r
170 RINOK(archive->GetProperty(index, kpidPosition, &prop));
\r
171 if (prop.vt != VT_EMPTY)
\r
173 if (prop.vt != VT_UI8)
\r
175 _position = prop.uhVal.QuadPart;
\r
180 RINOK(GetArchiveItemBoolProp(archive, index, kpidEncrypted, _encrypted));
\r
182 RINOK(GetUnpackSize());
\r
184 if (_wildcardCensor)
\r
186 if (!_wildcardCensor->CheckPath(fullPath, !_fi.IsDir))
\r
190 if (askExtractMode == NArchive::NExtract::NAskMode::kExtract && !_testMode)
\r
194 CMyComPtr<ISequentialOutStream> outStreamLoc = new CStdOutFileStream;
\r
195 *outStream = outStreamLoc.Detach();
\r
200 NCOM::CPropVariant prop;
\r
201 RINOK(archive->GetProperty(index, kpidAttrib, &prop));
\r
202 if (prop.vt == VT_UI4)
\r
204 _fi.Attrib = prop.ulVal;
\r
205 _fi.AttribDefined = true;
\r
207 else if (prop.vt == VT_EMPTY)
\r
208 _fi.AttribDefined = false;
\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
217 bool isAnti = false;
\r
218 RINOK(_arc->IsItemAnti(index, isAnti));
\r
220 UStringVector pathParts;
\r
221 SplitPathToParts(fullPath, pathParts);
\r
223 if (pathParts.IsEmpty())
\r
225 int numRemovePathParts = 0;
\r
228 case NExtract::NPathMode::kFullPathnames:
\r
230 case NExtract::NPathMode::kCurrentPathnames:
\r
232 numRemovePathParts = _removePathParts.Size();
\r
233 if (pathParts.Size() <= numRemovePathParts)
\r
235 for (int i = 0; i < numRemovePathParts; i++)
\r
236 if (_removePathParts[i].CompareNoCase(pathParts[i]) != 0)
\r
240 case NExtract::NPathMode::kNoPathnames:
\r
242 numRemovePathParts = pathParts.Size() - 1;
\r
246 pathParts.Delete(0, numRemovePathParts);
\r
247 MakeCorrectPath(pathParts);
\r
248 UString processedPath = MakePathNameFromParts(pathParts);
\r
253 if (!pathParts.IsEmpty())
\r
254 pathParts.DeleteBack();
\r
257 if (!pathParts.IsEmpty())
\r
259 UString fullPathNew;
\r
260 CreateComplexDirectory(pathParts, fullPathNew);
\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
270 UString fullProcessedPath = _directoryPath + processedPath;
\r
274 _diskFilePath = fullProcessedPath;
\r
276 NFile::NDirectory::MyRemoveDirectory(_diskFilePath);
\r
282 NFile::NFind::CFileInfoW fileInfo;
\r
283 if (fileInfo.Find(fullProcessedPath))
\r
285 switch(_overwriteMode)
\r
287 case NExtract::NOverwriteMode::kSkipExisting:
\r
289 case NExtract::NOverwriteMode::kAskBefore:
\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
298 switch(overwiteResult)
\r
300 case NOverwriteAnswer::kCancel:
\r
302 case NOverwriteAnswer::kNo:
\r
304 case NOverwriteAnswer::kNoToAll:
\r
305 _overwriteMode = NExtract::NOverwriteMode::kSkipExisting;
\r
307 case NOverwriteAnswer::kYesToAll:
\r
308 _overwriteMode = NExtract::NOverwriteMode::kWithoutPrompt;
\r
310 case NOverwriteAnswer::kYes:
\r
312 case NOverwriteAnswer::kAutoRename:
\r
313 _overwriteMode = NExtract::NOverwriteMode::kAutoRename;
\r
320 if (_overwriteMode == NExtract::NOverwriteMode::kAutoRename)
\r
322 if (!AutoRenamePath(fullProcessedPath))
\r
324 UString message = UString(kCantAutoRename) + fullProcessedPath;
\r
325 RINOK(_extractCallback2->MessageError(message));
\r
329 else if (_overwriteMode == NExtract::NOverwriteMode::kAutoRenameExisting)
\r
331 UString existPath = fullProcessedPath;
\r
332 if (!AutoRenamePath(existPath))
\r
334 UString message = kCantAutoRename + fullProcessedPath;
\r
335 RINOK(_extractCallback2->MessageError(message));
\r
338 if (!NFile::NDirectory::MyMoveFile(fullProcessedPath, existPath))
\r
340 UString message = UString(kCantRenameFile) + fullProcessedPath;
\r
341 RINOK(_extractCallback2->MessageError(message));
\r
346 if (!NFile::NDirectory::DeleteFileAlways(fullProcessedPath))
\r
348 UString message = UString(kCantDeleteOutputFile) + fullProcessedPath;
\r
349 RINOK(_extractCallback2->MessageError(message));
\r
357 _outFileStreamSpec = new COutFileStream;
\r
358 CMyComPtr<ISequentialOutStream> outStreamLoc(_outFileStreamSpec);
\r
359 if (!_outFileStreamSpec->Open(fullProcessedPath, _isSplit ? OPEN_ALWAYS: CREATE_ALWAYS))
\r
361 // if (::GetLastError() != ERROR_FILE_EXISTS || !isSplit)
\r
363 UString message = L"can not open output file " + fullProcessedPath;
\r
364 RINOK(_extractCallback2->MessageError(message));
\r
370 RINOK(_outFileStreamSpec->Seek(_position, STREAM_SEEK_SET, NULL));
\r
372 _outFileStream = outStreamLoc;
\r
373 *outStream = outStreamLoc.Detach();
\r
375 _diskFilePath = fullProcessedPath;
\r
383 _crcStreamSpec = new COutStreamWithCRC;
\r
384 _crcStream = _crcStreamSpec;
\r
385 CMyComPtr<ISequentialOutStream> crcStream = _crcStreamSpec;
\r
386 _crcStreamSpec->SetStream(*outStream);
\r
388 (*outStream)->Release();
\r
389 *outStream = crcStream.Detach();
\r
390 _crcStreamSpec->Init(true);
\r
396 STDMETHODIMP CArchiveExtractCallback::PrepareOperation(Int32 askExtractMode)
\r
399 _extractMode = false;
\r
400 switch (askExtractMode)
\r
402 case NArchive::NExtract::NAskMode::kExtract:
\r
404 askExtractMode = NArchive::NExtract::NAskMode::kTest;
\r
406 _extractMode = true;
\r
409 return _extractCallback2->PrepareOperation(_filePath, _fi.IsDir,
\r
410 askExtractMode, _isSplit ? &_position: 0);
\r
414 STDMETHODIMP CArchiveExtractCallback::SetOperationResult(Int32 operationResult)
\r
417 switch(operationResult)
\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
425 _outFileStream.Release();
\r
430 CrcSum += _crcStreamSpec->GetCRC();
\r
431 _curSize = _crcStreamSpec->GetSize();
\r
432 _curSizeDefined = true;
\r
433 _crcStream.Release();
\r
435 if (_outFileStream)
\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
446 if (!_curSizeDefined)
\r
448 if (_curSizeDefined)
\r
449 UnpackSize += _curSize;
\r
455 if (_extractMode && _fi.AttribDefined)
\r
456 NFile::NDirectory::MySetFileAttributes(_diskFilePath, _fi.Attrib);
\r
457 RINOK(_extractCallback2->SetOperationResult(operationResult, _encrypted));
\r
463 STDMETHODIMP CArchiveExtractCallback::GetInStream(
\r
464 const wchar_t *name, ISequentialInStream **inStream)
\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
477 STDMETHODIMP CArchiveExtractCallback::CryptoGetTextPassword(BSTR *password)
\r
480 if (!_cryptoGetTextPassword)
\r
482 RINOK(_extractCallback2.QueryInterface(IID_ICryptoGetTextPassword,
\r
483 &_cryptoGetTextPassword));
\r
485 return _cryptoGetTextPassword->CryptoGetTextPassword(password);
\r