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 
~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 
48   NumArchiveErrors = 0;
49   ThereAreMessageErrors = false;
50   #ifndef _SFX
51   NumFolders = NumFiles = 0;
52   NeedAddFile = false;
53   #endif
54 }
55 
AddError_Message(LPCWSTR s)56 void CExtractCallbackImp::AddError_Message(LPCWSTR s)
57 {
58   ThereAreMessageErrors = true;
59   ProgressDialog->Sync.AddError_Message(s);
60 }
61 
62 #ifndef _SFX
63 
SetNumFiles(UInt64 numFiles)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 
SetTotal(UInt64 total)78 STDMETHODIMP CExtractCallbackImp::SetTotal(UInt64 total)
79 {
80   ProgressDialog->Sync.Set_NumBytesTotal(total);
81   return S_OK;
82 }
83 
SetCompleted(const UInt64 * value)84 STDMETHODIMP CExtractCallbackImp::SetCompleted(const UInt64 *value)
85 {
86   return ProgressDialog->Sync.Set_NumBytesCur(value);
87 }
88 
Open_CheckBreak()89 HRESULT CExtractCallbackImp::Open_CheckBreak()
90 {
91   return ProgressDialog->Sync.CheckStop();
92 }
93 
Open_SetTotal(const UInt64 * files,const UInt64 * bytes)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 
Open_SetCompleted(const UInt64 * files,const UInt64 * bytes)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 
Open_Finished()136 HRESULT CExtractCallbackImp::Open_Finished()
137 {
138   return ProgressDialog->Sync.CheckStop();
139 }
140 
141 #ifndef _NO_CRYPTO
142 
Open_CryptoGetTextPassword(BSTR * password)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
SetRatioInfo(const UInt64 * inSize,const UInt64 * outSize)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 
AskOverwrite(const wchar_t * existName,const FILETIME * existTime,const UInt64 * existSize,const wchar_t * newName,const FILETIME * newTime,const UInt64 * newSize,Int32 * answer)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 
PrepareOperation(const wchar_t * name,Int32 isFolder,Int32 askExtractMode,const UInt64 *)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 
MessageError(const wchar_t * s)242 STDMETHODIMP 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 _SFX
256 
ShowMessage(const wchar_t * s)257 STDMETHODIMP CExtractCallbackImp::ShowMessage(const wchar_t *s)
258 {
259   AddError_Message(s);
260   return S_OK;
261 }
262 
263 #endif
264 
SetExtractErrorMessage(Int32 opRes,Int32 encrypted,const wchar_t * fileName,UString & s)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       s += "Error #";
339       s.Add_UInt32(opRes);
340     }
341 
342     if (encrypted && opRes != NArchive::NExtract::NOperationResult::kWrongPassword)
343     {
344       // s += " : ";
345       // AddLangString(s, IDS_EXTRACT_MSG_ENCRYPTED);
346       s += " : ";
347       AddLangString(s, IDS_EXTRACT_MSG_WRONG_PSW_GUESS);
348     }
349     s += " : ";
350     s += fileName;
351   }
352 }
353 
SetOperationResult(Int32 opRes,Int32 encrypted)354 STDMETHODIMP CExtractCallbackImp::SetOperationResult(Int32 opRes, Int32 encrypted)
355 {
356   switch (opRes)
357   {
358     case NArchive::NExtract::NOperationResult::kOK:
359       break;
360     default:
361     {
362       UString s;
363       SetExtractErrorMessage(opRes, encrypted, _currentFilePath, s);
364       Add_ArchiveName_Error();
365       AddError_Message(s);
366     }
367   }
368 
369   #ifndef _SFX
370   if (_isFolder)
371     NumFolders++;
372   else
373     NumFiles++;
374   ProgressDialog->Sync.Set_NumFilesCur(NumFiles);
375   #endif
376 
377   return S_OK;
378 }
379 
ReportExtractResult(Int32 opRes,Int32 encrypted,const wchar_t * name)380 STDMETHODIMP CExtractCallbackImp::ReportExtractResult(Int32 opRes, Int32 encrypted, const wchar_t *name)
381 {
382   if (opRes != NArchive::NExtract::NOperationResult::kOK)
383   {
384     UString s;
385     SetExtractErrorMessage(opRes, encrypted, name, s);
386     Add_ArchiveName_Error();
387     AddError_Message(s);
388   }
389   return S_OK;
390 }
391 
392 ////////////////////////////////////////
393 // IExtractCallbackUI
394 
BeforeOpen(const wchar_t * name,bool)395 HRESULT CExtractCallbackImp::BeforeOpen(const wchar_t *name, bool /* testMode */)
396 {
397   #ifndef _SFX
398   RINOK(ProgressDialog->Sync.CheckStop());
399   ProgressDialog->Sync.Set_TitleFileName(name);
400   #endif
401   _currentArchivePath = name;
402   return S_OK;
403 }
404 
SetCurrentFilePath2(const wchar_t * path)405 HRESULT CExtractCallbackImp::SetCurrentFilePath2(const wchar_t *path)
406 {
407   _currentFilePath = path;
408   #ifndef _SFX
409   ProgressDialog->Sync.Set_FilePath(path);
410   #endif
411   return S_OK;
412 }
413 
414 #ifndef _SFX
415 
SetCurrentFilePath(const wchar_t * path)416 HRESULT CExtractCallbackImp::SetCurrentFilePath(const wchar_t *path)
417 {
418   #ifndef _SFX
419   if (NeedAddFile)
420     NumFiles++;
421   NeedAddFile = true;
422   ProgressDialog->Sync.Set_NumFilesCur(NumFiles);
423   #endif
424   return SetCurrentFilePath2(path);
425 }
426 
427 #endif
428 
429 UString HResultToMessage(HRESULT errorCode);
430 
431 static const UInt32 k_ErrorFlagsIds[] =
432 {
433   IDS_EXTRACT_MSG_IS_NOT_ARC,
434   IDS_EXTRACT_MSG_HEADERS_ERROR,
435   IDS_EXTRACT_MSG_HEADERS_ERROR,
436   IDS_OPEN_MSG_UNAVAILABLE_START,
437   IDS_OPEN_MSG_UNCONFIRMED_START,
438   IDS_EXTRACT_MSG_UEXPECTED_END,
439   IDS_EXTRACT_MSG_DATA_AFTER_END,
440   IDS_EXTRACT_MSG_UNSUPPORTED_METHOD,
441   IDS_OPEN_MSG_UNSUPPORTED_FEATURE,
442   IDS_EXTRACT_MSG_DATA_ERROR,
443   IDS_EXTRACT_MSG_CRC_ERROR
444 };
445 
AddNewLineString(UString & s,const UString & m)446 static void AddNewLineString(UString &s, const UString &m)
447 {
448   s += m;
449   s.Add_LF();
450 }
451 
GetOpenArcErrorMessage(UInt32 errorFlags)452 UString GetOpenArcErrorMessage(UInt32 errorFlags)
453 {
454   UString s;
455 
456   for (unsigned i = 0; i < ARRAY_SIZE(k_ErrorFlagsIds); i++)
457   {
458     UInt32 f = ((UInt32)1 << i);
459     if ((errorFlags & f) == 0)
460       continue;
461     UInt32 id = k_ErrorFlagsIds[i];
462     UString m = LangString(id);
463     if (m.IsEmpty())
464       continue;
465     if (f == kpv_ErrorFlags_EncryptedHeadersError)
466     {
467       m += " : ";
468       AddLangString(m, IDS_EXTRACT_MSG_WRONG_PSW_GUESS);
469     }
470     if (!s.IsEmpty())
471       s.Add_LF();
472     s += m;
473     errorFlags &= ~f;
474   }
475 
476   if (errorFlags != 0)
477   {
478     char sz[16];
479     sz[0] = '0';
480     sz[1] = 'x';
481     ConvertUInt32ToHex(errorFlags, sz + 2);
482     if (!s.IsEmpty())
483       s.Add_LF();
484     s += sz;
485   }
486 
487   return s;
488 }
489 
ErrorInfo_Print(UString & s,const CArcErrorInfo & er)490 static void ErrorInfo_Print(UString &s, const CArcErrorInfo &er)
491 {
492   UInt32 errorFlags = er.GetErrorFlags();
493   UInt32 warningFlags = er.GetWarningFlags();
494 
495   if (errorFlags != 0)
496     AddNewLineString(s, GetOpenArcErrorMessage(errorFlags));
497 
498   if (!er.ErrorMessage.IsEmpty())
499     AddNewLineString(s, er.ErrorMessage);
500 
501   if (warningFlags != 0)
502   {
503     s += GetNameOfProperty(kpidWarningFlags, L"Warnings");
504     s += ":";
505     s.Add_LF();
506     AddNewLineString(s, GetOpenArcErrorMessage(warningFlags));
507   }
508 
509   if (!er.WarningMessage.IsEmpty())
510   {
511     s += GetNameOfProperty(kpidWarning, L"Warning");
512     s += ": ";
513     s += er.WarningMessage;
514     s.Add_LF();
515   }
516 }
517 
GetBracedType(const wchar_t * type)518 static UString GetBracedType(const wchar_t *type)
519 {
520   UString s ('[');
521   s += type;
522   s += ']';
523   return s;
524 }
525 
OpenResult_GUI(UString & s,const CCodecs * codecs,const CArchiveLink & arcLink,const wchar_t * name,HRESULT result)526 void OpenResult_GUI(UString &s, const CCodecs *codecs, const CArchiveLink &arcLink, const wchar_t *name, HRESULT result)
527 {
528   FOR_VECTOR (level, arcLink.Arcs)
529   {
530     const CArc &arc = arcLink.Arcs[level];
531     const CArcErrorInfo &er = arc.ErrorInfo;
532 
533     if (!er.IsThereErrorOrWarning() && er.ErrorFormatIndex < 0)
534       continue;
535 
536     if (s.IsEmpty())
537     {
538       s += name;
539       s.Add_LF();
540     }
541 
542     if (level != 0)
543     {
544       AddNewLineString(s, arc.Path);
545     }
546 
547     ErrorInfo_Print(s, er);
548 
549     if (er.ErrorFormatIndex >= 0)
550     {
551       AddNewLineString(s, GetNameOfProperty(kpidWarning, L"Warning"));
552       if (arc.FormatIndex == er.ErrorFormatIndex)
553       {
554         AddNewLineString(s, LangString(IDS_IS_OPEN_WITH_OFFSET));
555       }
556       else
557       {
558         AddNewLineString(s, MyFormatNew(IDS_CANT_OPEN_AS_TYPE, GetBracedType(codecs->GetFormatNamePtr(er.ErrorFormatIndex))));
559         AddNewLineString(s, MyFormatNew(IDS_IS_OPEN_AS_TYPE, GetBracedType(codecs->GetFormatNamePtr(arc.FormatIndex))));
560       }
561     }
562   }
563 
564   if (arcLink.NonOpen_ErrorInfo.ErrorFormatIndex >= 0 || result != S_OK)
565   {
566     s += name;
567     s.Add_LF();
568     if (!arcLink.Arcs.IsEmpty())
569       AddNewLineString(s, arcLink.NonOpen_ArcPath);
570 
571     if (arcLink.NonOpen_ErrorInfo.ErrorFormatIndex >= 0 || result == S_FALSE)
572     {
573       UINT id = IDS_CANT_OPEN_ARCHIVE;
574       UString param;
575       if (arcLink.PasswordWasAsked)
576         id = IDS_CANT_OPEN_ENCRYPTED_ARCHIVE;
577       else if (arcLink.NonOpen_ErrorInfo.ErrorFormatIndex >= 0)
578       {
579         id = IDS_CANT_OPEN_AS_TYPE;
580         param = GetBracedType(codecs->GetFormatNamePtr(arcLink.NonOpen_ErrorInfo.ErrorFormatIndex));
581       }
582       UString s2 = MyFormatNew(id, param);
583       s2.Replace(L" ''", L"");
584       s2.Replace(L"''", L"");
585       s += s2;
586     }
587     else
588       s += HResultToMessage(result);
589 
590     s.Add_LF();
591     ErrorInfo_Print(s, arcLink.NonOpen_ErrorInfo);
592   }
593 
594   if (!s.IsEmpty() && s.Back() == '\n')
595     s.DeleteBack();
596 }
597 
OpenResult(const CCodecs * codecs,const CArchiveLink & arcLink,const wchar_t * name,HRESULT result)598 HRESULT CExtractCallbackImp::OpenResult(const CCodecs *codecs, const CArchiveLink &arcLink, const wchar_t *name, HRESULT result)
599 {
600   _currentArchivePath = name;
601   _needWriteArchivePath = true;
602 
603   UString s;
604   OpenResult_GUI(s, codecs, arcLink, name, result);
605   if (!s.IsEmpty())
606   {
607     NumArchiveErrors++;
608     AddError_Message(s);
609     _needWriteArchivePath = false;
610   }
611 
612   return S_OK;
613 }
614 
ThereAreNoFiles()615 HRESULT CExtractCallbackImp::ThereAreNoFiles()
616 {
617   return S_OK;
618 }
619 
Add_ArchiveName_Error()620 void CExtractCallbackImp::Add_ArchiveName_Error()
621 {
622   if (_needWriteArchivePath)
623   {
624     if (!_currentArchivePath.IsEmpty())
625       AddError_Message(_currentArchivePath);
626     _needWriteArchivePath = false;
627   }
628 }
629 
ExtractResult(HRESULT result)630 HRESULT CExtractCallbackImp::ExtractResult(HRESULT result)
631 {
632   if (result == S_OK)
633     return result;
634   NumArchiveErrors++;
635   if (result == E_ABORT || result == ERROR_DISK_FULL)
636     return result;
637 
638   Add_ArchiveName_Error();
639   if (!_currentFilePath.IsEmpty())
640     MessageError(_currentFilePath);
641   MessageError(NError::MyFormatMessage(result));
642   return S_OK;
643 }
644 
645 #ifndef _NO_CRYPTO
646 
SetPassword(const UString & password)647 HRESULT CExtractCallbackImp::SetPassword(const UString &password)
648 {
649   PasswordIsDefined = true;
650   Password = password;
651   return S_OK;
652 }
653 
CryptoGetTextPassword(BSTR * password)654 STDMETHODIMP CExtractCallbackImp::CryptoGetTextPassword(BSTR *password)
655 {
656   PasswordWasAsked = true;
657   if (!PasswordIsDefined)
658   {
659     CPasswordDialog dialog;
660     #ifndef _SFX
661     bool showPassword = NExtract::Read_ShowPassword();
662     dialog.ShowPassword = showPassword;
663     #endif
664     ProgressDialog->WaitCreating();
665     if (dialog.Create(*ProgressDialog) != IDOK)
666       return E_ABORT;
667     Password = dialog.Password;
668     PasswordIsDefined = true;
669     #ifndef _SFX
670     if (dialog.ShowPassword != showPassword)
671       NExtract::Save_ShowPassword(dialog.ShowPassword);
672     #endif
673   }
674   return StringToBstr(Password, password);
675 }
676 
677 #endif
678 
679 #ifndef _SFX
680 
AskWrite(const wchar_t * srcPath,Int32 srcIsFolder,const FILETIME * srcTime,const UInt64 * srcSize,const wchar_t * destPath,BSTR * destPathResult,Int32 * writeAnswer)681 STDMETHODIMP CExtractCallbackImp::AskWrite(
682     const wchar_t *srcPath, Int32 srcIsFolder,
683     const FILETIME *srcTime, const UInt64 *srcSize,
684     const wchar_t *destPath,
685     BSTR *destPathResult,
686     Int32 *writeAnswer)
687 {
688   UString destPathResultTemp = destPath;
689 
690   // RINOK(StringToBstr(destPath, destPathResult));
691 
692   *destPathResult = 0;
693   *writeAnswer = BoolToInt(false);
694 
695   FString destPathSys = us2fs(destPath);
696   bool srcIsFolderSpec = IntToBool(srcIsFolder);
697   CFileInfo destFileInfo;
698 
699   if (destFileInfo.Find(destPathSys))
700   {
701     if (srcIsFolderSpec)
702     {
703       if (!destFileInfo.IsDir())
704       {
705         RINOK(MessageError("can not replace file with folder with same name", destPathSys));
706         return E_ABORT;
707       }
708       *writeAnswer = BoolToInt(false);
709       return S_OK;
710     }
711 
712     if (destFileInfo.IsDir())
713     {
714       RINOK(MessageError("can not replace folder with file with same name", destPathSys));
715       *writeAnswer = BoolToInt(false);
716       return S_OK;
717     }
718 
719     switch (OverwriteMode)
720     {
721       case NExtract::NOverwriteMode::kSkip:
722         return S_OK;
723       case NExtract::NOverwriteMode::kAsk:
724       {
725         Int32 overwriteResult;
726         UString destPathSpec = destPath;
727         int slashPos = destPathSpec.ReverseFind_PathSepar();
728         destPathSpec.DeleteFrom(slashPos + 1);
729         destPathSpec += fs2us(destFileInfo.Name);
730 
731         RINOK(AskOverwrite(
732             destPathSpec,
733             &destFileInfo.MTime, &destFileInfo.Size,
734             srcPath,
735             srcTime, srcSize,
736             &overwriteResult));
737 
738         switch (overwriteResult)
739         {
740           case NOverwriteAnswer::kCancel: return E_ABORT;
741           case NOverwriteAnswer::kNo: return S_OK;
742           case NOverwriteAnswer::kNoToAll: OverwriteMode = NExtract::NOverwriteMode::kSkip; return S_OK;
743           case NOverwriteAnswer::kYes: break;
744           case NOverwriteAnswer::kYesToAll: OverwriteMode = NExtract::NOverwriteMode::kOverwrite; break;
745           case NOverwriteAnswer::kAutoRename: OverwriteMode = NExtract::NOverwriteMode::kRename; break;
746           default:
747             return E_FAIL;
748         }
749       }
750     }
751 
752     if (OverwriteMode == NExtract::NOverwriteMode::kRename)
753     {
754       if (!AutoRenamePath(destPathSys))
755       {
756         RINOK(MessageError("can not create name for file", destPathSys));
757         return E_ABORT;
758       }
759       destPathResultTemp = fs2us(destPathSys);
760     }
761     else
762     {
763       if (NFind::DoesFileExist(destPathSys))
764       if (!NDir::DeleteFileAlways(destPathSys))
765       if (GetLastError() != ERROR_FILE_NOT_FOUND)
766       {
767         RINOK(MessageError("can not delete output file", destPathSys));
768         return E_ABORT;
769       }
770     }
771   }
772   *writeAnswer = BoolToInt(true);
773   return StringToBstr(destPathResultTemp, destPathResult);
774 }
775 
776 
UseExtractToStream(Int32 * res)777 STDMETHODIMP CExtractCallbackImp::UseExtractToStream(Int32 *res)
778 {
779   *res = BoolToInt(StreamMode);
780   return S_OK;
781 }
782 
GetTime(IGetProp * getProp,PROPID propID,FILETIME & ft,bool & ftDefined)783 static HRESULT GetTime(IGetProp *getProp, PROPID propID, FILETIME &ft, bool &ftDefined)
784 {
785   ftDefined = false;
786   NCOM::CPropVariant prop;
787   RINOK(getProp->GetProp(propID, &prop));
788   if (prop.vt == VT_FILETIME)
789   {
790     ft = prop.filetime;
791     ftDefined = (ft.dwHighDateTime != 0 || ft.dwLowDateTime != 0);
792   }
793   else if (prop.vt != VT_EMPTY)
794     return E_FAIL;
795   return S_OK;
796 }
797 
798 
GetItemBoolProp(IGetProp * getProp,PROPID propID,bool & result)799 static HRESULT GetItemBoolProp(IGetProp *getProp, PROPID propID, bool &result)
800 {
801   NCOM::CPropVariant prop;
802   result = false;
803   RINOK(getProp->GetProp(propID, &prop));
804   if (prop.vt == VT_BOOL)
805     result = VARIANT_BOOLToBool(prop.boolVal);
806   else if (prop.vt != VT_EMPTY)
807     return E_FAIL;
808   return S_OK;
809 }
810 
811 
GetStream7(const wchar_t * name,Int32 isDir,ISequentialOutStream ** outStream,Int32 askExtractMode,IGetProp * getProp)812 STDMETHODIMP CExtractCallbackImp::GetStream7(const wchar_t *name,
813     Int32 isDir,
814     ISequentialOutStream **outStream, Int32 askExtractMode,
815     IGetProp *getProp)
816 {
817   COM_TRY_BEGIN
818   *outStream = 0;
819   _newVirtFileWasAdded = false;
820   _hashStreamWasUsed = false;
821   _needUpdateStat = false;
822 
823   if (_hashStream)
824     _hashStreamSpec->ReleaseStream();
825 
826   GetItemBoolProp(getProp, kpidIsAltStream, _isAltStream);
827 
828   if (!ProcessAltStreams && _isAltStream)
829     return S_OK;
830 
831   _filePath = name;
832   _isFolder = IntToBool(isDir);
833   _curSize = 0;
834   _curSizeDefined = false;
835 
836   UInt64 size = 0;
837   bool sizeDefined;
838   {
839     NCOM::CPropVariant prop;
840     RINOK(getProp->GetProp(kpidSize, &prop));
841     sizeDefined = ConvertPropVariantToUInt64(prop, size);
842   }
843 
844   if (sizeDefined)
845   {
846     _curSize = size;
847     _curSizeDefined = true;
848   }
849 
850   if (askExtractMode != NArchive::NExtract::NAskMode::kExtract &&
851       askExtractMode != NArchive::NExtract::NAskMode::kTest)
852     return S_OK;
853 
854   _needUpdateStat = true;
855 
856   CMyComPtr<ISequentialOutStream> outStreamLoc;
857 
858   if (VirtFileSystem && askExtractMode == NArchive::NExtract::NAskMode::kExtract)
859   {
860     CVirtFile &file = VirtFileSystemSpec->AddNewFile();
861     _newVirtFileWasAdded = true;
862     file.Name = name;
863     file.IsDir = IntToBool(isDir);
864     file.IsAltStream = _isAltStream;
865     file.Size = 0;
866 
867     RINOK(GetTime(getProp, kpidCTime, file.CTime, file.CTimeDefined));
868     RINOK(GetTime(getProp, kpidATime, file.ATime, file.ATimeDefined));
869     RINOK(GetTime(getProp, kpidMTime, file.MTime, file.MTimeDefined));
870 
871     NCOM::CPropVariant prop;
872     RINOK(getProp->GetProp(kpidAttrib, &prop));
873     if (prop.vt == VT_UI4)
874     {
875       file.Attrib = prop.ulVal;
876       file.AttribDefined = true;
877     }
878     // else if (isDir) file.Attrib = FILE_ATTRIBUTE_DIRECTORY;
879 
880     file.ExpectedSize = 0;
881     if (sizeDefined)
882       file.ExpectedSize = size;
883     outStreamLoc = VirtFileSystem;
884   }
885 
886   if (_hashStream)
887   {
888     {
889       _hashStreamSpec->SetStream(outStreamLoc);
890       outStreamLoc = _hashStream;
891       _hashStreamSpec->Init(true);
892       _hashStreamWasUsed = true;
893     }
894   }
895 
896   if (outStreamLoc)
897     *outStream = outStreamLoc.Detach();
898   return S_OK;
899   COM_TRY_END
900 }
901 
PrepareOperation7(Int32 askExtractMode)902 STDMETHODIMP CExtractCallbackImp::PrepareOperation7(Int32 askExtractMode)
903 {
904   COM_TRY_BEGIN
905   _needUpdateStat = (
906       askExtractMode == NArchive::NExtract::NAskMode::kExtract ||
907       askExtractMode == NArchive::NExtract::NAskMode::kTest);
908 
909   /*
910   _extractMode = false;
911   switch (askExtractMode)
912   {
913     case NArchive::NExtract::NAskMode::kExtract:
914       if (_testMode)
915         askExtractMode = NArchive::NExtract::NAskMode::kTest;
916       else
917         _extractMode = true;
918       break;
919   };
920   */
921   return SetCurrentFilePath2(_filePath);
922   COM_TRY_END
923 }
924 
SetOperationResult7(Int32 opRes,Int32 encrypted)925 STDMETHODIMP CExtractCallbackImp::SetOperationResult7(Int32 opRes, Int32 encrypted)
926 {
927   COM_TRY_BEGIN
928   if (VirtFileSystem && _newVirtFileWasAdded)
929   {
930     // FIXME: probably we must request file size from VirtFileSystem
931     // _curSize = VirtFileSystem->GetLastFileSize()
932     // _curSizeDefined = true;
933     RINOK(VirtFileSystemSpec->CloseMemFile());
934   }
935   if (_hashStream && _hashStreamWasUsed)
936   {
937     _hashStreamSpec->_hash->Final(_isFolder, _isAltStream, _filePath);
938     _curSize = _hashStreamSpec->GetSize();
939     _curSizeDefined = true;
940     _hashStreamSpec->ReleaseStream();
941     _hashStreamWasUsed = false;
942   }
943   else if (_hashCalc && _needUpdateStat)
944   {
945     _hashCalc->SetSize(_curSize);
946     _hashCalc->Final(_isFolder, _isAltStream, _filePath);
947   }
948   return SetOperationResult(opRes, encrypted);
949   COM_TRY_END
950 }
951 
952 
953 static const size_t k_SizeT_MAX = (size_t)((size_t)0 - 1);
954 
955 static const UInt32 kBlockSize = ((UInt32)1 << 31);
956 
Write(const void * data,UInt32 size,UInt32 * processedSize)957 STDMETHODIMP CVirtFileSystem::Write(const void *data, UInt32 size, UInt32 *processedSize)
958 {
959   if (processedSize)
960     *processedSize = 0;
961   if (size == 0)
962     return S_OK;
963   if (!_fileMode)
964   {
965     CVirtFile &file = Files.Back();
966     size_t rem = file.Data.Size() - (size_t)file.Size;
967     bool useMem = true;
968     if (rem < size)
969     {
970       UInt64 b = 0;
971       if (file.Data.Size() == 0)
972         b = file.ExpectedSize;
973       UInt64 a = file.Size + size;
974       if (b < a)
975         b = a;
976       a = (UInt64)file.Data.Size() * 2;
977       if (b < a)
978         b = a;
979       useMem = false;
980       if (b <= k_SizeT_MAX && b <= MaxTotalAllocSize)
981         useMem = file.Data.ReAlloc_KeepData((size_t)b, (size_t)file.Size);
982     }
983     if (useMem)
984     {
985       memcpy(file.Data + file.Size, data, size);
986       file.Size += size;
987       if (processedSize)
988         *processedSize = (UInt32)size;
989       return S_OK;
990     }
991     _fileMode = true;
992   }
993   RINOK(FlushToDisk(false));
994   return _outFileStream->Write(data, size, processedSize);
995 }
996 
FlushToDisk(bool closeLast)997 HRESULT CVirtFileSystem::FlushToDisk(bool closeLast)
998 {
999   if (!_outFileStream)
1000   {
1001     _outFileStreamSpec = new COutFileStream;
1002     _outFileStream = _outFileStreamSpec;
1003   }
1004   while (_numFlushed < Files.Size())
1005   {
1006     const CVirtFile &file = Files[_numFlushed];
1007     const FString path = DirPrefix + us2fs(Get_Correct_FsFile_Name(file.Name));
1008     if (!_fileIsOpen)
1009     {
1010       if (!_outFileStreamSpec->Create(path, false))
1011       {
1012         _outFileStream.Release();
1013         return E_FAIL;
1014         // MessageBoxMyError(UString("Can't create file ") + fs2us(tempFilePath));
1015       }
1016       _fileIsOpen = true;
1017       RINOK(WriteStream(_outFileStream, file.Data, (size_t)file.Size));
1018     }
1019     if (_numFlushed == Files.Size() - 1 && !closeLast)
1020       break;
1021     if (file.CTimeDefined ||
1022         file.ATimeDefined ||
1023         file.MTimeDefined)
1024       _outFileStreamSpec->SetTime(
1025           file.CTimeDefined ? &file.CTime : NULL,
1026           file.ATimeDefined ? &file.ATime : NULL,
1027           file.MTimeDefined ? &file.MTime : NULL);
1028     _outFileStreamSpec->Close();
1029     _numFlushed++;
1030     _fileIsOpen = false;
1031     if (file.AttribDefined)
1032       NDir::SetFileAttrib_PosixHighDetect(path, file.Attrib);
1033   }
1034   return S_OK;
1035 }
1036 
1037 #endif
1038