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