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