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
26 using namespace NWindows;
27 using namespace NFile;
28 using namespace NDir;
29
CheckBreak2()30 static HRESULT CheckBreak2()
31 {
32 return NConsoleClose::TestBreakSignal() ? E_ABORT : S_OK;
33 }
34
35 static const char *kError = "ERROR: ";
36
37
StartScanning()38 void CExtractScanConsole::StartScanning()
39 {
40 if (NeedPercents())
41 _percent.Command = "Scan";
42 }
43
ScanProgress(const CDirItemsStat & st,const FString & path,bool)44 HRESULT 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
ScanError(const FString & path,DWORD systemError)57 HRESULT 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
Print_UInt64_and_String(AString & s,UInt64 val,const char * name)71 void 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
PrintSize_bytes_Smart(AString & s,UInt64 val)80 void 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
Print_DirItemsStat(AString & s,const CDirItemsStat & st)98 void 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
PrintStat(const CDirItemsStat & st)117 void 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
134 static NSynchronization::CCriticalSection g_CriticalSection;
135 #define MT_LOCK NSynchronization::CCriticalSectionLock lock(g_CriticalSection);
136 #else
137 #define MT_LOCK
138 #endif
139
140
141 static const char *kTestString = "T";
142 static const char *kExtractString = "-";
143 static 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
149 static const char *kMemoryExceptionMessage = "Can't allocate required memory!";
150
151 static const char *kExtracting = "Extracting archive: ";
152 static const char *kTesting = "Testing archive: ";
153
154 static const char *kEverythingIsOk = "Everything is Ok";
155 static const char *kNoFiles = "No files to process";
156
157 static const char *kUnsupportedMethod = "Unsupported Method";
158 static const char *kCrcFailed = "CRC Failed";
159 static const char *kCrcFailedEncrypted = "CRC Failed in encrypted file. Wrong password?";
160 static const char *kDataError = "Data Error";
161 static const char *kDataErrorEncrypted = "Data Error in encrypted file. Wrong password?";
162 static const char *kUnavailableData = "Unavailable data";
163 static const char *kUnexpectedEnd = "Unexpected end of data";
164 static const char *kDataAfterEnd = "There are some data after the end of the payload data";
165 static const char *kIsNotArc = "Is not archive";
166 static const char *kHeadersError = "Headers Error";
167 static const char *kWrongPassword = "Wrong password";
168
169 static 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
SetTotal(UInt64 size)184 STDMETHODIMP 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
SetCompleted(const UInt64 * completeValue)196 STDMETHODIMP 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
209 static const char *kTab = " ";
210
PrintFileInfo(CStdOutStream * _so,const wchar_t * path,const FILETIME * ft,const UInt64 * size)211 static 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
AskOverwrite(const wchar_t * existName,const FILETIME * existTime,const UInt64 * existSize,const wchar_t * newName,const FILETIME * newTime,const UInt64 * newSize,Int32 * answer)230 STDMETHODIMP 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
PrepareOperation(const wchar_t * name,Int32,Int32 askExtractMode,const UInt64 * position)272 STDMETHODIMP 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
MessageError(const wchar_t * message)331 STDMETHODIMP 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
SetExtractErrorMessage(Int32 opRes,Int32 encrypted,AString & dest)350 void 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
SetOperationResult(Int32 opRes,Int32 encrypted)398 STDMETHODIMP 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
ReportExtractResult(Int32 opRes,Int32 encrypted,const wchar_t * name)434 STDMETHODIMP 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
SetPassword(const UString & password)449 HRESULT CExtractCallbackConsole::SetPassword(const UString &password)
450 {
451 PasswordIsDefined = true;
452 Password = password;
453 return S_OK;
454 }
455
CryptoGetTextPassword(BSTR * password)456 STDMETHODIMP 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
BeforeOpen(const wchar_t * name,bool testMode)466 HRESULT 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
484 HRESULT Print_OpenArchive_Props(CStdOutStream &so, const CCodecs *codecs, const CArchiveLink &arcLink);
485 HRESULT Print_OpenArchive_Error(CStdOutStream &so, const CCodecs *codecs, const CArchiveLink &arcLink);
486
GetOpenArcErrorMessage(UInt32 errorFlags)487 static 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
PrintErrorFlags(CStdOutStream & so,const char * s,UInt32 errorFlags)517 void PrintErrorFlags(CStdOutStream &so, const char *s, UInt32 errorFlags)
518 {
519 if (errorFlags == 0)
520 return;
521 so << s << endl << GetOpenArcErrorMessage(errorFlags) << endl;
522 }
523
Add_Messsage_Pre_ArcType(UString & s,const char * pre,const wchar_t * arcType)524 void 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
Print_ErrorFormatIndex_Warning(CStdOutStream * _so,const CCodecs * codecs,const CArc & arc)533 void 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
OpenResult(const CCodecs * codecs,const CArchiveLink & arcLink,const wchar_t * name,HRESULT result)554 HRESULT 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
ThereAreNoFiles()694 HRESULT 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
ExtractResult(HRESULT result)707 HRESULT 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