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