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 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 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 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 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 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 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 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 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 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 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 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 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 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