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