// ArchiveExtractCallback.cpp | |
#include "StdAfx.h" | |
#include "Common/ComTry.h" | |
#include "Common/Wildcard.h" | |
#include "Windows/FileDir.h" | |
#include "Windows/FileFind.h" | |
#include "Windows/PropVariant.h" | |
#include "Windows/PropVariantConversions.h" | |
#include "../../Common/FilePathAutoRename.h" | |
#include "../Common/ExtractingFilePath.h" | |
#include "ArchiveExtractCallback.h" | |
using namespace NWindows; | |
static const wchar_t *kCantAutoRename = L"ERROR: Can not create file with auto name"; | |
static const wchar_t *kCantRenameFile = L"ERROR: Can not rename existing file "; | |
static const wchar_t *kCantDeleteOutputFile = L"ERROR: Can not delete output file "; | |
void CArchiveExtractCallback::Init( | |
const NWildcard::CCensorNode *wildcardCensor, | |
const CArc *arc, | |
IFolderArchiveExtractCallback *extractCallback2, | |
bool stdOutMode, bool testMode, bool crcMode, | |
const UString &directoryPath, | |
const UStringVector &removePathParts, | |
UInt64 packSize) | |
{ | |
_wildcardCensor = wildcardCensor; | |
_stdOutMode = stdOutMode; | |
_testMode = testMode; | |
_crcMode = crcMode; | |
_unpTotal = 1; | |
_packTotal = packSize; | |
_extractCallback2 = extractCallback2; | |
_compressProgress.Release(); | |
_extractCallback2.QueryInterface(IID_ICompressProgressInfo, &_compressProgress); | |
LocalProgressSpec->Init(extractCallback2, true); | |
LocalProgressSpec->SendProgress = false; | |
_removePathParts = removePathParts; | |
_arc = arc; | |
_directoryPath = directoryPath; | |
NFile::NName::NormalizeDirPathPrefix(_directoryPath); | |
} | |
STDMETHODIMP CArchiveExtractCallback::SetTotal(UInt64 size) | |
{ | |
COM_TRY_BEGIN | |
_unpTotal = size; | |
if (!_multiArchives && _extractCallback2) | |
return _extractCallback2->SetTotal(size); | |
return S_OK; | |
COM_TRY_END | |
} | |
static void NormalizeVals(UInt64 &v1, UInt64 &v2) | |
{ | |
const UInt64 kMax = (UInt64)1 << 31; | |
while (v1 > kMax) | |
{ | |
v1 >>= 1; | |
v2 >>= 1; | |
} | |
} | |
static UInt64 MyMultDiv64(UInt64 unpCur, UInt64 unpTotal, UInt64 packTotal) | |
{ | |
NormalizeVals(packTotal, unpTotal); | |
NormalizeVals(unpCur, unpTotal); | |
if (unpTotal == 0) | |
unpTotal = 1; | |
return unpCur * packTotal / unpTotal; | |
} | |
STDMETHODIMP CArchiveExtractCallback::SetCompleted(const UInt64 *completeValue) | |
{ | |
COM_TRY_BEGIN | |
if (!_extractCallback2) | |
return S_OK; | |
if (_multiArchives) | |
{ | |
if (completeValue != NULL) | |
{ | |
UInt64 packCur = LocalProgressSpec->InSize + MyMultDiv64(*completeValue, _unpTotal, _packTotal); | |
return _extractCallback2->SetCompleted(&packCur); | |
} | |
} | |
return _extractCallback2->SetCompleted(completeValue); | |
COM_TRY_END | |
} | |
STDMETHODIMP CArchiveExtractCallback::SetRatioInfo(const UInt64 *inSize, const UInt64 *outSize) | |
{ | |
COM_TRY_BEGIN | |
return _localProgress->SetRatioInfo(inSize, outSize); | |
COM_TRY_END | |
} | |
void CArchiveExtractCallback::CreateComplexDirectory(const UStringVector &dirPathParts, UString &fullPath) | |
{ | |
fullPath = _directoryPath; | |
for (int i = 0; i < dirPathParts.Size(); i++) | |
{ | |
if (i > 0) | |
fullPath += wchar_t(NFile::NName::kDirDelimiter); | |
fullPath += dirPathParts[i]; | |
NFile::NDirectory::MyCreateDirectory(fullPath); | |
} | |
} | |
HRESULT CArchiveExtractCallback::GetTime(int index, PROPID propID, FILETIME &filetime, bool &filetimeIsDefined) | |
{ | |
filetimeIsDefined = false; | |
NCOM::CPropVariant prop; | |
RINOK(_arc->Archive->GetProperty(index, propID, &prop)); | |
if (prop.vt == VT_FILETIME) | |
{ | |
filetime = prop.filetime; | |
filetimeIsDefined = (filetime.dwHighDateTime != 0 || filetime.dwLowDateTime != 0); | |
} | |
else if (prop.vt != VT_EMPTY) | |
return E_FAIL; | |
return S_OK; | |
} | |
HRESULT CArchiveExtractCallback::GetUnpackSize() | |
{ | |
NCOM::CPropVariant prop; | |
RINOK(_arc->Archive->GetProperty(_index, kpidSize, &prop)); | |
_curSizeDefined = (prop.vt != VT_EMPTY); | |
if (_curSizeDefined) | |
_curSize = ConvertPropVariantToUInt64(prop); | |
return S_OK; | |
} | |
STDMETHODIMP CArchiveExtractCallback::GetStream(UInt32 index, ISequentialOutStream **outStream, Int32 askExtractMode) | |
{ | |
COM_TRY_BEGIN | |
_crcStream.Release(); | |
*outStream = 0; | |
_outFileStream.Release(); | |
_encrypted = false; | |
_isSplit = false; | |
_curSize = 0; | |
_curSizeDefined = false; | |
_index = index; | |
UString fullPath; | |
IInArchive *archive = _arc->Archive; | |
RINOK(_arc->GetItemPath(index, fullPath)); | |
RINOK(IsArchiveItemFolder(archive, index, _fi.IsDir)); | |
_filePath = fullPath; | |
{ | |
NCOM::CPropVariant prop; | |
RINOK(archive->GetProperty(index, kpidPosition, &prop)); | |
if (prop.vt != VT_EMPTY) | |
{ | |
if (prop.vt != VT_UI8) | |
return E_FAIL; | |
_position = prop.uhVal.QuadPart; | |
_isSplit = true; | |
} | |
} | |
RINOK(GetArchiveItemBoolProp(archive, index, kpidEncrypted, _encrypted)); | |
RINOK(GetUnpackSize()); | |
if (_wildcardCensor) | |
{ | |
if (!_wildcardCensor->CheckPath(fullPath, !_fi.IsDir)) | |
return S_OK; | |
} | |
if (askExtractMode == NArchive::NExtract::NAskMode::kExtract && !_testMode) | |
{ | |
if (_stdOutMode) | |
{ | |
CMyComPtr<ISequentialOutStream> outStreamLoc = new CStdOutFileStream; | |
*outStream = outStreamLoc.Detach(); | |
return S_OK; | |
} | |
{ | |
NCOM::CPropVariant prop; | |
RINOK(archive->GetProperty(index, kpidAttrib, &prop)); | |
if (prop.vt == VT_UI4) | |
{ | |
_fi.Attrib = prop.ulVal; | |
_fi.AttribDefined = true; | |
} | |
else if (prop.vt == VT_EMPTY) | |
_fi.AttribDefined = false; | |
else | |
return E_FAIL; | |
} | |
RINOK(GetTime(index, kpidCTime, _fi.CTime, _fi.CTimeDefined)); | |
RINOK(GetTime(index, kpidATime, _fi.ATime, _fi.ATimeDefined)); | |
RINOK(GetTime(index, kpidMTime, _fi.MTime, _fi.MTimeDefined)); | |
bool isAnti = false; | |
RINOK(_arc->IsItemAnti(index, isAnti)); | |
UStringVector pathParts; | |
SplitPathToParts(fullPath, pathParts); | |
if (pathParts.IsEmpty()) | |
return E_FAIL; | |
int numRemovePathParts = 0; | |
switch(_pathMode) | |
{ | |
case NExtract::NPathMode::kFullPathnames: | |
break; | |
case NExtract::NPathMode::kCurrentPathnames: | |
{ | |
numRemovePathParts = _removePathParts.Size(); | |
if (pathParts.Size() <= numRemovePathParts) | |
return E_FAIL; | |
for (int i = 0; i < numRemovePathParts; i++) | |
if (_removePathParts[i].CompareNoCase(pathParts[i]) != 0) | |
return E_FAIL; | |
break; | |
} | |
case NExtract::NPathMode::kNoPathnames: | |
{ | |
numRemovePathParts = pathParts.Size() - 1; | |
break; | |
} | |
} | |
pathParts.Delete(0, numRemovePathParts); | |
MakeCorrectPath(pathParts); | |
UString processedPath = MakePathNameFromParts(pathParts); | |
if (!isAnti) | |
{ | |
if (!_fi.IsDir) | |
{ | |
if (!pathParts.IsEmpty()) | |
pathParts.DeleteBack(); | |
} | |
if (!pathParts.IsEmpty()) | |
{ | |
UString fullPathNew; | |
CreateComplexDirectory(pathParts, fullPathNew); | |
if (_fi.IsDir) | |
NFile::NDirectory::SetDirTime(fullPathNew, | |
(WriteCTime && _fi.CTimeDefined) ? &_fi.CTime : NULL, | |
(WriteATime && _fi.ATimeDefined) ? &_fi.ATime : NULL, | |
(WriteMTime && _fi.MTimeDefined) ? &_fi.MTime : (_arc->MTimeDefined ? &_arc->MTime : NULL)); | |
} | |
} | |
UString fullProcessedPath = _directoryPath + processedPath; | |
if (_fi.IsDir) | |
{ | |
_diskFilePath = fullProcessedPath; | |
if (isAnti) | |
NFile::NDirectory::MyRemoveDirectory(_diskFilePath); | |
return S_OK; | |
} | |
if (!_isSplit) | |
{ | |
NFile::NFind::CFileInfoW fileInfo; | |
if (fileInfo.Find(fullProcessedPath)) | |
{ | |
switch(_overwriteMode) | |
{ | |
case NExtract::NOverwriteMode::kSkipExisting: | |
return S_OK; | |
case NExtract::NOverwriteMode::kAskBefore: | |
{ | |
Int32 overwiteResult; | |
RINOK(_extractCallback2->AskOverwrite( | |
fullProcessedPath, &fileInfo.MTime, &fileInfo.Size, fullPath, | |
_fi.MTimeDefined ? &_fi.MTime : NULL, | |
_curSizeDefined ? &_curSize : NULL, | |
&overwiteResult)) | |
switch(overwiteResult) | |
{ | |
case NOverwriteAnswer::kCancel: | |
return E_ABORT; | |
case NOverwriteAnswer::kNo: | |
return S_OK; | |
case NOverwriteAnswer::kNoToAll: | |
_overwriteMode = NExtract::NOverwriteMode::kSkipExisting; | |
return S_OK; | |
case NOverwriteAnswer::kYesToAll: | |
_overwriteMode = NExtract::NOverwriteMode::kWithoutPrompt; | |
break; | |
case NOverwriteAnswer::kYes: | |
break; | |
case NOverwriteAnswer::kAutoRename: | |
_overwriteMode = NExtract::NOverwriteMode::kAutoRename; | |
break; | |
default: | |
return E_FAIL; | |
} | |
} | |
} | |
if (_overwriteMode == NExtract::NOverwriteMode::kAutoRename) | |
{ | |
if (!AutoRenamePath(fullProcessedPath)) | |
{ | |
UString message = UString(kCantAutoRename) + fullProcessedPath; | |
RINOK(_extractCallback2->MessageError(message)); | |
return E_FAIL; | |
} | |
} | |
else if (_overwriteMode == NExtract::NOverwriteMode::kAutoRenameExisting) | |
{ | |
UString existPath = fullProcessedPath; | |
if (!AutoRenamePath(existPath)) | |
{ | |
UString message = kCantAutoRename + fullProcessedPath; | |
RINOK(_extractCallback2->MessageError(message)); | |
return E_FAIL; | |
} | |
if (!NFile::NDirectory::MyMoveFile(fullProcessedPath, existPath)) | |
{ | |
UString message = UString(kCantRenameFile) + fullProcessedPath; | |
RINOK(_extractCallback2->MessageError(message)); | |
return E_FAIL; | |
} | |
} | |
else | |
if (!NFile::NDirectory::DeleteFileAlways(fullProcessedPath)) | |
{ | |
UString message = UString(kCantDeleteOutputFile) + fullProcessedPath; | |
RINOK(_extractCallback2->MessageError(message)); | |
return S_OK; | |
// return E_FAIL; | |
} | |
} | |
} | |
if (!isAnti) | |
{ | |
_outFileStreamSpec = new COutFileStream; | |
CMyComPtr<ISequentialOutStream> outStreamLoc(_outFileStreamSpec); | |
if (!_outFileStreamSpec->Open(fullProcessedPath, _isSplit ? OPEN_ALWAYS: CREATE_ALWAYS)) | |
{ | |
// if (::GetLastError() != ERROR_FILE_EXISTS || !isSplit) | |
{ | |
UString message = L"can not open output file " + fullProcessedPath; | |
RINOK(_extractCallback2->MessageError(message)); | |
return S_OK; | |
} | |
} | |
if (_isSplit) | |
{ | |
RINOK(_outFileStreamSpec->Seek(_position, STREAM_SEEK_SET, NULL)); | |
} | |
_outFileStream = outStreamLoc; | |
*outStream = outStreamLoc.Detach(); | |
} | |
_diskFilePath = fullProcessedPath; | |
} | |
else | |
{ | |
*outStream = NULL; | |
} | |
if (_crcMode) | |
{ | |
_crcStreamSpec = new COutStreamWithCRC; | |
_crcStream = _crcStreamSpec; | |
CMyComPtr<ISequentialOutStream> crcStream = _crcStreamSpec; | |
_crcStreamSpec->SetStream(*outStream); | |
if (*outStream) | |
(*outStream)->Release(); | |
*outStream = crcStream.Detach(); | |
_crcStreamSpec->Init(true); | |
} | |
return S_OK; | |
COM_TRY_END | |
} | |
STDMETHODIMP CArchiveExtractCallback::PrepareOperation(Int32 askExtractMode) | |
{ | |
COM_TRY_BEGIN | |
_extractMode = false; | |
switch (askExtractMode) | |
{ | |
case NArchive::NExtract::NAskMode::kExtract: | |
if (_testMode) | |
askExtractMode = NArchive::NExtract::NAskMode::kTest; | |
else | |
_extractMode = true; | |
break; | |
}; | |
return _extractCallback2->PrepareOperation(_filePath, _fi.IsDir, | |
askExtractMode, _isSplit ? &_position: 0); | |
COM_TRY_END | |
} | |
STDMETHODIMP CArchiveExtractCallback::SetOperationResult(Int32 operationResult) | |
{ | |
COM_TRY_BEGIN | |
switch(operationResult) | |
{ | |
case NArchive::NExtract::NOperationResult::kOK: | |
case NArchive::NExtract::NOperationResult::kUnSupportedMethod: | |
case NArchive::NExtract::NOperationResult::kCRCError: | |
case NArchive::NExtract::NOperationResult::kDataError: | |
break; | |
default: | |
_outFileStream.Release(); | |
return E_FAIL; | |
} | |
if (_crcStream) | |
{ | |
CrcSum += _crcStreamSpec->GetCRC(); | |
_curSize = _crcStreamSpec->GetSize(); | |
_curSizeDefined = true; | |
_crcStream.Release(); | |
} | |
if (_outFileStream) | |
{ | |
_outFileStreamSpec->SetTime( | |
(WriteCTime && _fi.CTimeDefined) ? &_fi.CTime : NULL, | |
(WriteATime && _fi.ATimeDefined) ? &_fi.ATime : NULL, | |
(WriteMTime && _fi.MTimeDefined) ? &_fi.MTime : (_arc->MTimeDefined ? &_arc->MTime : NULL)); | |
_curSize = _outFileStreamSpec->ProcessedSize; | |
_curSizeDefined = true; | |
RINOK(_outFileStreamSpec->Close()); | |
_outFileStream.Release(); | |
} | |
if (!_curSizeDefined) | |
GetUnpackSize(); | |
if (_curSizeDefined) | |
UnpackSize += _curSize; | |
if (_fi.IsDir) | |
NumFolders++; | |
else | |
NumFiles++; | |
if (_extractMode && _fi.AttribDefined) | |
NFile::NDirectory::MySetFileAttributes(_diskFilePath, _fi.Attrib); | |
RINOK(_extractCallback2->SetOperationResult(operationResult, _encrypted)); | |
return S_OK; | |
COM_TRY_END | |
} | |
/* | |
STDMETHODIMP CArchiveExtractCallback::GetInStream( | |
const wchar_t *name, ISequentialInStream **inStream) | |
{ | |
COM_TRY_BEGIN | |
CInFileStream *inFile = new CInFileStream; | |
CMyComPtr<ISequentialInStream> inStreamTemp = inFile; | |
if (!inFile->Open(_srcDirectoryPrefix + name)) | |
return ::GetLastError(); | |
*inStream = inStreamTemp.Detach(); | |
return S_OK; | |
COM_TRY_END | |
} | |
*/ | |
STDMETHODIMP CArchiveExtractCallback::CryptoGetTextPassword(BSTR *password) | |
{ | |
COM_TRY_BEGIN | |
if (!_cryptoGetTextPassword) | |
{ | |
RINOK(_extractCallback2.QueryInterface(IID_ICryptoGetTextPassword, | |
&_cryptoGetTextPassword)); | |
} | |
return _cryptoGetTextPassword->CryptoGetTextPassword(password); | |
COM_TRY_END | |
} | |