1// ExtractCallbackConsole.cpp 2 3#include "StdAfx.h" 4 5#include "../../../Common/IntToString.h" 6#include "../../../Common/Wildcard.h" 7 8#include "../../../Windows/FileDir.h" 9#include "../../../Windows/FileFind.h" 10#include "../../../Windows/TimeUtils.h" 11#include "../../../Windows/ErrorMsg.h" 12#include "../../../Windows/PropVariantConv.h" 13 14#ifndef _7ZIP_ST 15#include "../../../Windows/Synchronization.h" 16#endif 17 18#include "../../Common/FilePathAutoRename.h" 19 20#include "../Common/ExtractingFilePath.h" 21 22#include "ConsoleClose.h" 23#include "ExtractCallbackConsole.h" 24#include "UserInputUtils.h" 25 26using namespace NWindows; 27using namespace NFile; 28using namespace NDir; 29 30static HRESULT CheckBreak2() 31{ 32 return NConsoleClose::TestBreakSignal() ? E_ABORT : S_OK; 33} 34 35static const char *kError = "ERROR: "; 36 37 38void CExtractScanConsole::StartScanning() 39{ 40 if (NeedPercents()) 41 _percent.Command = "Scan"; 42} 43 44HRESULT CExtractScanConsole::ScanProgress(const CDirItemsStat &st, const FString &path, bool /* isDir */) 45{ 46 if (NeedPercents()) 47 { 48 _percent.Files = st.NumDirs + st.NumFiles; 49 _percent.Completed = st.GetTotalBytes(); 50 _percent.FileName = fs2us(path); 51 _percent.Print(); 52 } 53 54 return CheckBreak2(); 55} 56 57HRESULT CExtractScanConsole::ScanError(const FString &path, DWORD systemError) 58{ 59 ClosePercentsAndFlush(); 60 61 if (_se) 62 { 63 *_se << endl << kError << NError::MyFormatMessage(systemError) << endl << 64 fs2us(path) << endl << endl; 65 _se->Flush(); 66 } 67 return HRESULT_FROM_WIN32(systemError); 68} 69 70 71void Print_UInt64_and_String(AString &s, UInt64 val, const char *name) 72{ 73 char temp[32]; 74 ConvertUInt64ToString(val, temp); 75 s += temp; 76 s.Add_Space(); 77 s += name; 78} 79 80void PrintSize_bytes_Smart(AString &s, UInt64 val) 81{ 82 Print_UInt64_and_String(s, val, "bytes"); 83 84 if (val == 0) 85 return; 86 87 unsigned numBits = 10; 88 char c = 'K'; 89 char temp[4] = { 'K', 'i', 'B', 0 }; 90 if (val >= ((UInt64)10 << 30)) { numBits = 30; c = 'G'; } 91 else if (val >= ((UInt64)10 << 20)) { numBits = 20; c = 'M'; } 92 temp[0] = c; 93 s += " ("; 94 Print_UInt64_and_String(s, ((val + ((UInt64)1 << numBits) - 1) >> numBits), temp); 95 s += ')'; 96} 97 98void Print_DirItemsStat(AString &s, const CDirItemsStat &st) 99{ 100 if (st.NumDirs != 0) 101 { 102 Print_UInt64_and_String(s, st.NumDirs, st.NumDirs == 1 ? "folder" : "folders"); 103 s += ", "; 104 } 105 Print_UInt64_and_String(s, st.NumFiles, st.NumFiles == 1 ? "file" : "files"); 106 s += ", "; 107 PrintSize_bytes_Smart(s, st.FilesSize); 108 if (st.NumAltStreams != 0) 109 { 110 s.Add_LF(); 111 Print_UInt64_and_String(s, st.NumAltStreams, "alternate streams"); 112 s += ", "; 113 PrintSize_bytes_Smart(s, st.AltStreamsSize); 114 } 115} 116 117void CExtractScanConsole::PrintStat(const CDirItemsStat &st) 118{ 119 if (_so) 120 { 121 AString s; 122 Print_DirItemsStat(s, st); 123 *_so << s << endl; 124 } 125} 126 127 128 129 130 131 132 133#ifndef _7ZIP_ST 134static NSynchronization::CCriticalSection g_CriticalSection; 135#define MT_LOCK NSynchronization::CCriticalSectionLock lock(g_CriticalSection); 136#else 137#define MT_LOCK 138#endif 139 140 141static const char *kTestString = "T"; 142static const char *kExtractString = "-"; 143static const char *kSkipString = "."; 144 145// static const char *kCantAutoRename = "can not create file with auto name\n"; 146// static const char *kCantRenameFile = "can not rename existing file\n"; 147// static const char *kCantDeleteOutputFile = "can not delete output file "; 148 149static const char *kMemoryExceptionMessage = "Can't allocate required memory!"; 150 151static const char *kExtracting = "Extracting archive: "; 152static const char *kTesting = "Testing archive: "; 153 154static const char *kEverythingIsOk = "Everything is Ok"; 155static const char *kNoFiles = "No files to process"; 156 157static const char *kUnsupportedMethod = "Unsupported Method"; 158static const char *kCrcFailed = "CRC Failed"; 159static const char *kCrcFailedEncrypted = "CRC Failed in encrypted file. Wrong password?"; 160static const char *kDataError = "Data Error"; 161static const char *kDataErrorEncrypted = "Data Error in encrypted file. Wrong password?"; 162static const char *kUnavailableData = "Unavailable data"; 163static const char *kUnexpectedEnd = "Unexpected end of data"; 164static const char *kDataAfterEnd = "There are some data after the end of the payload data"; 165static const char *kIsNotArc = "Is not archive"; 166static const char *kHeadersError = "Headers Error"; 167static const char *kWrongPassword = "Wrong password"; 168 169static const char * const k_ErrorFlagsMessages[] = 170{ 171 "Is not archive" 172 , "Headers Error" 173 , "Headers Error in encrypted archive. Wrong password?" 174 , "Unavailable start of archive" 175 , "Unconfirmed start of archive" 176 , "Unexpected end of archive" 177 , "There are data after the end of archive" 178 , "Unsupported method" 179 , "Unsupported feature" 180 , "Data Error" 181 , "CRC Error" 182}; 183 184STDMETHODIMP CExtractCallbackConsole::SetTotal(UInt64 size) 185{ 186 MT_LOCK 187 188 if (NeedPercents()) 189 { 190 _percent.Total = size; 191 _percent.Print(); 192 } 193 return CheckBreak2(); 194} 195 196STDMETHODIMP CExtractCallbackConsole::SetCompleted(const UInt64 *completeValue) 197{ 198 MT_LOCK 199 200 if (NeedPercents()) 201 { 202 if (completeValue) 203 _percent.Completed = *completeValue; 204 _percent.Print(); 205 } 206 return CheckBreak2(); 207} 208 209static const char *kTab = " "; 210 211static void PrintFileInfo(CStdOutStream *_so, const wchar_t *path, const FILETIME *ft, const UInt64 *size) 212{ 213 *_so << kTab << "Path: " << path << endl; 214 if (size) 215 { 216 AString s; 217 PrintSize_bytes_Smart(s, *size); 218 *_so << kTab << "Size: " << s << endl; 219 } 220 if (ft) 221 { 222 char temp[64]; 223 FILETIME locTime; 224 if (FileTimeToLocalFileTime(ft, &locTime)) 225 if (ConvertFileTimeToString(locTime, temp, true, true)) 226 *_so << kTab << "Modified: " << temp << endl; 227 } 228} 229 230STDMETHODIMP CExtractCallbackConsole::AskOverwrite( 231 const wchar_t *existName, const FILETIME *existTime, const UInt64 *existSize, 232 const wchar_t *newName, const FILETIME *newTime, const UInt64 *newSize, 233 Int32 *answer) 234{ 235 MT_LOCK 236 237 RINOK(CheckBreak2()); 238 239 ClosePercentsAndFlush(); 240 241 if (_so) 242 { 243 *_so << endl << "Would you like to replace the existing file:\n"; 244 PrintFileInfo(_so, existName, existTime, existSize); 245 *_so << "with the file from archive:\n"; 246 PrintFileInfo(_so, newName, newTime, newSize); 247 } 248 249 NUserAnswerMode::EEnum overwriteAnswer = ScanUserYesNoAllQuit(_so); 250 251 switch (overwriteAnswer) 252 { 253 case NUserAnswerMode::kQuit: return E_ABORT; 254 case NUserAnswerMode::kNo: *answer = NOverwriteAnswer::kNo; break; 255 case NUserAnswerMode::kNoAll: *answer = NOverwriteAnswer::kNoToAll; break; 256 case NUserAnswerMode::kYesAll: *answer = NOverwriteAnswer::kYesToAll; break; 257 case NUserAnswerMode::kYes: *answer = NOverwriteAnswer::kYes; break; 258 case NUserAnswerMode::kAutoRenameAll: *answer = NOverwriteAnswer::kAutoRename; break; 259 default: return E_FAIL; 260 } 261 262 if (_so) 263 { 264 *_so << endl; 265 if (NeedFlush) 266 _so->Flush(); 267 } 268 269 return CheckBreak2(); 270} 271 272STDMETHODIMP CExtractCallbackConsole::PrepareOperation(const wchar_t *name, Int32 /* isFolder */, Int32 askExtractMode, const UInt64 *position) 273{ 274 MT_LOCK 275 276 _currentName = name; 277 278 const char *s; 279 unsigned requiredLevel = 1; 280 281 switch (askExtractMode) 282 { 283 case NArchive::NExtract::NAskMode::kExtract: s = kExtractString; break; 284 case NArchive::NExtract::NAskMode::kTest: s = kTestString; break; 285 case NArchive::NExtract::NAskMode::kSkip: s = kSkipString; requiredLevel = 2; break; 286 default: s = "???"; requiredLevel = 2; 287 }; 288 289 bool show2 = (LogLevel >= requiredLevel && _so); 290 291 if (show2) 292 { 293 ClosePercents_for_so(); 294 295 _tempA = s; 296 if (name) 297 _tempA.Add_Space(); 298 *_so << _tempA; 299 300 _tempU.Empty(); 301 if (name) 302 _tempU = name; 303 _so->PrintUString(_tempU, _tempA); 304 if (position) 305 *_so << " <" << *position << ">"; 306 *_so << endl; 307 308 if (NeedFlush) 309 _so->Flush(); 310 } 311 312 if (NeedPercents()) 313 { 314 if (PercentsNameLevel >= 1) 315 { 316 _percent.FileName.Empty(); 317 _percent.Command.Empty(); 318 if (PercentsNameLevel > 1 || !show2) 319 { 320 _percent.Command = s; 321 if (name) 322 _percent.FileName = name; 323 } 324 } 325 _percent.Print(); 326 } 327 328 return CheckBreak2(); 329} 330 331STDMETHODIMP CExtractCallbackConsole::MessageError(const wchar_t *message) 332{ 333 MT_LOCK 334 335 RINOK(CheckBreak2()); 336 337 NumFileErrors_in_Current++; 338 NumFileErrors++; 339 340 ClosePercentsAndFlush(); 341 if (_se) 342 { 343 *_se << kError << message << endl; 344 _se->Flush(); 345 } 346 347 return CheckBreak2(); 348} 349 350void SetExtractErrorMessage(Int32 opRes, Int32 encrypted, AString &dest) 351{ 352 dest.Empty(); 353 const char *s = NULL; 354 355 switch (opRes) 356 { 357 case NArchive::NExtract::NOperationResult::kUnsupportedMethod: 358 s = kUnsupportedMethod; 359 break; 360 case NArchive::NExtract::NOperationResult::kCRCError: 361 s = (encrypted ? kCrcFailedEncrypted : kCrcFailed); 362 break; 363 case NArchive::NExtract::NOperationResult::kDataError: 364 s = (encrypted ? kDataErrorEncrypted : kDataError); 365 break; 366 case NArchive::NExtract::NOperationResult::kUnavailable: 367 s = kUnavailableData; 368 break; 369 case NArchive::NExtract::NOperationResult::kUnexpectedEnd: 370 s = kUnexpectedEnd; 371 break; 372 case NArchive::NExtract::NOperationResult::kDataAfterEnd: 373 s = kDataAfterEnd; 374 break; 375 case NArchive::NExtract::NOperationResult::kIsNotArc: 376 s = kIsNotArc; 377 break; 378 case NArchive::NExtract::NOperationResult::kHeadersError: 379 s = kHeadersError; 380 break; 381 case NArchive::NExtract::NOperationResult::kWrongPassword: 382 s = kWrongPassword; 383 break; 384 } 385 386 dest += kError; 387 if (s) 388 dest += s; 389 else 390 { 391 char temp[16]; 392 ConvertUInt32ToString(opRes, temp); 393 dest += "Error #"; 394 dest += temp; 395 } 396} 397 398STDMETHODIMP CExtractCallbackConsole::SetOperationResult(Int32 opRes, Int32 encrypted) 399{ 400 MT_LOCK 401 402 if (opRes == NArchive::NExtract::NOperationResult::kOK) 403 { 404 if (NeedPercents()) 405 { 406 _percent.Command.Empty(); 407 _percent.FileName.Empty(); 408 _percent.Files++; 409 } 410 } 411 else 412 { 413 NumFileErrors_in_Current++; 414 NumFileErrors++; 415 416 if (_se) 417 { 418 ClosePercentsAndFlush(); 419 420 AString s; 421 SetExtractErrorMessage(opRes, encrypted, s); 422 423 *_se << s; 424 if (!_currentName.IsEmpty()) 425 *_se << " : " << _currentName; 426 *_se << endl; 427 _se->Flush(); 428 } 429 } 430 431 return CheckBreak2(); 432} 433 434STDMETHODIMP CExtractCallbackConsole::ReportExtractResult(Int32 opRes, Int32 encrypted, const wchar_t *name) 435{ 436 if (opRes != NArchive::NExtract::NOperationResult::kOK) 437 { 438 _currentName = name; 439 return SetOperationResult(opRes, encrypted); 440 } 441 442 return CheckBreak2(); 443} 444 445 446 447#ifndef _NO_CRYPTO 448 449HRESULT CExtractCallbackConsole::SetPassword(const UString &password) 450{ 451 PasswordIsDefined = true; 452 Password = password; 453 return S_OK; 454} 455 456STDMETHODIMP CExtractCallbackConsole::CryptoGetTextPassword(BSTR *password) 457{ 458 COM_TRY_BEGIN 459 MT_LOCK 460 return Open_CryptoGetTextPassword(password); 461 COM_TRY_END 462} 463 464#endif 465 466HRESULT CExtractCallbackConsole::BeforeOpen(const wchar_t *name, bool testMode) 467{ 468 RINOK(CheckBreak2()); 469 470 NumTryArcs++; 471 ThereIsError_in_Current = false; 472 ThereIsWarning_in_Current = false; 473 NumFileErrors_in_Current = 0; 474 475 ClosePercents_for_so(); 476 if (_so) 477 *_so << endl << (testMode ? kTesting : kExtracting) << name << endl; 478 479 if (NeedPercents()) 480 _percent.Command = "Open"; 481 return S_OK; 482} 483 484HRESULT Print_OpenArchive_Props(CStdOutStream &so, const CCodecs *codecs, const CArchiveLink &arcLink); 485HRESULT Print_OpenArchive_Error(CStdOutStream &so, const CCodecs *codecs, const CArchiveLink &arcLink); 486 487static AString GetOpenArcErrorMessage(UInt32 errorFlags) 488{ 489 AString s; 490 491 for (unsigned i = 0; i < ARRAY_SIZE(k_ErrorFlagsMessages); i++) 492 { 493 UInt32 f = (1 << i); 494 if ((errorFlags & f) == 0) 495 continue; 496 const char *m = k_ErrorFlagsMessages[i]; 497 if (!s.IsEmpty()) 498 s.Add_LF(); 499 s += m; 500 errorFlags &= ~f; 501 } 502 503 if (errorFlags != 0) 504 { 505 char sz[16]; 506 sz[0] = '0'; 507 sz[1] = 'x'; 508 ConvertUInt32ToHex(errorFlags, sz + 2); 509 if (!s.IsEmpty()) 510 s.Add_LF(); 511 s += sz; 512 } 513 514 return s; 515} 516 517void PrintErrorFlags(CStdOutStream &so, const char *s, UInt32 errorFlags) 518{ 519 if (errorFlags == 0) 520 return; 521 so << s << endl << GetOpenArcErrorMessage(errorFlags) << endl; 522} 523 524void Add_Messsage_Pre_ArcType(UString &s, const char *pre, const wchar_t *arcType) 525{ 526 s.Add_LF(); 527 s.AddAscii(pre); 528 s.AddAscii(" as ["); 529 s += arcType; 530 s.AddAscii("] archive"); 531} 532 533void Print_ErrorFormatIndex_Warning(CStdOutStream *_so, const CCodecs *codecs, const CArc &arc) 534{ 535 const CArcErrorInfo &er = arc.ErrorInfo; 536 537 UString s = L"WARNING:\n"; 538 s += arc.Path; 539 if (arc.FormatIndex == er.ErrorFormatIndex) 540 { 541 s.Add_LF(); 542 s.AddAscii("The archive is open with offset"); 543 } 544 else 545 { 546 Add_Messsage_Pre_ArcType(s, "Can not open the file", codecs->GetFormatNamePtr(er.ErrorFormatIndex)); 547 Add_Messsage_Pre_ArcType(s, "The file is open", codecs->GetFormatNamePtr(arc.FormatIndex)); 548 } 549 550 *_so << s << endl << endl; 551} 552 553 554HRESULT CExtractCallbackConsole::OpenResult( 555 const CCodecs *codecs, const CArchiveLink &arcLink, 556 const wchar_t *name, HRESULT result) 557{ 558 ClosePercents(); 559 560 if (NeedPercents()) 561 { 562 _percent.Files = 0; 563 _percent.Command.Empty(); 564 _percent.FileName.Empty(); 565 } 566 567 568 ClosePercentsAndFlush(); 569 570 FOR_VECTOR (level, arcLink.Arcs) 571 { 572 const CArc &arc = arcLink.Arcs[level]; 573 const CArcErrorInfo &er = arc.ErrorInfo; 574 575 UInt32 errorFlags = er.GetErrorFlags(); 576 577 if (errorFlags != 0 || !er.ErrorMessage.IsEmpty()) 578 { 579 if (_se) 580 { 581 *_se << endl; 582 if (level != 0) 583 *_se << arc.Path << endl; 584 } 585 586 if (errorFlags != 0) 587 { 588 if (_se) 589 PrintErrorFlags(*_se, "ERRORS:", errorFlags); 590 NumOpenArcErrors++; 591 ThereIsError_in_Current = true; 592 } 593 594 if (!er.ErrorMessage.IsEmpty()) 595 { 596 if (_se) 597 *_se << "ERRORS:" << endl << er.ErrorMessage << endl; 598 NumOpenArcErrors++; 599 ThereIsError_in_Current = true; 600 } 601 602 if (_se) 603 { 604 *_se << endl; 605 _se->Flush(); 606 } 607 } 608 609 UInt32 warningFlags = er.GetWarningFlags(); 610 611 if (warningFlags != 0 || !er.WarningMessage.IsEmpty()) 612 { 613 if (_so) 614 { 615 *_so << endl; 616 if (level != 0) 617 *_so << arc.Path << endl; 618 } 619 620 if (warningFlags != 0) 621 { 622 if (_so) 623 PrintErrorFlags(*_so, "WARNINGS:", warningFlags); 624 NumOpenArcWarnings++; 625 ThereIsWarning_in_Current = true; 626 } 627 628 if (!er.WarningMessage.IsEmpty()) 629 { 630 if (_so) 631 *_so << "WARNINGS:" << endl << er.WarningMessage << endl; 632 NumOpenArcWarnings++; 633 ThereIsWarning_in_Current = true; 634 } 635 636 if (_so) 637 { 638 *_so << endl; 639 if (NeedFlush) 640 _so->Flush(); 641 } 642 } 643 644 645 if (er.ErrorFormatIndex >= 0) 646 { 647 if (_so) 648 { 649 Print_ErrorFormatIndex_Warning(_so, codecs, arc); 650 if (NeedFlush) 651 _so->Flush(); 652 } 653 ThereIsWarning_in_Current = true; 654 } 655 } 656 657 if (result == S_OK) 658 { 659 if (_so) 660 { 661 RINOK(Print_OpenArchive_Props(*_so, codecs, arcLink)); 662 *_so << endl; 663 } 664 } 665 else 666 { 667 NumCantOpenArcs++; 668 if (_so) 669 _so->Flush(); 670 if (_se) 671 { 672 *_se << kError << name << endl; 673 HRESULT res = Print_OpenArchive_Error(*_se, codecs, arcLink); 674 RINOK(res); 675 if (result == S_FALSE) 676 { 677 } 678 else 679 { 680 if (result == E_OUTOFMEMORY) 681 *_se << "Can't allocate required memory"; 682 else 683 *_se << NError::MyFormatMessage(result); 684 *_se << endl; 685 } 686 _se->Flush(); 687 } 688 } 689 690 691 return CheckBreak2(); 692} 693 694HRESULT CExtractCallbackConsole::ThereAreNoFiles() 695{ 696 ClosePercents_for_so(); 697 698 if (_so) 699 { 700 *_so << endl << kNoFiles << endl; 701 if (NeedFlush) 702 _so->Flush(); 703 } 704 return CheckBreak2(); 705} 706 707HRESULT CExtractCallbackConsole::ExtractResult(HRESULT result) 708{ 709 MT_LOCK 710 711 if (NeedPercents()) 712 { 713 _percent.ClosePrint(true); 714 _percent.Command.Empty(); 715 _percent.FileName.Empty(); 716 } 717 718 if (_so) 719 _so->Flush(); 720 721 if (result == S_OK) 722 { 723 if (NumFileErrors_in_Current == 0 && !ThereIsError_in_Current) 724 { 725 if (ThereIsWarning_in_Current) 726 NumArcsWithWarnings++; 727 else 728 NumOkArcs++; 729 if (_so) 730 *_so << kEverythingIsOk << endl; 731 } 732 else 733 { 734 NumArcsWithError++; 735 if (_so) 736 { 737 *_so << endl; 738 if (NumFileErrors_in_Current != 0) 739 *_so << "Sub items Errors: " << NumFileErrors_in_Current << endl; 740 } 741 } 742 if (_so && NeedFlush) 743 _so->Flush(); 744 } 745 else 746 { 747 NumArcsWithError++; 748 if (result == E_ABORT || result == ERROR_DISK_FULL) 749 return result; 750 751 if (_se) 752 { 753 *_se << endl << kError; 754 if (result == E_OUTOFMEMORY) 755 *_se << kMemoryExceptionMessage; 756 else 757 *_se << NError::MyFormatMessage(result); 758 *_se << endl; 759 _se->Flush(); 760 } 761 } 762 763 return CheckBreak2(); 764} 765