Home | History | Annotate | Download | only in Common
      1 // ArchiveExtractCallback.cpp
      2 
      3 #include "StdAfx.h"
      4 
      5 #include "Common/ComTry.h"
      6 #include "Common/Wildcard.h"
      7 
      8 #include "Windows/FileDir.h"
      9 #include "Windows/FileFind.h"
     10 #include "Windows/PropVariant.h"
     11 #include "Windows/PropVariantConversions.h"
     12 
     13 #include "../../Common/FilePathAutoRename.h"
     14 
     15 #include "../Common/ExtractingFilePath.h"
     16 
     17 #include "ArchiveExtractCallback.h"
     18 
     19 using namespace NWindows;
     20 
     21 static const wchar_t *kCantAutoRename = L"ERROR: Can not create file with auto name";
     22 static const wchar_t *kCantRenameFile = L"ERROR: Can not rename existing file ";
     23 static const wchar_t *kCantDeleteOutputFile = L"ERROR: Can not delete output file ";
     24 
     25 void CArchiveExtractCallback::Init(
     26     const NWildcard::CCensorNode *wildcardCensor,
     27     const CArc *arc,
     28     IFolderArchiveExtractCallback *extractCallback2,
     29     bool stdOutMode, bool testMode, bool crcMode,
     30     const UString &directoryPath,
     31     const UStringVector &removePathParts,
     32     UInt64 packSize)
     33 {
     34   _wildcardCensor = wildcardCensor;
     35 
     36   _stdOutMode = stdOutMode;
     37   _testMode = testMode;
     38   _crcMode = crcMode;
     39   _unpTotal = 1;
     40   _packTotal = packSize;
     41 
     42   _extractCallback2 = extractCallback2;
     43   _compressProgress.Release();
     44   _extractCallback2.QueryInterface(IID_ICompressProgressInfo, &_compressProgress);
     45 
     46   LocalProgressSpec->Init(extractCallback2, true);
     47   LocalProgressSpec->SendProgress = false;
     48 
     49 
     50   _removePathParts = removePathParts;
     51   _arc = arc;
     52   _directoryPath = directoryPath;
     53   NFile::NName::NormalizeDirPathPrefix(_directoryPath);
     54 }
     55 
     56 STDMETHODIMP CArchiveExtractCallback::SetTotal(UInt64 size)
     57 {
     58   COM_TRY_BEGIN
     59   _unpTotal = size;
     60   if (!_multiArchives && _extractCallback2)
     61     return _extractCallback2->SetTotal(size);
     62   return S_OK;
     63   COM_TRY_END
     64 }
     65 
     66 static void NormalizeVals(UInt64 &v1, UInt64 &v2)
     67 {
     68   const UInt64 kMax = (UInt64)1 << 31;
     69   while (v1 > kMax)
     70   {
     71     v1 >>= 1;
     72     v2 >>= 1;
     73   }
     74 }
     75 
     76 static UInt64 MyMultDiv64(UInt64 unpCur, UInt64 unpTotal, UInt64 packTotal)
     77 {
     78   NormalizeVals(packTotal, unpTotal);
     79   NormalizeVals(unpCur, unpTotal);
     80   if (unpTotal == 0)
     81     unpTotal = 1;
     82   return unpCur * packTotal / unpTotal;
     83 }
     84 
     85 STDMETHODIMP CArchiveExtractCallback::SetCompleted(const UInt64 *completeValue)
     86 {
     87   COM_TRY_BEGIN
     88   if (!_extractCallback2)
     89     return S_OK;
     90 
     91   if (_multiArchives)
     92   {
     93     if (completeValue != NULL)
     94     {
     95       UInt64 packCur = LocalProgressSpec->InSize + MyMultDiv64(*completeValue, _unpTotal, _packTotal);
     96       return _extractCallback2->SetCompleted(&packCur);
     97     }
     98   }
     99   return _extractCallback2->SetCompleted(completeValue);
    100   COM_TRY_END
    101 }
    102 
    103 STDMETHODIMP CArchiveExtractCallback::SetRatioInfo(const UInt64 *inSize, const UInt64 *outSize)
    104 {
    105   COM_TRY_BEGIN
    106   return _localProgress->SetRatioInfo(inSize, outSize);
    107   COM_TRY_END
    108 }
    109 
    110 void CArchiveExtractCallback::CreateComplexDirectory(const UStringVector &dirPathParts, UString &fullPath)
    111 {
    112   fullPath = _directoryPath;
    113   for (int i = 0; i < dirPathParts.Size(); i++)
    114   {
    115     if (i > 0)
    116       fullPath += wchar_t(NFile::NName::kDirDelimiter);
    117     fullPath += dirPathParts[i];
    118     NFile::NDirectory::MyCreateDirectory(fullPath);
    119   }
    120 }
    121 
    122 HRESULT CArchiveExtractCallback::GetTime(int index, PROPID propID, FILETIME &filetime, bool &filetimeIsDefined)
    123 {
    124   filetimeIsDefined = false;
    125   NCOM::CPropVariant prop;
    126   RINOK(_arc->Archive->GetProperty(index, propID, &prop));
    127   if (prop.vt == VT_FILETIME)
    128   {
    129     filetime = prop.filetime;
    130     filetimeIsDefined = (filetime.dwHighDateTime != 0 || filetime.dwLowDateTime != 0);
    131   }
    132   else if (prop.vt != VT_EMPTY)
    133     return E_FAIL;
    134   return S_OK;
    135 }
    136 
    137 HRESULT CArchiveExtractCallback::GetUnpackSize()
    138 {
    139   NCOM::CPropVariant prop;
    140   RINOK(_arc->Archive->GetProperty(_index, kpidSize, &prop));
    141   _curSizeDefined = (prop.vt != VT_EMPTY);
    142   if (_curSizeDefined)
    143     _curSize = ConvertPropVariantToUInt64(prop);
    144   return S_OK;
    145 }
    146 
    147 STDMETHODIMP CArchiveExtractCallback::GetStream(UInt32 index, ISequentialOutStream **outStream, Int32 askExtractMode)
    148 {
    149   COM_TRY_BEGIN
    150   _crcStream.Release();
    151   *outStream = 0;
    152   _outFileStream.Release();
    153 
    154   _encrypted = false;
    155   _isSplit = false;
    156   _curSize = 0;
    157   _curSizeDefined = false;
    158   _index = index;
    159 
    160   UString fullPath;
    161 
    162   IInArchive *archive = _arc->Archive;
    163   RINOK(_arc->GetItemPath(index, fullPath));
    164   RINOK(IsArchiveItemFolder(archive, index, _fi.IsDir));
    165 
    166   _filePath = fullPath;
    167 
    168   {
    169     NCOM::CPropVariant prop;
    170     RINOK(archive->GetProperty(index, kpidPosition, &prop));
    171     if (prop.vt != VT_EMPTY)
    172     {
    173       if (prop.vt != VT_UI8)
    174         return E_FAIL;
    175       _position = prop.uhVal.QuadPart;
    176       _isSplit = true;
    177     }
    178   }
    179 
    180   RINOK(GetArchiveItemBoolProp(archive, index, kpidEncrypted, _encrypted));
    181 
    182   RINOK(GetUnpackSize());
    183 
    184   if (_wildcardCensor)
    185   {
    186     if (!_wildcardCensor->CheckPath(fullPath, !_fi.IsDir))
    187       return S_OK;
    188   }
    189 
    190   if (askExtractMode == NArchive::NExtract::NAskMode::kExtract && !_testMode)
    191   {
    192     if (_stdOutMode)
    193     {
    194       CMyComPtr<ISequentialOutStream> outStreamLoc = new CStdOutFileStream;
    195       *outStream = outStreamLoc.Detach();
    196       return S_OK;
    197     }
    198 
    199     {
    200       NCOM::CPropVariant prop;
    201       RINOK(archive->GetProperty(index, kpidAttrib, &prop));
    202       if (prop.vt == VT_UI4)
    203       {
    204         _fi.Attrib = prop.ulVal;
    205         _fi.AttribDefined = true;
    206       }
    207       else if (prop.vt == VT_EMPTY)
    208         _fi.AttribDefined = false;
    209       else
    210         return E_FAIL;
    211     }
    212 
    213     RINOK(GetTime(index, kpidCTime, _fi.CTime, _fi.CTimeDefined));
    214     RINOK(GetTime(index, kpidATime, _fi.ATime, _fi.ATimeDefined));
    215     RINOK(GetTime(index, kpidMTime, _fi.MTime, _fi.MTimeDefined));
    216 
    217     bool isAnti = false;
    218     RINOK(_arc->IsItemAnti(index, isAnti));
    219 
    220     UStringVector pathParts;
    221     SplitPathToParts(fullPath, pathParts);
    222 
    223     if (pathParts.IsEmpty())
    224       return E_FAIL;
    225     int numRemovePathParts = 0;
    226     switch(_pathMode)
    227     {
    228       case NExtract::NPathMode::kFullPathnames:
    229         break;
    230       case NExtract::NPathMode::kCurrentPathnames:
    231       {
    232         numRemovePathParts = _removePathParts.Size();
    233         if (pathParts.Size() <= numRemovePathParts)
    234           return E_FAIL;
    235         for (int i = 0; i < numRemovePathParts; i++)
    236           if (_removePathParts[i].CompareNoCase(pathParts[i]) != 0)
    237             return E_FAIL;
    238         break;
    239       }
    240       case NExtract::NPathMode::kNoPathnames:
    241       {
    242         numRemovePathParts = pathParts.Size() - 1;
    243         break;
    244       }
    245     }
    246     pathParts.Delete(0, numRemovePathParts);
    247     MakeCorrectPath(pathParts);
    248     UString processedPath = MakePathNameFromParts(pathParts);
    249     if (!isAnti)
    250     {
    251       if (!_fi.IsDir)
    252       {
    253         if (!pathParts.IsEmpty())
    254           pathParts.DeleteBack();
    255       }
    256 
    257       if (!pathParts.IsEmpty())
    258       {
    259         UString fullPathNew;
    260         CreateComplexDirectory(pathParts, fullPathNew);
    261         if (_fi.IsDir)
    262           NFile::NDirectory::SetDirTime(fullPathNew,
    263             (WriteCTime && _fi.CTimeDefined) ? &_fi.CTime : NULL,
    264             (WriteATime && _fi.ATimeDefined) ? &_fi.ATime : NULL,
    265             (WriteMTime && _fi.MTimeDefined) ? &_fi.MTime : (_arc->MTimeDefined ? &_arc->MTime : NULL));
    266       }
    267     }
    268 
    269 
    270     UString fullProcessedPath = _directoryPath + processedPath;
    271 
    272     if (_fi.IsDir)
    273     {
    274       _diskFilePath = fullProcessedPath;
    275       if (isAnti)
    276         NFile::NDirectory::MyRemoveDirectory(_diskFilePath);
    277       return S_OK;
    278     }
    279 
    280     if (!_isSplit)
    281     {
    282     NFile::NFind::CFileInfoW fileInfo;
    283     if (fileInfo.Find(fullProcessedPath))
    284     {
    285       switch(_overwriteMode)
    286       {
    287         case NExtract::NOverwriteMode::kSkipExisting:
    288           return S_OK;
    289         case NExtract::NOverwriteMode::kAskBefore:
    290         {
    291           Int32 overwiteResult;
    292           RINOK(_extractCallback2->AskOverwrite(
    293               fullProcessedPath, &fileInfo.MTime, &fileInfo.Size, fullPath,
    294               _fi.MTimeDefined ? &_fi.MTime : NULL,
    295               _curSizeDefined ? &_curSize : NULL,
    296               &overwiteResult))
    297 
    298           switch(overwiteResult)
    299           {
    300             case NOverwriteAnswer::kCancel:
    301               return E_ABORT;
    302             case NOverwriteAnswer::kNo:
    303               return S_OK;
    304             case NOverwriteAnswer::kNoToAll:
    305               _overwriteMode = NExtract::NOverwriteMode::kSkipExisting;
    306               return S_OK;
    307             case NOverwriteAnswer::kYesToAll:
    308               _overwriteMode = NExtract::NOverwriteMode::kWithoutPrompt;
    309               break;
    310             case NOverwriteAnswer::kYes:
    311               break;
    312             case NOverwriteAnswer::kAutoRename:
    313               _overwriteMode = NExtract::NOverwriteMode::kAutoRename;
    314               break;
    315             default:
    316               return E_FAIL;
    317           }
    318         }
    319       }
    320       if (_overwriteMode == NExtract::NOverwriteMode::kAutoRename)
    321       {
    322         if (!AutoRenamePath(fullProcessedPath))
    323         {
    324           UString message = UString(kCantAutoRename) + fullProcessedPath;
    325           RINOK(_extractCallback2->MessageError(message));
    326           return E_FAIL;
    327         }
    328       }
    329       else if (_overwriteMode == NExtract::NOverwriteMode::kAutoRenameExisting)
    330       {
    331         UString existPath = fullProcessedPath;
    332         if (!AutoRenamePath(existPath))
    333         {
    334           UString message = kCantAutoRename + fullProcessedPath;
    335           RINOK(_extractCallback2->MessageError(message));
    336           return E_FAIL;
    337         }
    338         if (!NFile::NDirectory::MyMoveFile(fullProcessedPath, existPath))
    339         {
    340           UString message = UString(kCantRenameFile) + fullProcessedPath;
    341           RINOK(_extractCallback2->MessageError(message));
    342           return E_FAIL;
    343         }
    344       }
    345       else
    346         if (!NFile::NDirectory::DeleteFileAlways(fullProcessedPath))
    347         {
    348           UString message = UString(kCantDeleteOutputFile) +  fullProcessedPath;
    349           RINOK(_extractCallback2->MessageError(message));
    350           return S_OK;
    351           // return E_FAIL;
    352         }
    353     }
    354     }
    355     if (!isAnti)
    356     {
    357       _outFileStreamSpec = new COutFileStream;
    358       CMyComPtr<ISequentialOutStream> outStreamLoc(_outFileStreamSpec);
    359       if (!_outFileStreamSpec->Open(fullProcessedPath, _isSplit ? OPEN_ALWAYS: CREATE_ALWAYS))
    360       {
    361         // if (::GetLastError() != ERROR_FILE_EXISTS || !isSplit)
    362         {
    363           UString message = L"can not open output file " + fullProcessedPath;
    364           RINOK(_extractCallback2->MessageError(message));
    365           return S_OK;
    366         }
    367       }
    368       if (_isSplit)
    369       {
    370         RINOK(_outFileStreamSpec->Seek(_position, STREAM_SEEK_SET, NULL));
    371       }
    372       _outFileStream = outStreamLoc;
    373       *outStream = outStreamLoc.Detach();
    374     }
    375     _diskFilePath = fullProcessedPath;
    376   }
    377   else
    378   {
    379     *outStream = NULL;
    380   }
    381   if (_crcMode)
    382   {
    383     _crcStreamSpec = new COutStreamWithCRC;
    384     _crcStream = _crcStreamSpec;
    385     CMyComPtr<ISequentialOutStream> crcStream = _crcStreamSpec;
    386     _crcStreamSpec->SetStream(*outStream);
    387     if (*outStream)
    388       (*outStream)->Release();
    389     *outStream = crcStream.Detach();
    390     _crcStreamSpec->Init(true);
    391   }
    392   return S_OK;
    393   COM_TRY_END
    394 }
    395 
    396 STDMETHODIMP CArchiveExtractCallback::PrepareOperation(Int32 askExtractMode)
    397 {
    398   COM_TRY_BEGIN
    399   _extractMode = false;
    400   switch (askExtractMode)
    401   {
    402     case NArchive::NExtract::NAskMode::kExtract:
    403       if (_testMode)
    404         askExtractMode = NArchive::NExtract::NAskMode::kTest;
    405       else
    406         _extractMode = true;
    407       break;
    408   };
    409   return _extractCallback2->PrepareOperation(_filePath, _fi.IsDir,
    410       askExtractMode, _isSplit ? &_position: 0);
    411   COM_TRY_END
    412 }
    413 
    414 STDMETHODIMP CArchiveExtractCallback::SetOperationResult(Int32 operationResult)
    415 {
    416   COM_TRY_BEGIN
    417   switch(operationResult)
    418   {
    419     case NArchive::NExtract::NOperationResult::kOK:
    420     case NArchive::NExtract::NOperationResult::kUnSupportedMethod:
    421     case NArchive::NExtract::NOperationResult::kCRCError:
    422     case NArchive::NExtract::NOperationResult::kDataError:
    423       break;
    424     default:
    425       _outFileStream.Release();
    426       return E_FAIL;
    427   }
    428   if (_crcStream)
    429   {
    430     CrcSum += _crcStreamSpec->GetCRC();
    431     _curSize = _crcStreamSpec->GetSize();
    432     _curSizeDefined = true;
    433     _crcStream.Release();
    434   }
    435   if (_outFileStream)
    436   {
    437     _outFileStreamSpec->SetTime(
    438         (WriteCTime && _fi.CTimeDefined) ? &_fi.CTime : NULL,
    439         (WriteATime && _fi.ATimeDefined) ? &_fi.ATime : NULL,
    440         (WriteMTime && _fi.MTimeDefined) ? &_fi.MTime : (_arc->MTimeDefined ? &_arc->MTime : NULL));
    441     _curSize = _outFileStreamSpec->ProcessedSize;
    442     _curSizeDefined = true;
    443     RINOK(_outFileStreamSpec->Close());
    444     _outFileStream.Release();
    445   }
    446   if (!_curSizeDefined)
    447     GetUnpackSize();
    448   if (_curSizeDefined)
    449     UnpackSize += _curSize;
    450   if (_fi.IsDir)
    451     NumFolders++;
    452   else
    453     NumFiles++;
    454 
    455   if (_extractMode && _fi.AttribDefined)
    456     NFile::NDirectory::MySetFileAttributes(_diskFilePath, _fi.Attrib);
    457   RINOK(_extractCallback2->SetOperationResult(operationResult, _encrypted));
    458   return S_OK;
    459   COM_TRY_END
    460 }
    461 
    462 /*
    463 STDMETHODIMP CArchiveExtractCallback::GetInStream(
    464     const wchar_t *name, ISequentialInStream **inStream)
    465 {
    466   COM_TRY_BEGIN
    467   CInFileStream *inFile = new CInFileStream;
    468   CMyComPtr<ISequentialInStream> inStreamTemp = inFile;
    469   if (!inFile->Open(_srcDirectoryPrefix + name))
    470     return ::GetLastError();
    471   *inStream = inStreamTemp.Detach();
    472   return S_OK;
    473   COM_TRY_END
    474 }
    475 */
    476 
    477 STDMETHODIMP CArchiveExtractCallback::CryptoGetTextPassword(BSTR *password)
    478 {
    479   COM_TRY_BEGIN
    480   if (!_cryptoGetTextPassword)
    481   {
    482     RINOK(_extractCallback2.QueryInterface(IID_ICryptoGetTextPassword,
    483         &_cryptoGetTextPassword));
    484   }
    485   return _cryptoGetTextPassword->CryptoGetTextPassword(password);
    486   COM_TRY_END
    487 }
    488 
    489