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
36using namespace NWindows;
37using namespace NFile;
38using namespace NFind;
39
40CExtractCallbackImp::~CExtractCallbackImp() {}
41
42void 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
57void CExtractCallbackImp::AddError_Message(LPCWSTR s)
58{
59  ThereAreMessageErrors = true;
60  ProgressDialog->Sync.AddError_Message(s);
61}
62
63#ifndef Z7_SFX
64
65Z7_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
77Z7_COM7F_IMF(CExtractCallbackImp::SetTotal(UInt64 total))
78{
79  ProgressDialog->Sync.Set_NumBytesTotal(total);
80  return S_OK;
81}
82
83Z7_COM7F_IMF(CExtractCallbackImp::SetCompleted(const UInt64 *value))
84{
85  return ProgressDialog->Sync.Set_NumBytesCur(value);
86}
87
88HRESULT CExtractCallbackImp::Open_CheckBreak()
89{
90  return ProgressDialog->Sync.CheckStop();
91}
92
93HRESULT 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
118HRESULT 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
135HRESULT CExtractCallbackImp::Open_Finished()
136{
137  return ProgressDialog->Sync.CheckStop();
138}
139
140#ifndef Z7_NO_CRYPTO
141
142HRESULT CExtractCallbackImp::Open_CryptoGetTextPassword(BSTR *password)
143{
144  return CryptoGetTextPassword(password);
145}
146
147/*
148HRESULT CExtractCallbackImp::Open_GetPasswordIfAny(bool &passwordIsDefined, UString &password)
149{
150  passwordIsDefined = PasswordIsDefined;
151  password = Password;
152  return S_OK;
153}
154
155bool CExtractCallbackImp::Open_WasPasswordAsked()
156{
157  return PasswordWasAsked;
158}
159
160void CExtractCallbackImp::Open_Clear_PasswordWasAsked_Flag()
161{
162  PasswordWasAsked = false;
163}
164*/
165
166#endif
167
168
169#ifndef Z7_SFX
170Z7_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/*
178Z7_COM7F_IMF(CExtractCallbackImp::SetTotalFiles(UInt64 total)
179{
180  ProgressDialog->Sync.SetNumFilesTotal(total);
181  return S_OK;
182}
183
184Z7_COM7F_IMF(CExtractCallbackImp::SetCompletedFiles(const UInt64 *value)
185{
186  if (value != NULL)
187    ProgressDialog->Sync.SetNumFilesCur(*value);
188  return S_OK;
189}
190*/
191
192Z7_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
224Z7_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
242Z7_COM7F_IMF(CExtractCallbackImp::MessageError(const wchar_t *s))
243{
244  AddError_Message(s);
245  return S_OK;
246}
247
248HRESULT 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
257Z7_COM7F_IMF(CExtractCallbackImp::ShowMessage(const wchar_t *s))
258{
259  AddError_Message(s);
260  return S_OK;
261}
262
263#endif
264
265void SetExtractErrorMessage(Int32 opRes, Int32 encrypted, const wchar_t *fileName, UString &s);
266void 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
364Z7_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
390Z7_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
405HRESULT 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
415HRESULT 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
426Z7_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
439UString HResultToMessage(HRESULT errorCode);
440
441static 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
456static void AddNewLineString(UString &s, const UString &m)
457{
458  s += m;
459  s.Add_LF();
460}
461
462UString GetOpenArcErrorMessage(UInt32 errorFlags);
463UString 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
501static 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
529static UString GetBracedType(const wchar_t *type)
530{
531  UString s ('[');
532  s += type;
533  s += ']';
534  return s;
535}
536
537void OpenResult_GUI(UString &s, const CCodecs *codecs, const CArchiveLink &arcLink, const wchar_t *name, HRESULT result);
538void 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
610HRESULT 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
627HRESULT CExtractCallbackImp::ThereAreNoFiles()
628{
629  return S_OK;
630}
631
632void CExtractCallbackImp::Add_ArchiveName_Error()
633{
634  if (_needWriteArchivePath)
635  {
636    if (!_currentArchivePath.IsEmpty())
637      AddError_Message(_currentArchivePath);
638    _needWriteArchivePath = false;
639  }
640}
641
642HRESULT 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
661HRESULT CExtractCallbackImp::SetPassword(const UString &password)
662{
663  PasswordIsDefined = true;
664  Password = password;
665  return S_OK;
666}
667
668Z7_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
695Z7_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
794Z7_COM7F_IMF(CExtractCallbackImp::UseExtractToStream(Int32 *res))
795{
796  *res = BoolToInt(StreamMode);
797  return S_OK;
798}
799
800static 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
816static 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
829Z7_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
919Z7_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
944Z7_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
975Z7_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
1016HRESULT 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