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