1 // ExtractCallback.cpp 2 3 #include "StdAfx.h" 4 5 6 #include "../../../Common/ComTry.h" 7 #include "../../../Common/IntToString.h" 8 #include "../../../Common/Lang.h" 9 #include "../../../Common/StringConvert.h" 10 11 #include "../../../Windows/ErrorMsg.h" 12 #include "../../../Windows/FileDir.h" 13 #include "../../../Windows/FileFind.h" 14 #include "../../../Windows/PropVariantConv.h" 15 16 #include "../../Common/FilePathAutoRename.h" 17 #include "../../Common/StreamUtils.h" 18 #include "../Common/ExtractingFilePath.h" 19 20 #ifndef _SFX 21 #include "../Common/ZipRegistry.h" 22 #endif 23 24 #include "../GUI/ExtractRes.h" 25 #include "resourceGui.h" 26 27 #include "ExtractCallback.h" 28 #include "FormatUtils.h" 29 #include "LangUtils.h" 30 #include "OverwriteDialog.h" 31 #ifndef _NO_CRYPTO 32 #include "PasswordDialog.h" 33 #endif 34 #include "PropertyName.h" 35 36 using namespace NWindows; 37 using namespace NFile; 38 using namespace NFind; 39 40 CExtractCallbackImp::~CExtractCallbackImp() {} 41 42 void CExtractCallbackImp::Init() 43 { 44 _lang_Extracting = LangString(IDS_PROGRESS_EXTRACTING); 45 _lang_Testing = LangString(IDS_PROGRESS_TESTING); 46 _lang_Skipping = LangString(IDS_PROGRESS_SKIPPING); 47 48 NumArchiveErrors = 0; 49 ThereAreMessageErrors = false; 50 #ifndef _SFX 51 NumFolders = NumFiles = 0; 52 NeedAddFile = false; 53 #endif 54 } 55 56 void CExtractCallbackImp::AddError_Message(LPCWSTR s) 57 { 58 ThereAreMessageErrors = true; 59 ProgressDialog->Sync.AddError_Message(s); 60 } 61 62 #ifndef _SFX 63 64 STDMETHODIMP CExtractCallbackImp::SetNumFiles(UInt64 65 #ifndef _SFX 66 numFiles 67 #endif 68 ) 69 { 70 #ifndef _SFX 71 ProgressDialog->Sync.Set_NumFilesTotal(numFiles); 72 #endif 73 return S_OK; 74 } 75 76 #endif 77 78 STDMETHODIMP CExtractCallbackImp::SetTotal(UInt64 total) 79 { 80 ProgressDialog->Sync.Set_NumBytesTotal(total); 81 return S_OK; 82 } 83 84 STDMETHODIMP CExtractCallbackImp::SetCompleted(const UInt64 *value) 85 { 86 return ProgressDialog->Sync.Set_NumBytesCur(value); 87 } 88 89 HRESULT CExtractCallbackImp::Open_CheckBreak() 90 { 91 return ProgressDialog->Sync.CheckStop(); 92 } 93 94 HRESULT CExtractCallbackImp::Open_SetTotal(const UInt64 *files, const UInt64 *bytes) 95 { 96 HRESULT res = S_OK; 97 if (!MultiArcMode) 98 { 99 if (files) 100 { 101 _totalFilesDefined = true; 102 // res = ProgressDialog->Sync.Set_NumFilesTotal(*files); 103 } 104 else 105 _totalFilesDefined = false; 106 107 if (bytes) 108 { 109 _totalBytesDefined = true; 110 ProgressDialog->Sync.Set_NumBytesTotal(*bytes); 111 } 112 else 113 _totalBytesDefined = false; 114 } 115 116 return res; 117 } 118 119 HRESULT CExtractCallbackImp::Open_SetCompleted(const UInt64 *files, const UInt64 *bytes) 120 { 121 if (!MultiArcMode) 122 { 123 if (files) 124 { 125 ProgressDialog->Sync.Set_NumFilesCur(*files); 126 } 127 128 if (bytes) 129 { 130 } 131 } 132 133 return ProgressDialog->Sync.CheckStop(); 134 } 135 136 HRESULT CExtractCallbackImp::Open_Finished() 137 { 138 return ProgressDialog->Sync.CheckStop(); 139 } 140 141 #ifndef _NO_CRYPTO 142 143 HRESULT CExtractCallbackImp::Open_CryptoGetTextPassword(BSTR *password) 144 { 145 return CryptoGetTextPassword(password); 146 } 147 148 /* 149 HRESULT CExtractCallbackImp::Open_GetPasswordIfAny(bool &passwordIsDefined, UString &password) 150 { 151 passwordIsDefined = PasswordIsDefined; 152 password = Password; 153 return S_OK; 154 } 155 156 bool CExtractCallbackImp::Open_WasPasswordAsked() 157 { 158 return PasswordWasAsked; 159 } 160 161 void CExtractCallbackImp::Open_Clear_PasswordWasAsked_Flag() 162 { 163 PasswordWasAsked = false; 164 } 165 */ 166 167 #endif 168 169 170 #ifndef _SFX 171 STDMETHODIMP CExtractCallbackImp::SetRatioInfo(const UInt64 *inSize, const UInt64 *outSize) 172 { 173 ProgressDialog->Sync.Set_Ratio(inSize, outSize); 174 return S_OK; 175 } 176 #endif 177 178 /* 179 STDMETHODIMP CExtractCallbackImp::SetTotalFiles(UInt64 total) 180 { 181 ProgressDialog->Sync.SetNumFilesTotal(total); 182 return S_OK; 183 } 184 185 STDMETHODIMP CExtractCallbackImp::SetCompletedFiles(const UInt64 *value) 186 { 187 if (value != NULL) 188 ProgressDialog->Sync.SetNumFilesCur(*value); 189 return S_OK; 190 } 191 */ 192 193 STDMETHODIMP CExtractCallbackImp::AskOverwrite( 194 const wchar_t *existName, const FILETIME *existTime, const UInt64 *existSize, 195 const wchar_t *newName, const FILETIME *newTime, const UInt64 *newSize, 196 Int32 *answer) 197 { 198 COverwriteDialog dialog; 199 200 dialog.OldFileInfo.SetTime(existTime); 201 dialog.OldFileInfo.SetSize(existSize); 202 dialog.OldFileInfo.Name = existName; 203 204 dialog.NewFileInfo.SetTime(newTime); 205 dialog.NewFileInfo.SetSize(newSize); 206 dialog.NewFileInfo.Name = newName; 207 208 ProgressDialog->WaitCreating(); 209 INT_PTR writeAnswer = dialog.Create(*ProgressDialog); 210 211 switch (writeAnswer) 212 { 213 case IDCANCEL: *answer = NOverwriteAnswer::kCancel; return E_ABORT; 214 case IDYES: *answer = NOverwriteAnswer::kYes; break; 215 case IDNO: *answer = NOverwriteAnswer::kNo; break; 216 case IDB_YES_TO_ALL: *answer = NOverwriteAnswer::kYesToAll; break; 217 case IDB_NO_TO_ALL: *answer = NOverwriteAnswer::kNoToAll; break; 218 case IDB_AUTO_RENAME: *answer = NOverwriteAnswer::kAutoRename; break; 219 default: return E_FAIL; 220 } 221 return S_OK; 222 } 223 224 225 STDMETHODIMP CExtractCallbackImp::PrepareOperation(const wchar_t *name, Int32 isFolder, Int32 askExtractMode, const UInt64 * /* position */) 226 { 227 _isFolder = IntToBool(isFolder); 228 _currentFilePath = name; 229 230 const UString *msg = &_lang_Empty; 231 switch (askExtractMode) 232 { 233 case NArchive::NExtract::NAskMode::kExtract: msg = &_lang_Extracting; break; 234 case NArchive::NExtract::NAskMode::kTest: msg = &_lang_Testing; break; 235 case NArchive::NExtract::NAskMode::kSkip: msg = &_lang_Skipping; break; 236 // default: s = "Unknown operation"; 237 } 238 239 return ProgressDialog->Sync.Set_Status2(*msg, name, IntToBool(isFolder)); 240 } 241 242 STDMETHODIMP CExtractCallbackImp::MessageError(const wchar_t *s) 243 { 244 AddError_Message(s); 245 return S_OK; 246 } 247 248 HRESULT CExtractCallbackImp::MessageError(const char *message, const FString &path) 249 { 250 ThereAreMessageErrors = true; 251 ProgressDialog->Sync.AddError_Message_Name(GetUnicodeString(message), fs2us(path)); 252 return S_OK; 253 } 254 255 #ifndef _SFX 256 257 STDMETHODIMP CExtractCallbackImp::ShowMessage(const wchar_t *s) 258 { 259 AddError_Message(s); 260 return S_OK; 261 } 262 263 #endif 264 265 void SetExtractErrorMessage(Int32 opRes, Int32 encrypted, const wchar_t *fileName, UString &s) 266 { 267 s.Empty(); 268 269 if (opRes == NArchive::NExtract::NOperationResult::kOK) 270 return; 271 272 UINT messageID = 0; 273 UINT id = 0; 274 275 switch (opRes) 276 { 277 case NArchive::NExtract::NOperationResult::kUnsupportedMethod: 278 messageID = IDS_EXTRACT_MESSAGE_UNSUPPORTED_METHOD; 279 id = IDS_EXTRACT_MSG_UNSUPPORTED_METHOD; 280 break; 281 case NArchive::NExtract::NOperationResult::kDataError: 282 messageID = encrypted ? 283 IDS_EXTRACT_MESSAGE_DATA_ERROR_ENCRYPTED: 284 IDS_EXTRACT_MESSAGE_DATA_ERROR; 285 id = IDS_EXTRACT_MSG_DATA_ERROR; 286 break; 287 case NArchive::NExtract::NOperationResult::kCRCError: 288 messageID = encrypted ? 289 IDS_EXTRACT_MESSAGE_CRC_ERROR_ENCRYPTED: 290 IDS_EXTRACT_MESSAGE_CRC_ERROR; 291 id = IDS_EXTRACT_MSG_CRC_ERROR; 292 break; 293 case NArchive::NExtract::NOperationResult::kUnavailable: 294 id = IDS_EXTRACT_MSG_UNAVAILABLE_DATA; 295 break; 296 case NArchive::NExtract::NOperationResult::kUnexpectedEnd: 297 id = IDS_EXTRACT_MSG_UEXPECTED_END; 298 break; 299 case NArchive::NExtract::NOperationResult::kDataAfterEnd: 300 id = IDS_EXTRACT_MSG_DATA_AFTER_END; 301 break; 302 case NArchive::NExtract::NOperationResult::kIsNotArc: 303 id = IDS_EXTRACT_MSG_IS_NOT_ARC; 304 break; 305 case NArchive::NExtract::NOperationResult::kHeadersError: 306 id = IDS_EXTRACT_MSG_HEADERS_ERROR; 307 break; 308 case NArchive::NExtract::NOperationResult::kWrongPassword: 309 id = IDS_EXTRACT_MSG_WRONG_PSW_CLAIM; 310 break; 311 /* 312 default: 313 messageID = IDS_EXTRACT_MESSAGE_UNKNOWN_ERROR; 314 break; 315 */ 316 } 317 318 UString msg; 319 UString msgOld; 320 321 #ifndef _SFX 322 if (id != 0) 323 LangString_OnlyFromLangFile(id, msg); 324 if (messageID != 0 && msg.IsEmpty()) 325 LangString_OnlyFromLangFile(messageID, msgOld); 326 #endif 327 328 if (msg.IsEmpty() && !msgOld.IsEmpty()) 329 s = MyFormatNew(msgOld, fileName); 330 else 331 { 332 if (msg.IsEmpty() && id != 0) 333 LangString(id, msg); 334 if (!msg.IsEmpty()) 335 s += msg; 336 else 337 { 338 char temp[16]; 339 ConvertUInt32ToString(opRes, temp); 340 s.AddAscii("Error #"); 341 s.AddAscii(temp); 342 } 343 344 if (encrypted && opRes != NArchive::NExtract::NOperationResult::kWrongPassword) 345 { 346 // s.AddAscii(" : "); 347 // AddLangString(s, IDS_EXTRACT_MSG_ENCRYPTED); 348 s.AddAscii(" : "); 349 AddLangString(s, IDS_EXTRACT_MSG_WRONG_PSW_GUESS); 350 } 351 s.AddAscii(" : "); 352 s += fileName; 353 } 354 } 355 356 STDMETHODIMP CExtractCallbackImp::SetOperationResult(Int32 opRes, Int32 encrypted) 357 { 358 switch (opRes) 359 { 360 case NArchive::NExtract::NOperationResult::kOK: 361 break; 362 default: 363 { 364 UString s; 365 SetExtractErrorMessage(opRes, encrypted, _currentFilePath, s); 366 Add_ArchiveName_Error(); 367 AddError_Message(s); 368 } 369 } 370 371 #ifndef _SFX 372 if (_isFolder) 373 NumFolders++; 374 else 375 NumFiles++; 376 ProgressDialog->Sync.Set_NumFilesCur(NumFiles); 377 #endif 378 379 return S_OK; 380 } 381 382 STDMETHODIMP CExtractCallbackImp::ReportExtractResult(Int32 opRes, Int32 encrypted, const wchar_t *name) 383 { 384 if (opRes != NArchive::NExtract::NOperationResult::kOK) 385 { 386 UString s; 387 SetExtractErrorMessage(opRes, encrypted, name, s); 388 Add_ArchiveName_Error(); 389 AddError_Message(s); 390 } 391 return S_OK; 392 } 393 394 //////////////////////////////////////// 395 // IExtractCallbackUI 396 397 HRESULT CExtractCallbackImp::BeforeOpen(const wchar_t *name, bool /* testMode */) 398 { 399 #ifndef _SFX 400 RINOK(ProgressDialog->Sync.CheckStop()); 401 ProgressDialog->Sync.Set_TitleFileName(name); 402 #endif 403 _currentArchivePath = name; 404 return S_OK; 405 } 406 407 HRESULT CExtractCallbackImp::SetCurrentFilePath2(const wchar_t *path) 408 { 409 _currentFilePath = path; 410 #ifndef _SFX 411 ProgressDialog->Sync.Set_FilePath(path); 412 #endif 413 return S_OK; 414 } 415 416 #ifndef _SFX 417 418 HRESULT CExtractCallbackImp::SetCurrentFilePath(const wchar_t *path) 419 { 420 #ifndef _SFX 421 if (NeedAddFile) 422 NumFiles++; 423 NeedAddFile = true; 424 ProgressDialog->Sync.Set_NumFilesCur(NumFiles); 425 #endif 426 return SetCurrentFilePath2(path); 427 } 428 429 #endif 430 431 UString HResultToMessage(HRESULT errorCode); 432 433 static const UInt32 k_ErrorFlagsIds[] = 434 { 435 IDS_EXTRACT_MSG_IS_NOT_ARC, 436 IDS_EXTRACT_MSG_HEADERS_ERROR, 437 IDS_EXTRACT_MSG_HEADERS_ERROR, 438 IDS_OPEN_MSG_UNAVAILABLE_START, 439 IDS_OPEN_MSG_UNCONFIRMED_START, 440 IDS_EXTRACT_MSG_UEXPECTED_END, 441 IDS_EXTRACT_MSG_DATA_AFTER_END, 442 IDS_EXTRACT_MSG_UNSUPPORTED_METHOD, 443 IDS_OPEN_MSG_UNSUPPORTED_FEATURE, 444 IDS_EXTRACT_MSG_DATA_ERROR, 445 IDS_EXTRACT_MSG_CRC_ERROR 446 }; 447 448 static void AddNewLineString(UString &s, const UString &m) 449 { 450 s += m; 451 s.Add_LF(); 452 } 453 454 UString GetOpenArcErrorMessage(UInt32 errorFlags) 455 { 456 UString s; 457 458 for (unsigned i = 0; i < ARRAY_SIZE(k_ErrorFlagsIds); i++) 459 { 460 UInt32 f = ((UInt32)1 << i); 461 if ((errorFlags & f) == 0) 462 continue; 463 UInt32 id = k_ErrorFlagsIds[i]; 464 UString m = LangString(id); 465 if (m.IsEmpty()) 466 continue; 467 if (f == kpv_ErrorFlags_EncryptedHeadersError) 468 { 469 m.AddAscii(" : "); 470 AddLangString(m, IDS_EXTRACT_MSG_WRONG_PSW_GUESS); 471 } 472 if (!s.IsEmpty()) 473 s.Add_LF(); 474 s += m; 475 errorFlags &= ~f; 476 } 477 478 if (errorFlags != 0) 479 { 480 char sz[16]; 481 sz[0] = '0'; 482 sz[1] = 'x'; 483 ConvertUInt32ToHex(errorFlags, sz + 2); 484 if (!s.IsEmpty()) 485 s.Add_LF(); 486 s.AddAscii(sz); 487 } 488 489 return s; 490 } 491 492 static void ErrorInfo_Print(UString &s, const CArcErrorInfo &er) 493 { 494 UInt32 errorFlags = er.GetErrorFlags(); 495 UInt32 warningFlags = er.GetWarningFlags(); 496 497 if (errorFlags != 0) 498 AddNewLineString(s, GetOpenArcErrorMessage(errorFlags)); 499 500 if (!er.ErrorMessage.IsEmpty()) 501 AddNewLineString(s, er.ErrorMessage); 502 503 if (warningFlags != 0) 504 { 505 s += GetNameOfProperty(kpidWarningFlags, L"Warnings"); 506 s.AddAscii(":"); 507 s.Add_LF(); 508 AddNewLineString(s, GetOpenArcErrorMessage(warningFlags)); 509 } 510 511 if (!er.WarningMessage.IsEmpty()) 512 { 513 s += GetNameOfProperty(kpidWarning, L"Warning"); 514 s.AddAscii(": "); 515 s += er.WarningMessage; 516 s.Add_LF(); 517 } 518 } 519 520 static UString GetBracedType(const wchar_t *type) 521 { 522 UString s = L'['; 523 s += type; 524 s += L']'; 525 return s; 526 } 527 528 void OpenResult_GUI(UString &s, const CCodecs *codecs, const CArchiveLink &arcLink, const wchar_t *name, HRESULT result) 529 { 530 FOR_VECTOR (level, arcLink.Arcs) 531 { 532 const CArc &arc = arcLink.Arcs[level]; 533 const CArcErrorInfo &er = arc.ErrorInfo; 534 535 if (!er.IsThereErrorOrWarning() && er.ErrorFormatIndex < 0) 536 continue; 537 538 if (s.IsEmpty()) 539 { 540 s += name; 541 s.Add_LF(); 542 } 543 544 if (level != 0) 545 { 546 AddNewLineString(s, arc.Path); 547 } 548 549 ErrorInfo_Print(s, er); 550 551 if (er.ErrorFormatIndex >= 0) 552 { 553 AddNewLineString(s, GetNameOfProperty(kpidWarning, L"Warning")); 554 if (arc.FormatIndex == er.ErrorFormatIndex) 555 { 556 AddNewLineString(s, LangString(IDS_IS_OPEN_WITH_OFFSET)); 557 } 558 else 559 { 560 AddNewLineString(s, MyFormatNew(IDS_CANT_OPEN_AS_TYPE, GetBracedType(codecs->GetFormatNamePtr(er.ErrorFormatIndex)))); 561 AddNewLineString(s, MyFormatNew(IDS_IS_OPEN_AS_TYPE, GetBracedType(codecs->GetFormatNamePtr(arc.FormatIndex)))); 562 } 563 } 564 } 565 566 if (arcLink.NonOpen_ErrorInfo.ErrorFormatIndex >= 0 || result != S_OK) 567 { 568 s += name; 569 s.Add_LF(); 570 if (!arcLink.Arcs.IsEmpty()) 571 AddNewLineString(s, arcLink.NonOpen_ArcPath); 572 573 if (arcLink.NonOpen_ErrorInfo.ErrorFormatIndex >= 0 || result == S_FALSE) 574 { 575 UINT id = IDS_CANT_OPEN_ARCHIVE; 576 UString param; 577 if (arcLink.PasswordWasAsked) 578 id = IDS_CANT_OPEN_ENCRYPTED_ARCHIVE; 579 else if (arcLink.NonOpen_ErrorInfo.ErrorFormatIndex >= 0) 580 { 581 id = IDS_CANT_OPEN_AS_TYPE; 582 param = GetBracedType(codecs->GetFormatNamePtr(arcLink.NonOpen_ErrorInfo.ErrorFormatIndex)); 583 } 584 UString s2 = MyFormatNew(id, param); 585 s2.Replace(L" ''", L""); 586 s2.Replace(L"''", L""); 587 s += s2; 588 } 589 else 590 s += HResultToMessage(result); 591 592 s.Add_LF(); 593 ErrorInfo_Print(s, arcLink.NonOpen_ErrorInfo); 594 } 595 596 if (!s.IsEmpty() && s.Back() == '\n') 597 s.DeleteBack(); 598 } 599 600 HRESULT CExtractCallbackImp::OpenResult(const CCodecs *codecs, const CArchiveLink &arcLink, const wchar_t *name, HRESULT result) 601 { 602 _currentArchivePath = name; 603 _needWriteArchivePath = true; 604 605 UString s; 606 OpenResult_GUI(s, codecs, arcLink, name, result); 607 if (!s.IsEmpty()) 608 { 609 NumArchiveErrors++; 610 AddError_Message(s); 611 _needWriteArchivePath = false; 612 } 613 614 return S_OK; 615 } 616 617 HRESULT CExtractCallbackImp::ThereAreNoFiles() 618 { 619 return S_OK; 620 } 621 622 void CExtractCallbackImp::Add_ArchiveName_Error() 623 { 624 if (_needWriteArchivePath) 625 { 626 if (!_currentArchivePath.IsEmpty()) 627 AddError_Message(_currentArchivePath); 628 _needWriteArchivePath = false; 629 } 630 } 631 632 HRESULT CExtractCallbackImp::ExtractResult(HRESULT result) 633 { 634 if (result == S_OK) 635 return result; 636 NumArchiveErrors++; 637 if (result == E_ABORT || result == ERROR_DISK_FULL) 638 return result; 639 640 Add_ArchiveName_Error(); 641 if (!_currentFilePath.IsEmpty()) 642 MessageError(_currentFilePath); 643 MessageError(NError::MyFormatMessage(result)); 644 return S_OK; 645 } 646 647 #ifndef _NO_CRYPTO 648 649 HRESULT CExtractCallbackImp::SetPassword(const UString &password) 650 { 651 PasswordIsDefined = true; 652 Password = password; 653 return S_OK; 654 } 655 656 STDMETHODIMP CExtractCallbackImp::CryptoGetTextPassword(BSTR *password) 657 { 658 PasswordWasAsked = true; 659 if (!PasswordIsDefined) 660 { 661 CPasswordDialog dialog; 662 #ifndef _SFX 663 bool showPassword = NExtract::Read_ShowPassword(); 664 dialog.ShowPassword = showPassword; 665 #endif 666 ProgressDialog->WaitCreating(); 667 if (dialog.Create(*ProgressDialog) != IDOK) 668 return E_ABORT; 669 Password = dialog.Password; 670 PasswordIsDefined = true; 671 #ifndef _SFX 672 if (dialog.ShowPassword != showPassword) 673 NExtract::Save_ShowPassword(dialog.ShowPassword); 674 #endif 675 } 676 return StringToBstr(Password, password); 677 } 678 679 #endif 680 681 #ifndef _SFX 682 683 STDMETHODIMP CExtractCallbackImp::AskWrite( 684 const wchar_t *srcPath, Int32 srcIsFolder, 685 const FILETIME *srcTime, const UInt64 *srcSize, 686 const wchar_t *destPath, 687 BSTR *destPathResult, 688 Int32 *writeAnswer) 689 { 690 UString destPathResultTemp = destPath; 691 692 // RINOK(StringToBstr(destPath, destPathResult)); 693 694 *destPathResult = 0; 695 *writeAnswer = BoolToInt(false); 696 697 FString destPathSys = us2fs(destPath); 698 bool srcIsFolderSpec = IntToBool(srcIsFolder); 699 CFileInfo destFileInfo; 700 701 if (destFileInfo.Find(destPathSys)) 702 { 703 if (srcIsFolderSpec) 704 { 705 if (!destFileInfo.IsDir()) 706 { 707 RINOK(MessageError("can not replace file with folder with same name", destPathSys)); 708 return E_ABORT; 709 } 710 *writeAnswer = BoolToInt(false); 711 return S_OK; 712 } 713 714 if (destFileInfo.IsDir()) 715 { 716 RINOK(MessageError("can not replace folder with file with same name", destPathSys)); 717 *writeAnswer = BoolToInt(false); 718 return S_OK; 719 } 720 721 switch (OverwriteMode) 722 { 723 case NExtract::NOverwriteMode::kSkip: 724 return S_OK; 725 case NExtract::NOverwriteMode::kAsk: 726 { 727 Int32 overwriteResult; 728 UString destPathSpec = destPath; 729 int slashPos = destPathSpec.ReverseFind_PathSepar(); 730 destPathSpec.DeleteFrom(slashPos + 1); 731 destPathSpec += fs2us(destFileInfo.Name); 732 733 RINOK(AskOverwrite( 734 destPathSpec, 735 &destFileInfo.MTime, &destFileInfo.Size, 736 srcPath, 737 srcTime, srcSize, 738 &overwriteResult)); 739 740 switch (overwriteResult) 741 { 742 case NOverwriteAnswer::kCancel: return E_ABORT; 743 case NOverwriteAnswer::kNo: return S_OK; 744 case NOverwriteAnswer::kNoToAll: OverwriteMode = NExtract::NOverwriteMode::kSkip; return S_OK; 745 case NOverwriteAnswer::kYes: break; 746 case NOverwriteAnswer::kYesToAll: OverwriteMode = NExtract::NOverwriteMode::kOverwrite; break; 747 case NOverwriteAnswer::kAutoRename: OverwriteMode = NExtract::NOverwriteMode::kRename; break; 748 default: 749 return E_FAIL; 750 } 751 } 752 } 753 754 if (OverwriteMode == NExtract::NOverwriteMode::kRename) 755 { 756 if (!AutoRenamePath(destPathSys)) 757 { 758 RINOK(MessageError("can not create name for file", destPathSys)); 759 return E_ABORT; 760 } 761 destPathResultTemp = fs2us(destPathSys); 762 } 763 else 764 if (!NDir::DeleteFileAlways(destPathSys)) 765 { 766 RINOK(MessageError("can not delete output file", destPathSys)); 767 return E_ABORT; 768 } 769 } 770 *writeAnswer = BoolToInt(true); 771 return StringToBstr(destPathResultTemp, destPathResult); 772 } 773 774 775 STDMETHODIMP CExtractCallbackImp::UseExtractToStream(Int32 *res) 776 { 777 *res = BoolToInt(StreamMode); 778 return S_OK; 779 } 780 781 static HRESULT GetTime(IGetProp *getProp, PROPID propID, FILETIME &ft, bool &ftDefined) 782 { 783 ftDefined = false; 784 NCOM::CPropVariant prop; 785 RINOK(getProp->GetProp(propID, &prop)); 786 if (prop.vt == VT_FILETIME) 787 { 788 ft = prop.filetime; 789 ftDefined = (ft.dwHighDateTime != 0 || ft.dwLowDateTime != 0); 790 } 791 else if (prop.vt != VT_EMPTY) 792 return E_FAIL; 793 return S_OK; 794 } 795 796 797 static HRESULT GetItemBoolProp(IGetProp *getProp, PROPID propID, bool &result) 798 { 799 NCOM::CPropVariant prop; 800 result = false; 801 RINOK(getProp->GetProp(propID, &prop)); 802 if (prop.vt == VT_BOOL) 803 result = VARIANT_BOOLToBool(prop.boolVal); 804 else if (prop.vt != VT_EMPTY) 805 return E_FAIL; 806 return S_OK; 807 } 808 809 810 STDMETHODIMP CExtractCallbackImp::GetStream7(const wchar_t *name, 811 Int32 isDir, 812 ISequentialOutStream **outStream, Int32 askExtractMode, 813 IGetProp *getProp) 814 { 815 COM_TRY_BEGIN 816 *outStream = 0; 817 _newVirtFileWasAdded = false; 818 _hashStreamWasUsed = false; 819 _needUpdateStat = false; 820 821 if (_hashStream) 822 _hashStreamSpec->ReleaseStream(); 823 824 GetItemBoolProp(getProp, kpidIsAltStream, _isAltStream); 825 826 if (!ProcessAltStreams && _isAltStream) 827 return S_OK; 828 829 _filePath = name; 830 _isFolder = IntToBool(isDir); 831 _curSize = 0; 832 _curSizeDefined = false; 833 834 UInt64 size = 0; 835 bool sizeDefined; 836 { 837 NCOM::CPropVariant prop; 838 RINOK(getProp->GetProp(kpidSize, &prop)); 839 sizeDefined = ConvertPropVariantToUInt64(prop, size); 840 } 841 842 if (sizeDefined) 843 { 844 _curSize = size; 845 _curSizeDefined = true; 846 } 847 848 if (askExtractMode != NArchive::NExtract::NAskMode::kExtract && 849 askExtractMode != NArchive::NExtract::NAskMode::kTest) 850 return S_OK; 851 852 _needUpdateStat = true; 853 854 CMyComPtr<ISequentialOutStream> outStreamLoc; 855 856 if (VirtFileSystem && askExtractMode == NArchive::NExtract::NAskMode::kExtract) 857 { 858 CVirtFile &file = VirtFileSystemSpec->AddNewFile(); 859 _newVirtFileWasAdded = true; 860 file.Name = name; 861 file.IsDir = IntToBool(isDir); 862 file.IsAltStream = _isAltStream; 863 file.Size = 0; 864 865 RINOK(GetTime(getProp, kpidCTime, file.CTime, file.CTimeDefined)); 866 RINOK(GetTime(getProp, kpidATime, file.ATime, file.ATimeDefined)); 867 RINOK(GetTime(getProp, kpidMTime, file.MTime, file.MTimeDefined)); 868 869 NCOM::CPropVariant prop; 870 RINOK(getProp->GetProp(kpidAttrib, &prop)); 871 if (prop.vt == VT_UI4) 872 { 873 file.Attrib = prop.ulVal; 874 file.AttribDefined = true; 875 } 876 // else if (isDir) file.Attrib = FILE_ATTRIBUTE_DIRECTORY; 877 878 file.ExpectedSize = 0; 879 if (sizeDefined) 880 file.ExpectedSize = size; 881 outStreamLoc = VirtFileSystem; 882 } 883 884 if (_hashStream) 885 { 886 { 887 _hashStreamSpec->SetStream(outStreamLoc); 888 outStreamLoc = _hashStream; 889 _hashStreamSpec->Init(true); 890 _hashStreamWasUsed = true; 891 } 892 } 893 894 if (outStreamLoc) 895 *outStream = outStreamLoc.Detach(); 896 return S_OK; 897 COM_TRY_END 898 } 899 900 STDMETHODIMP CExtractCallbackImp::PrepareOperation7(Int32 askExtractMode) 901 { 902 COM_TRY_BEGIN 903 _needUpdateStat = ( 904 askExtractMode == NArchive::NExtract::NAskMode::kExtract || 905 askExtractMode == NArchive::NExtract::NAskMode::kTest); 906 907 /* 908 _extractMode = false; 909 switch (askExtractMode) 910 { 911 case NArchive::NExtract::NAskMode::kExtract: 912 if (_testMode) 913 askExtractMode = NArchive::NExtract::NAskMode::kTest; 914 else 915 _extractMode = true; 916 break; 917 }; 918 */ 919 return SetCurrentFilePath2(_filePath); 920 COM_TRY_END 921 } 922 923 STDMETHODIMP CExtractCallbackImp::SetOperationResult7(Int32 opRes, Int32 encrypted) 924 { 925 COM_TRY_BEGIN 926 if (VirtFileSystem && _newVirtFileWasAdded) 927 { 928 // FIXME: probably we must request file size from VirtFileSystem 929 // _curSize = VirtFileSystem->GetLastFileSize() 930 // _curSizeDefined = true; 931 RINOK(VirtFileSystemSpec->CloseMemFile()); 932 } 933 if (_hashStream && _hashStreamWasUsed) 934 { 935 _hashStreamSpec->_hash->Final(_isFolder, _isAltStream, _filePath); 936 _curSize = _hashStreamSpec->GetSize(); 937 _curSizeDefined = true; 938 _hashStreamSpec->ReleaseStream(); 939 _hashStreamWasUsed = false; 940 } 941 else if (_hashCalc && _needUpdateStat) 942 { 943 _hashCalc->SetSize(_curSize); 944 _hashCalc->Final(_isFolder, _isAltStream, _filePath); 945 } 946 return SetOperationResult(opRes, encrypted); 947 COM_TRY_END 948 } 949 950 951 static const size_t k_SizeT_MAX = (size_t)((size_t)0 - 1); 952 953 static const UInt32 kBlockSize = ((UInt32)1 << 31); 954 955 STDMETHODIMP CVirtFileSystem::Write(const void *data, UInt32 size, UInt32 *processedSize) 956 { 957 if (processedSize) 958 *processedSize = 0; 959 if (size == 0) 960 return S_OK; 961 if (!_fileMode) 962 { 963 CVirtFile &file = Files.Back(); 964 size_t rem = file.Data.Size() - (size_t)file.Size; 965 bool useMem = true; 966 if (rem < size) 967 { 968 UInt64 b = 0; 969 if (file.Data.Size() == 0) 970 b = file.ExpectedSize; 971 UInt64 a = file.Size + size; 972 if (b < a) 973 b = a; 974 a = (UInt64)file.Data.Size() * 2; 975 if (b < a) 976 b = a; 977 useMem = false; 978 if (b <= k_SizeT_MAX && b <= MaxTotalAllocSize) 979 useMem = file.Data.ReAlloc_KeepData((size_t)b, (size_t)file.Size); 980 } 981 if (useMem) 982 { 983 memcpy(file.Data + file.Size, data, size); 984 file.Size += size; 985 if (processedSize) 986 *processedSize = (UInt32)size; 987 return S_OK; 988 } 989 _fileMode = true; 990 } 991 RINOK(FlushToDisk(false)); 992 return _outFileStream->Write(data, size, processedSize); 993 } 994 995 HRESULT CVirtFileSystem::FlushToDisk(bool closeLast) 996 { 997 if (!_outFileStream) 998 { 999 _outFileStreamSpec = new COutFileStream; 1000 _outFileStream = _outFileStreamSpec; 1001 } 1002 while (_numFlushed < Files.Size()) 1003 { 1004 const CVirtFile &file = Files[_numFlushed]; 1005 const FString path = DirPrefix + us2fs(Get_Correct_FsFile_Name(file.Name)); 1006 if (!_fileIsOpen) 1007 { 1008 if (!_outFileStreamSpec->Create(path, false)) 1009 { 1010 _outFileStream.Release(); 1011 return E_FAIL; 1012 // MessageBoxMyError(UString(L"Can't create file ") + fs2us(tempFilePath)); 1013 } 1014 _fileIsOpen = true; 1015 RINOK(WriteStream(_outFileStream, file.Data, (size_t)file.Size)); 1016 } 1017 if (_numFlushed == Files.Size() - 1 && !closeLast) 1018 break; 1019 if (file.CTimeDefined || 1020 file.ATimeDefined || 1021 file.MTimeDefined) 1022 _outFileStreamSpec->SetTime( 1023 file.CTimeDefined ? &file.CTime : NULL, 1024 file.ATimeDefined ? &file.ATime : NULL, 1025 file.MTimeDefined ? &file.MTime : NULL); 1026 _outFileStreamSpec->Close(); 1027 _numFlushed++; 1028 _fileIsOpen = false; 1029 if (file.AttribDefined) 1030 NDir::SetFileAttrib(path, file.Attrib); 1031 } 1032 return S_OK; 1033 } 1034 1035 #endif 1036