tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

Extract.cpp (12459B)


      1 // Extract.cpp
      2 
      3 #include "StdAfx.h"
      4 
      5 #include "../../../../C/Sort.h"
      6 
      7 #include "../../../Common/StringConvert.h"
      8 
      9 #include "../../../Windows/FileDir.h"
     10 #include "../../../Windows/PropVariant.h"
     11 #include "../../../Windows/PropVariantConv.h"
     12 
     13 #include "../Common/ExtractingFilePath.h"
     14 
     15 #include "Extract.h"
     16 #include "SetProperties.h"
     17 
     18 using namespace NWindows;
     19 using namespace NFile;
     20 using namespace NDir;
     21 
     22 static HRESULT DecompressArchive(
     23    CCodecs *codecs,
     24    const CArchiveLink &arcLink,
     25    UInt64 packSize,
     26    const NWildcard::CCensorNode &wildcardCensor,
     27    const CExtractOptions &options,
     28    bool calcCrc,
     29    IExtractCallbackUI *callback,
     30    CArchiveExtractCallback *ecs,
     31    UString &errorMessage,
     32    UInt64 &stdInProcessed)
     33 {
     34  const CArc &arc = arcLink.Arcs.Back();
     35  stdInProcessed = 0;
     36  IInArchive *archive = arc.Archive;
     37  CRecordVector<UInt32> realIndices;
     38  
     39  UStringVector removePathParts;
     40 
     41  FString outDir = options.OutputDir;
     42  UString replaceName = arc.DefaultName;
     43  
     44  if (arcLink.Arcs.Size() > 1)
     45  {
     46    // Most "pe" archives have same name of archive subfile "[0]" or ".rsrc_1".
     47    // So it extracts different archives to one folder.
     48    // We will use top level archive name
     49    const CArc &arc0 = arcLink.Arcs[0];
     50    if (StringsAreEqualNoCase_Ascii(codecs->Formats[arc0.FormatIndex].Name, "pe"))
     51      replaceName = arc0.DefaultName;
     52  }
     53 
     54  outDir.Replace(FString("*"), us2fs(Get_Correct_FsFile_Name(replaceName)));
     55 
     56  bool elimIsPossible = false;
     57  UString elimPrefix; // only pure name without dir delimiter
     58  FString outDirReduced = outDir;
     59  
     60  if (options.ElimDup.Val && options.PathMode != NExtract::NPathMode::kAbsPaths)
     61  {
     62    UString dirPrefix;
     63    SplitPathToParts_Smart(fs2us(outDir), dirPrefix, elimPrefix);
     64    if (!elimPrefix.IsEmpty())
     65    {
     66      if (IsPathSepar(elimPrefix.Back()))
     67        elimPrefix.DeleteBack();
     68      if (!elimPrefix.IsEmpty())
     69      {
     70        outDirReduced = us2fs(dirPrefix);
     71        elimIsPossible = true;
     72      }
     73    }
     74  }
     75 
     76  bool allFilesAreAllowed = wildcardCensor.AreAllAllowed();
     77 
     78  if (!options.StdInMode)
     79  {
     80    UInt32 numItems;
     81    RINOK(archive->GetNumberOfItems(&numItems));
     82    
     83    CReadArcItem item;
     84 
     85    for (UInt32 i = 0; i < numItems; i++)
     86    {
     87      if (elimIsPossible || !allFilesAreAllowed)
     88      {
     89        RINOK(arc.GetItem(i, item));
     90      }
     91      else
     92      {
     93        #ifdef SUPPORT_ALT_STREAMS
     94        item.IsAltStream = false;
     95        if (!options.NtOptions.AltStreams.Val && arc.Ask_AltStream)
     96        {
     97          RINOK(Archive_IsItem_AltStream(arc.Archive, i, item.IsAltStream));
     98        }
     99        #endif
    100      }
    101 
    102      #ifdef SUPPORT_ALT_STREAMS
    103      if (!options.NtOptions.AltStreams.Val && item.IsAltStream)
    104        continue;
    105      #endif
    106      
    107      if (elimIsPossible)
    108      {
    109        const UString &s =
    110          #ifdef SUPPORT_ALT_STREAMS
    111            item.MainPath;
    112          #else
    113            item.Path;
    114          #endif
    115        if (!IsPath1PrefixedByPath2(s, elimPrefix))
    116          elimIsPossible = false;
    117        else
    118        {
    119          wchar_t c = s[elimPrefix.Len()];
    120          if (c == 0)
    121          {
    122            if (!item.MainIsDir)
    123              elimIsPossible = false;
    124          }
    125          else if (!IsPathSepar(c))
    126            elimIsPossible = false;
    127        }
    128      }
    129 
    130      if (!allFilesAreAllowed)
    131      {
    132        if (!CensorNode_CheckPath(wildcardCensor, item))
    133          continue;
    134      }
    135 
    136      realIndices.Add(i);
    137    }
    138    
    139    if (realIndices.Size() == 0)
    140    {
    141      callback->ThereAreNoFiles();
    142      return callback->ExtractResult(S_OK);
    143    }
    144  }
    145 
    146  if (elimIsPossible)
    147  {
    148    removePathParts.Add(elimPrefix);
    149    // outDir = outDirReduced;
    150  }
    151 
    152  #ifdef _WIN32
    153  // GetCorrectFullFsPath doesn't like "..".
    154  // outDir.TrimRight();
    155  // outDir = GetCorrectFullFsPath(outDir);
    156  #endif
    157 
    158  if (outDir.IsEmpty())
    159    outDir = "." STRING_PATH_SEPARATOR;
    160  /*
    161  #ifdef _WIN32
    162  else if (NName::IsAltPathPrefix(outDir)) {}
    163  #endif
    164  */
    165  else if (!CreateComplexDir(outDir))
    166  {
    167    HRESULT res = ::GetLastError();
    168    if (res == S_OK)
    169      res = E_FAIL;
    170    errorMessage = "Can not create output directory: ";
    171    errorMessage += fs2us(outDir);
    172    return res;
    173  }
    174 
    175  ecs->Init(
    176      options.NtOptions,
    177      options.StdInMode ? &wildcardCensor : NULL,
    178      &arc,
    179      callback,
    180      options.StdOutMode, options.TestMode,
    181      outDir,
    182      removePathParts, false,
    183      packSize);
    184 
    185  
    186  #ifdef SUPPORT_LINKS
    187  
    188  if (!options.StdInMode &&
    189      !options.TestMode &&
    190      options.NtOptions.HardLinks.Val)
    191  {
    192    RINOK(ecs->PrepareHardLinks(&realIndices));
    193  }
    194    
    195  #endif
    196 
    197  
    198  HRESULT result;
    199  Int32 testMode = (options.TestMode && !calcCrc) ? 1: 0;
    200 
    201  CArchiveExtractCallback_Closer ecsCloser(ecs);
    202 
    203  if (options.StdInMode)
    204  {
    205    result = archive->Extract(NULL, (UInt32)(Int32)-1, testMode, ecs);
    206    NCOM::CPropVariant prop;
    207    if (archive->GetArchiveProperty(kpidPhySize, &prop) == S_OK)
    208      ConvertPropVariantToUInt64(prop, stdInProcessed);
    209  }
    210  else
    211    result = archive->Extract(&realIndices.Front(), realIndices.Size(), testMode, ecs);
    212  
    213  HRESULT res2 = ecsCloser.Close();
    214  if (result == S_OK)
    215    result = res2;
    216 
    217  return callback->ExtractResult(result);
    218 }
    219 
    220 /* v9.31: BUG was fixed:
    221   Sorted list for file paths was sorted with case insensitive compare function.
    222   But FindInSorted function did binary search via case sensitive compare function */
    223 
    224 int Find_FileName_InSortedVector(const UStringVector &fileName, const UString &name)
    225 {
    226  unsigned left = 0, right = fileName.Size();
    227  while (left != right)
    228  {
    229    unsigned mid = (left + right) / 2;
    230    const UString &midValue = fileName[mid];
    231    int compare = CompareFileNames(name, midValue);
    232    if (compare == 0)
    233      return mid;
    234    if (compare < 0)
    235      right = mid;
    236    else
    237      left = mid + 1;
    238  }
    239  return -1;
    240 }
    241 
    242 HRESULT Extract(
    243    CCodecs *codecs,
    244    const CObjectVector<COpenType> &types,
    245    const CIntVector &excludedFormats,
    246    UStringVector &arcPaths, UStringVector &arcPathsFull,
    247    const NWildcard::CCensorNode &wildcardCensor,
    248    const CExtractOptions &options,
    249    IOpenCallbackUI *openCallback,
    250    IExtractCallbackUI *extractCallback,
    251    #ifndef _SFX
    252    IHashCalc *hash,
    253    #endif
    254    UString &errorMessage,
    255    CDecompressStat &st)
    256 {
    257  st.Clear();
    258  UInt64 totalPackSize = 0;
    259  CRecordVector<UInt64> arcSizes;
    260 
    261  unsigned numArcs = options.StdInMode ? 1 : arcPaths.Size();
    262 
    263  unsigned i;
    264  
    265  for (i = 0; i < numArcs; i++)
    266  {
    267    NFind::CFileInfo fi;
    268    fi.Size = 0;
    269    if (!options.StdInMode)
    270    {
    271      const FString &arcPath = us2fs(arcPaths[i]);
    272      if (!fi.Find(arcPath))
    273        throw "there is no such archive";
    274      if (fi.IsDir())
    275        throw "can't decompress folder";
    276    }
    277    arcSizes.Add(fi.Size);
    278    totalPackSize += fi.Size;
    279  }
    280 
    281  CBoolArr skipArcs(numArcs);
    282  for (i = 0; i < numArcs; i++)
    283    skipArcs[i] = false;
    284 
    285  CArchiveExtractCallback *ecs = new CArchiveExtractCallback;
    286  CMyComPtr<IArchiveExtractCallback> ec(ecs);
    287  bool multi = (numArcs > 1);
    288  ecs->InitForMulti(multi, options.PathMode, options.OverwriteMode,
    289      false // keepEmptyDirParts
    290      );
    291  #ifndef _SFX
    292  ecs->SetHashMethods(hash);
    293  #endif
    294 
    295  if (multi)
    296  {
    297    RINOK(extractCallback->SetTotal(totalPackSize));
    298  }
    299 
    300  UInt64 totalPackProcessed = 0;
    301  bool thereAreNotOpenArcs = false;
    302  
    303  for (i = 0; i < numArcs; i++)
    304  {
    305    if (skipArcs[i])
    306      continue;
    307 
    308    const UString &arcPath = arcPaths[i];
    309    NFind::CFileInfo fi;
    310    if (options.StdInMode)
    311    {
    312      fi.Size = 0;
    313      fi.Attrib = 0;
    314    }
    315    else
    316    {
    317      if (!fi.Find(us2fs(arcPath)) || fi.IsDir())
    318        throw "there is no such archive";
    319    }
    320 
    321    /*
    322    #ifndef _NO_CRYPTO
    323    openCallback->Open_Clear_PasswordWasAsked_Flag();
    324    #endif
    325    */
    326 
    327    RINOK(extractCallback->BeforeOpen(arcPath, options.TestMode));
    328    CArchiveLink arcLink;
    329 
    330    CObjectVector<COpenType> types2 = types;
    331    /*
    332    #ifndef _SFX
    333    if (types.IsEmpty())
    334    {
    335      int pos = arcPath.ReverseFind(L'.');
    336      if (pos >= 0)
    337      {
    338        UString s = arcPath.Ptr(pos + 1);
    339        int index = codecs->FindFormatForExtension(s);
    340        if (index >= 0 && s == L"001")
    341        {
    342          s = arcPath.Left(pos);
    343          pos = s.ReverseFind(L'.');
    344          if (pos >= 0)
    345          {
    346            int index2 = codecs->FindFormatForExtension(s.Ptr(pos + 1));
    347            if (index2 >= 0) // && s.CompareNoCase(L"rar") != 0
    348            {
    349              types2.Add(index2);
    350              types2.Add(index);
    351            }
    352          }
    353        }
    354      }
    355    }
    356    #endif
    357    */
    358 
    359    COpenOptions op;
    360    #ifndef _SFX
    361    op.props = &options.Properties;
    362    #endif
    363    op.codecs = codecs;
    364    op.types = &types2;
    365    op.excludedFormats = &excludedFormats;
    366    op.stdInMode = options.StdInMode;
    367    op.stream = NULL;
    368    op.filePath = arcPath;
    369 
    370    HRESULT result = arcLink.Open_Strict(op, openCallback);
    371 
    372    if (result == E_ABORT)
    373      return result;
    374 
    375    // arcLink.Set_ErrorsText();
    376    RINOK(extractCallback->OpenResult(codecs, arcLink, arcPath, result));
    377 
    378    if (result != S_OK)
    379    {
    380      thereAreNotOpenArcs = true;
    381      if (!options.StdInMode)
    382      {
    383        NFind::CFileInfo fi2;
    384        if (fi2.Find(us2fs(arcPath)))
    385          if (!fi2.IsDir())
    386            totalPackProcessed += fi2.Size;
    387      }
    388      continue;
    389    }
    390 
    391    if (!options.StdInMode)
    392    {
    393      // numVolumes += arcLink.VolumePaths.Size();
    394      // arcLink.VolumesSize;
    395 
    396      // totalPackSize -= DeleteUsedFileNamesFromList(arcLink, i + 1, arcPaths, arcPathsFull, &arcSizes);
    397      // numArcs = arcPaths.Size();
    398      if (arcLink.VolumePaths.Size() != 0)
    399      {
    400        Int64 correctionSize = arcLink.VolumesSize;
    401        FOR_VECTOR (v, arcLink.VolumePaths)
    402        {
    403          int index = Find_FileName_InSortedVector(arcPathsFull, arcLink.VolumePaths[v]);
    404          if (index >= 0)
    405          {
    406            if ((unsigned)index > i)
    407            {
    408              skipArcs[(unsigned)index] = true;
    409              correctionSize -= arcSizes[(unsigned)index];
    410            }
    411          }
    412        }
    413        if (correctionSize != 0)
    414        {
    415          Int64 newPackSize = (Int64)totalPackSize + correctionSize;
    416          if (newPackSize < 0)
    417            newPackSize = 0;
    418          totalPackSize = newPackSize;
    419          RINOK(extractCallback->SetTotal(totalPackSize));
    420        }
    421      }
    422    }
    423 
    424    /*
    425    // Now openCallback and extractCallback use same object. So we don't need to send password.
    426 
    427    #ifndef _NO_CRYPTO
    428    bool passwordIsDefined;
    429    UString password;
    430    RINOK(openCallback->Open_GetPasswordIfAny(passwordIsDefined, password));
    431    if (passwordIsDefined)
    432    {
    433      RINOK(extractCallback->SetPassword(password));
    434    }
    435    #endif
    436    */
    437 
    438    CArc &arc = arcLink.Arcs.Back();
    439    arc.MTimeDefined = (!options.StdInMode && !fi.IsDevice);
    440    arc.MTime = fi.MTime;
    441 
    442    UInt64 packProcessed;
    443    bool calcCrc =
    444        #ifndef _SFX
    445          (hash != NULL);
    446        #else
    447          false;
    448        #endif
    449 
    450    RINOK(DecompressArchive(
    451        codecs,
    452        arcLink,
    453        fi.Size + arcLink.VolumesSize,
    454        wildcardCensor,
    455        options,
    456        calcCrc,
    457        extractCallback, ecs, errorMessage, packProcessed));
    458 
    459    if (!options.StdInMode)
    460      packProcessed = fi.Size + arcLink.VolumesSize;
    461    totalPackProcessed += packProcessed;
    462    ecs->LocalProgressSpec->InSize += packProcessed;
    463    ecs->LocalProgressSpec->OutSize = ecs->UnpackSize;
    464    if (!errorMessage.IsEmpty())
    465      return E_FAIL;
    466  }
    467 
    468  if (multi || thereAreNotOpenArcs)
    469  {
    470    RINOK(extractCallback->SetTotal(totalPackSize));
    471    RINOK(extractCallback->SetCompleted(&totalPackProcessed));
    472  }
    473 
    474  st.NumFolders = ecs->NumFolders;
    475  st.NumFiles = ecs->NumFiles;
    476  st.NumAltStreams = ecs->NumAltStreams;
    477  st.UnpackSize = ecs->UnpackSize;
    478  st.AltStreams_UnpackSize = ecs->AltStreams_UnpackSize;
    479  st.NumArchives = arcPaths.Size();
    480  st.PackSize = ecs->LocalProgressSpec->InSize;
    481  return S_OK;
    482 }