1 // ArchiveExtractCallback.cpp
5 #include "ArchiveExtractCallback.h"
7 #include "Common/Wildcard.h"
8 #include "Common/StringConvert.h"
9 #include "Common/ComTry.h"
11 #include "Windows/FileDir.h"
12 #include "Windows/FileFind.h"
13 #include "Windows/Time.h"
14 #include "Windows/Defs.h"
15 #include "Windows/PropVariant.h"
17 #include "Windows/PropVariantConversions.h"
19 #include "../../Common/FilePathAutoRename.h"
21 #include "../Common/ExtractingFilePath.h"
22 #include "OpenArchive.h"
24 using namespace NWindows;
26 static const wchar_t *kCantAutoRename = L"ERROR: Can not create file with auto name";
27 static const wchar_t *kCantRenameFile = L"ERROR: Can not rename existing file ";
28 static const wchar_t *kCantDeleteOutputFile = L"ERROR: Can not delete output file ";
31 void CArchiveExtractCallback::Init(
32 IInArchive *archiveHandler,
33 IFolderArchiveExtractCallback *extractCallback2,
35 const UString &directoryPath,
36 const UStringVector &removePathParts,
37 const UString &itemDefaultName,
38 const FILETIME &utcLastWriteTimeDefault,
39 UInt32 attributesDefault,
42 _stdOutMode = stdOutMode;
45 _packTotal = packSize;
47 _extractCallback2 = extractCallback2;
48 _compressProgress.Release();
49 _extractCallback2.QueryInterface(IID_ICompressProgressInfo, &_compressProgress);
51 LocalProgressSpec->Init(extractCallback2, true);
52 LocalProgressSpec->SendProgress = false;
54 _itemDefaultName = itemDefaultName;
55 _utcLastWriteTimeDefault = utcLastWriteTimeDefault;
56 _attributesDefault = attributesDefault;
57 _removePathParts = removePathParts;
58 _archiveHandler = archiveHandler;
59 _directoryPath = directoryPath;
60 NFile::NName::NormalizeDirPathPrefix(_directoryPath);
63 STDMETHODIMP CArchiveExtractCallback::SetTotal(UInt64 size)
67 if (!_multiArchives && _extractCallback2)
68 return _extractCallback2->SetTotal(size);
73 static void NormalizeVals(UInt64 &v1, UInt64 &v2)
75 const UInt64 kMax = (UInt64)1 << 31;
83 static UInt64 MyMultDiv64(UInt64 unpCur, UInt64 unpTotal, UInt64 packTotal)
85 NormalizeVals(packTotal, unpTotal);
86 NormalizeVals(unpCur, unpTotal);
89 return unpCur * packTotal / unpTotal;
92 STDMETHODIMP CArchiveExtractCallback::SetCompleted(const UInt64 *completeValue)
95 if (!_extractCallback2)
100 if (completeValue != NULL)
102 UInt64 packCur = LocalProgressSpec->InSize + MyMultDiv64(*completeValue, _unpTotal, _packTotal);
103 return _extractCallback2->SetCompleted(&packCur);
106 return _extractCallback2->SetCompleted(completeValue);
110 STDMETHODIMP CArchiveExtractCallback::SetRatioInfo(const UInt64 *inSize, const UInt64 *outSize)
113 return _localProgress->SetRatioInfo(inSize, outSize);
117 void CArchiveExtractCallback::CreateComplexDirectory(const UStringVector &dirPathParts, UString &fullPath)
119 fullPath = _directoryPath;
120 for(int i = 0; i < dirPathParts.Size(); i++)
123 fullPath += wchar_t(NFile::NName::kDirDelimiter);
124 fullPath += dirPathParts[i];
125 NFile::NDirectory::MyCreateDirectory(fullPath);
129 static UString MakePathNameFromParts(const UStringVector &parts)
132 for(int i = 0; i < parts.Size(); i++)
135 result += wchar_t(NFile::NName::kDirDelimiter);
142 HRESULT CArchiveExtractCallback::GetTime(int index, PROPID propID, FILETIME &filetime, bool &filetimeIsDefined)
144 filetimeIsDefined = false;
145 NCOM::CPropVariant prop;
146 RINOK(_archiveHandler->GetProperty(index, propID, &prop));
147 if (prop.vt == VT_FILETIME)
149 filetime = prop.filetime;
150 filetimeIsDefined = (filetime.dwHighDateTime != 0 || filetime.dwLowDateTime != 0);
152 else if (prop.vt != VT_EMPTY)
157 STDMETHODIMP CArchiveExtractCallback::GetStream(UInt32 index, ISequentialOutStream **outStream, Int32 askExtractMode)
161 _outFileStream.Release();
169 RINOK(GetArchiveItemPath(_archiveHandler, index, _itemDefaultName, fullPath));
170 RINOK(IsArchiveItemFolder(_archiveHandler, index, _processedFileInfo.IsDirectory));
172 _filePath = fullPath;
175 NCOM::CPropVariant prop;
176 RINOK(_archiveHandler->GetProperty(index, kpidPosition, &prop));
177 if (prop.vt != VT_EMPTY)
179 if (prop.vt != VT_UI8)
181 _position = prop.uhVal.QuadPart;
186 RINOK(IsArchiveItemProp(_archiveHandler, index, kpidEncrypted, _encrypted));
188 bool newFileSizeDefined;
191 NCOM::CPropVariant prop;
192 RINOK(_archiveHandler->GetProperty(index, kpidSize, &prop));
193 newFileSizeDefined = (prop.vt != VT_EMPTY);
194 if (newFileSizeDefined)
196 newFileSize = ConvertPropVariantToUInt64(prop);
197 _curSize = newFileSize;
201 if(askExtractMode == NArchive::NExtract::NAskMode::kExtract)
205 CMyComPtr<ISequentialOutStream> outStreamLoc = new CStdOutFileStream;
206 *outStream = outStreamLoc.Detach();
211 NCOM::CPropVariant prop;
212 RINOK(_archiveHandler->GetProperty(index, kpidAttributes, &prop));
213 if (prop.vt == VT_EMPTY)
215 _processedFileInfo.Attributes = _attributesDefault;
216 _processedFileInfo.AttributesAreDefined = false;
220 if (prop.vt != VT_UI4)
222 _processedFileInfo.Attributes = prop.ulVal;
223 _processedFileInfo.AttributesAreDefined = true;
227 RINOK(GetTime(index, kpidCreationTime, _processedFileInfo.CreationTime,
228 _processedFileInfo.IsCreationTimeDefined));
229 RINOK(GetTime(index, kpidLastWriteTime, _processedFileInfo.LastWriteTime,
230 _processedFileInfo.IsLastWriteTimeDefined));
231 RINOK(GetTime(index, kpidLastAccessTime, _processedFileInfo.LastAccessTime,
232 _processedFileInfo.IsLastAccessTimeDefined));
235 RINOK(IsArchiveItemProp(_archiveHandler, index, kpidIsAnti, isAnti));
237 UStringVector pathParts;
238 SplitPathToParts(fullPath, pathParts);
240 if(pathParts.IsEmpty())
242 int numRemovePathParts = 0;
245 case NExtract::NPathMode::kFullPathnames:
247 case NExtract::NPathMode::kCurrentPathnames:
249 numRemovePathParts = _removePathParts.Size();
250 if (pathParts.Size() <= numRemovePathParts)
252 for (int i = 0; i < numRemovePathParts; i++)
253 if (_removePathParts[i].CompareNoCase(pathParts[i]) != 0)
257 case NExtract::NPathMode::kNoPathnames:
259 numRemovePathParts = pathParts.Size() - 1;
263 pathParts.Delete(0, numRemovePathParts);
264 MakeCorrectPath(pathParts);
265 UString processedPath = MakePathNameFromParts(pathParts);
268 if (!_processedFileInfo.IsDirectory)
270 if (!pathParts.IsEmpty())
271 pathParts.DeleteBack();
274 if (!pathParts.IsEmpty())
277 CreateComplexDirectory(pathParts, fullPathNew);
278 if (_processedFileInfo.IsDirectory)
279 NFile::NDirectory::SetDirTime(fullPathNew,
280 (WriteCreated && _processedFileInfo.IsCreationTimeDefined) ? &_processedFileInfo.CreationTime : NULL,
281 (WriteAccessed && _processedFileInfo.IsLastAccessTimeDefined) ? &_processedFileInfo.LastAccessTime : NULL,
282 (WriteModified && _processedFileInfo.IsLastWriteTimeDefined) ? &_processedFileInfo.LastWriteTime : &_utcLastWriteTimeDefault);
287 UString fullProcessedPath = _directoryPath + processedPath;
289 if(_processedFileInfo.IsDirectory)
291 _diskFilePath = fullProcessedPath;
293 NFile::NDirectory::MyRemoveDirectory(_diskFilePath);
299 NFile::NFind::CFileInfoW fileInfo;
300 if(NFile::NFind::FindFile(fullProcessedPath, fileInfo))
302 switch(_overwriteMode)
304 case NExtract::NOverwriteMode::kSkipExisting:
306 case NExtract::NOverwriteMode::kAskBefore:
308 Int32 overwiteResult;
309 RINOK(_extractCallback2->AskOverwrite(
310 fullProcessedPath, &fileInfo.LastWriteTime, &fileInfo.Size, fullPath,
311 _processedFileInfo.IsLastWriteTimeDefined ? &_processedFileInfo.LastWriteTime : NULL,
312 newFileSizeDefined ? &newFileSize : NULL,
315 switch(overwiteResult)
317 case NOverwriteAnswer::kCancel:
319 case NOverwriteAnswer::kNo:
321 case NOverwriteAnswer::kNoToAll:
322 _overwriteMode = NExtract::NOverwriteMode::kSkipExisting;
324 case NOverwriteAnswer::kYesToAll:
325 _overwriteMode = NExtract::NOverwriteMode::kWithoutPrompt;
327 case NOverwriteAnswer::kYes:
329 case NOverwriteAnswer::kAutoRename:
330 _overwriteMode = NExtract::NOverwriteMode::kAutoRename;
337 if (_overwriteMode == NExtract::NOverwriteMode::kAutoRename)
339 if (!AutoRenamePath(fullProcessedPath))
341 UString message = UString(kCantAutoRename) + fullProcessedPath;
342 RINOK(_extractCallback2->MessageError(message));
346 else if (_overwriteMode == NExtract::NOverwriteMode::kAutoRenameExisting)
348 UString existPath = fullProcessedPath;
349 if (!AutoRenamePath(existPath))
351 UString message = kCantAutoRename + fullProcessedPath;
352 RINOK(_extractCallback2->MessageError(message));
355 if(!NFile::NDirectory::MyMoveFile(fullProcessedPath, existPath))
357 UString message = UString(kCantRenameFile) + fullProcessedPath;
358 RINOK(_extractCallback2->MessageError(message));
363 if (!NFile::NDirectory::DeleteFileAlways(fullProcessedPath))
365 UString message = UString(kCantDeleteOutputFile) + fullProcessedPath;
366 RINOK(_extractCallback2->MessageError(message));
374 _outFileStreamSpec = new COutFileStream;
375 CMyComPtr<ISequentialOutStream> outStreamLoc(_outFileStreamSpec);
376 if (!_outFileStreamSpec->Open(fullProcessedPath, _isSplit ? OPEN_ALWAYS: CREATE_ALWAYS))
378 // if (::GetLastError() != ERROR_FILE_EXISTS || !isSplit)
380 UString message = L"can not open output file " + fullProcessedPath;
381 RINOK(_extractCallback2->MessageError(message));
387 RINOK(_outFileStreamSpec->Seek(_position, STREAM_SEEK_SET, NULL));
389 _outFileStream = outStreamLoc;
390 *outStream = outStreamLoc.Detach();
392 _diskFilePath = fullProcessedPath;
402 STDMETHODIMP CArchiveExtractCallback::PrepareOperation(Int32 askExtractMode)
405 _extractMode = false;
406 switch (askExtractMode)
408 case NArchive::NExtract::NAskMode::kExtract:
411 return _extractCallback2->PrepareOperation(_filePath, _processedFileInfo.IsDirectory,
412 askExtractMode, _isSplit ? &_position: 0);
416 STDMETHODIMP CArchiveExtractCallback::SetOperationResult(Int32 operationResult)
419 switch(operationResult)
421 case NArchive::NExtract::NOperationResult::kOK:
422 case NArchive::NExtract::NOperationResult::kUnSupportedMethod:
423 case NArchive::NExtract::NOperationResult::kCRCError:
424 case NArchive::NExtract::NOperationResult::kDataError:
427 _outFileStream.Release();
430 if (_outFileStream != NULL)
432 _outFileStreamSpec->SetTime(
433 (WriteCreated && _processedFileInfo.IsCreationTimeDefined) ? &_processedFileInfo.CreationTime : NULL,
434 (WriteAccessed && _processedFileInfo.IsLastAccessTimeDefined) ? &_processedFileInfo.LastAccessTime : NULL,
435 (WriteModified && _processedFileInfo.IsLastWriteTimeDefined) ? &_processedFileInfo.LastWriteTime : &_utcLastWriteTimeDefault);
436 _curSize = _outFileStreamSpec->ProcessedSize;
437 RINOK(_outFileStreamSpec->Close());
438 _outFileStream.Release();
440 UnpackSize += _curSize;
441 if (_processedFileInfo.IsDirectory)
446 if (_extractMode && _processedFileInfo.AttributesAreDefined)
447 NFile::NDirectory::MySetFileAttributes(_diskFilePath, _processedFileInfo.Attributes);
448 RINOK(_extractCallback2->SetOperationResult(operationResult, _encrypted));
454 STDMETHODIMP CArchiveExtractCallback::GetInStream(
455 const wchar_t *name, ISequentialInStream **inStream)
458 CInFileStream *inFile = new CInFileStream;
459 CMyComPtr<ISequentialInStream> inStreamTemp = inFile;
460 if (!inFile->Open(_srcDirectoryPrefix + name))
461 return ::GetLastError();
462 *inStream = inStreamTemp.Detach();
468 STDMETHODIMP CArchiveExtractCallback::CryptoGetTextPassword(BSTR *password)
471 if (!_cryptoGetTextPassword)
473 RINOK(_extractCallback2.QueryInterface(IID_ICryptoGetTextPassword,
474 &_cryptoGetTextPassword));
476 return _cryptoGetTextPassword->CryptoGetTextPassword(password);