Home | History | Annotate | Download | only in 7z
      1 // 7zHandlerOut.cpp
      2 
      3 #include "StdAfx.h"
      4 
      5 #include "../../../Windows/PropVariant.h"
      6 
      7 #include "../../../Common/ComTry.h"
      8 #include "../../../Common/StringToInt.h"
      9 
     10 #include "../../ICoder.h"
     11 
     12 #include "../Common/ItemNameUtils.h"
     13 #include "../Common/ParseProperties.h"
     14 
     15 #include "7zHandler.h"
     16 #include "7zOut.h"
     17 #include "7zUpdate.h"
     18 
     19 using namespace NWindows;
     20 
     21 namespace NArchive {
     22 namespace N7z {
     23 
     24 static const wchar_t *kLZMAMethodName = L"LZMA";
     25 static const wchar_t *kCopyMethod = L"Copy";
     26 static const wchar_t *kDefaultMethodName = kLZMAMethodName;
     27 
     28 static const UInt32 kLzmaAlgorithmX5 = 1;
     29 static const wchar_t *kLzmaMatchFinderForHeaders = L"BT2";
     30 static const UInt32 kDictionaryForHeaders =
     31   #ifdef UNDER_CE
     32   1 << 18
     33   #else
     34   1 << 20
     35   #endif
     36 ;
     37 static const UInt32 kNumFastBytesForHeaders = 273;
     38 static const UInt32 kAlgorithmForHeaders = kLzmaAlgorithmX5;
     39 
     40 static inline bool IsCopyMethod(const UString &methodName)
     41   { return (methodName.CompareNoCase(kCopyMethod) == 0); }
     42 
     43 STDMETHODIMP CHandler::GetFileTimeType(UInt32 *type)
     44 {
     45   *type = NFileTimeType::kWindows;
     46   return S_OK;
     47 }
     48 
     49 HRESULT CHandler::SetCompressionMethod(
     50     CCompressionMethodMode &methodMode,
     51     CCompressionMethodMode &headerMethod)
     52 {
     53   HRESULT res = SetCompressionMethod(methodMode, _methods
     54   #ifndef _7ZIP_ST
     55   , _numThreads
     56   #endif
     57   );
     58   RINOK(res);
     59   methodMode.Binds = _binds;
     60 
     61   if (_compressHeaders)
     62   {
     63     // headerMethod.Methods.Add(methodMode.Methods.Back());
     64 
     65     CObjectVector<COneMethodInfo> headerMethodInfoVector;
     66     COneMethodInfo oneMethodInfo;
     67     oneMethodInfo.MethodName = kLZMAMethodName;
     68     {
     69       CProp prop;
     70       prop.Id = NCoderPropID::kMatchFinder;
     71       prop.Value = kLzmaMatchFinderForHeaders;
     72       oneMethodInfo.Props.Add(prop);
     73     }
     74     {
     75       CProp prop;
     76       prop.Id = NCoderPropID::kAlgorithm;
     77       prop.Value = kAlgorithmForHeaders;
     78       oneMethodInfo.Props.Add(prop);
     79     }
     80     {
     81       CProp prop;
     82       prop.Id = NCoderPropID::kNumFastBytes;
     83       prop.Value = (UInt32)kNumFastBytesForHeaders;
     84       oneMethodInfo.Props.Add(prop);
     85     }
     86     {
     87       CProp prop;
     88       prop.Id = NCoderPropID::kDictionarySize;
     89       prop.Value = (UInt32)kDictionaryForHeaders;
     90       oneMethodInfo.Props.Add(prop);
     91     }
     92     headerMethodInfoVector.Add(oneMethodInfo);
     93     HRESULT res = SetCompressionMethod(headerMethod, headerMethodInfoVector
     94       #ifndef _7ZIP_ST
     95       , 1
     96       #endif
     97     );
     98     RINOK(res);
     99   }
    100   return S_OK;
    101 }
    102 
    103 HRESULT CHandler::SetCompressionMethod(
    104     CCompressionMethodMode &methodMode,
    105     CObjectVector<COneMethodInfo> &methodsInfo
    106     #ifndef _7ZIP_ST
    107     , UInt32 numThreads
    108     #endif
    109     )
    110 {
    111   UInt32 level = _level;
    112 
    113   if (methodsInfo.IsEmpty())
    114   {
    115     COneMethodInfo oneMethodInfo;
    116     oneMethodInfo.MethodName = ((level == 0) ? kCopyMethod : kDefaultMethodName);
    117     methodsInfo.Add(oneMethodInfo);
    118   }
    119 
    120   bool needSolid = false;
    121   for(int i = 0; i < methodsInfo.Size(); i++)
    122   {
    123     COneMethodInfo &oneMethodInfo = methodsInfo[i];
    124     SetCompressionMethod2(oneMethodInfo
    125       #ifndef _7ZIP_ST
    126       , numThreads
    127       #endif
    128       );
    129 
    130     if (!IsCopyMethod(oneMethodInfo.MethodName))
    131       needSolid = true;
    132 
    133     CMethodFull methodFull;
    134 
    135     if (!FindMethod(
    136         EXTERNAL_CODECS_VARS
    137         oneMethodInfo.MethodName, methodFull.Id, methodFull.NumInStreams, methodFull.NumOutStreams))
    138       return E_INVALIDARG;
    139     methodFull.Props = oneMethodInfo.Props;
    140     methodMode.Methods.Add(methodFull);
    141 
    142     if (!_numSolidBytesDefined)
    143     {
    144       for (int j = 0; j < methodFull.Props.Size(); j++)
    145       {
    146         const CProp &prop = methodFull.Props[j];
    147         if ((prop.Id == NCoderPropID::kDictionarySize ||
    148              prop.Id == NCoderPropID::kUsedMemorySize) && prop.Value.vt == VT_UI4)
    149         {
    150           _numSolidBytes = ((UInt64)prop.Value.ulVal) << 7;
    151           const UInt64 kMinSize = (1 << 24);
    152           if (_numSolidBytes < kMinSize)
    153             _numSolidBytes = kMinSize;
    154           _numSolidBytesDefined = true;
    155           break;
    156         }
    157       }
    158     }
    159   }
    160 
    161   if (!needSolid && !_numSolidBytesDefined)
    162   {
    163     _numSolidBytesDefined = true;
    164     _numSolidBytes  = 0;
    165   }
    166   return S_OK;
    167 }
    168 
    169 static HRESULT GetTime(IArchiveUpdateCallback *updateCallback, int index, bool writeTime, PROPID propID, UInt64 &ft, bool &ftDefined)
    170 {
    171   ft = 0;
    172   ftDefined = false;
    173   if (!writeTime)
    174     return S_OK;
    175   NCOM::CPropVariant prop;
    176   RINOK(updateCallback->GetProperty(index, propID, &prop));
    177   if (prop.vt == VT_FILETIME)
    178   {
    179     ft = prop.filetime.dwLowDateTime | ((UInt64)prop.filetime.dwHighDateTime << 32);
    180     ftDefined = true;
    181   }
    182   else if (prop.vt != VT_EMPTY)
    183     return E_INVALIDARG;
    184   return S_OK;
    185 }
    186 
    187 STDMETHODIMP CHandler::UpdateItems(ISequentialOutStream *outStream, UInt32 numItems,
    188     IArchiveUpdateCallback *updateCallback)
    189 {
    190   COM_TRY_BEGIN
    191 
    192   const CArchiveDatabaseEx *db = 0;
    193   #ifdef _7Z_VOL
    194   if (_volumes.Size() > 1)
    195     return E_FAIL;
    196   const CVolume *volume = 0;
    197   if (_volumes.Size() == 1)
    198   {
    199     volume = &_volumes.Front();
    200     db = &volume->Database;
    201   }
    202   #else
    203   if (_inStream != 0)
    204     db = &_db;
    205   #endif
    206 
    207   CObjectVector<CUpdateItem> updateItems;
    208 
    209   for (UInt32 i = 0; i < numItems; i++)
    210   {
    211     Int32 newData, newProps;
    212     UInt32 indexInArchive;
    213     if (!updateCallback)
    214       return E_FAIL;
    215     RINOK(updateCallback->GetUpdateItemInfo(i, &newData, &newProps, &indexInArchive));
    216     CUpdateItem ui;
    217     ui.NewProps = IntToBool(newProps);
    218     ui.NewData = IntToBool(newData);
    219     ui.IndexInArchive = indexInArchive;
    220     ui.IndexInClient = i;
    221     ui.IsAnti = false;
    222     ui.Size = 0;
    223 
    224     if (ui.IndexInArchive != -1)
    225     {
    226       if (db == 0 || ui.IndexInArchive >= db->Files.Size())
    227         return E_INVALIDARG;
    228       const CFileItem &fi = db->Files[ui.IndexInArchive];
    229       ui.Name = fi.Name;
    230       ui.IsDir = fi.IsDir;
    231       ui.Size = fi.Size;
    232       ui.IsAnti = db->IsItemAnti(ui.IndexInArchive);
    233 
    234       ui.CTimeDefined = db->CTime.GetItem(ui.IndexInArchive, ui.CTime);
    235       ui.ATimeDefined = db->ATime.GetItem(ui.IndexInArchive, ui.ATime);
    236       ui.MTimeDefined = db->MTime.GetItem(ui.IndexInArchive, ui.MTime);
    237     }
    238 
    239     if (ui.NewProps)
    240     {
    241       bool nameIsDefined;
    242       bool folderStatusIsDefined;
    243       {
    244         NCOM::CPropVariant prop;
    245         RINOK(updateCallback->GetProperty(i, kpidAttrib, &prop));
    246         if (prop.vt == VT_EMPTY)
    247           ui.AttribDefined = false;
    248         else if (prop.vt != VT_UI4)
    249           return E_INVALIDARG;
    250         else
    251         {
    252           ui.Attrib = prop.ulVal;
    253           ui.AttribDefined = true;
    254         }
    255       }
    256 
    257       // we need MTime to sort files.
    258       RINOK(GetTime(updateCallback, i, WriteCTime, kpidCTime, ui.CTime, ui.CTimeDefined));
    259       RINOK(GetTime(updateCallback, i, WriteATime, kpidATime, ui.ATime, ui.ATimeDefined));
    260       RINOK(GetTime(updateCallback, i, true,       kpidMTime, ui.MTime, ui.MTimeDefined));
    261 
    262       {
    263         NCOM::CPropVariant prop;
    264         RINOK(updateCallback->GetProperty(i, kpidPath, &prop));
    265         if (prop.vt == VT_EMPTY)
    266           nameIsDefined = false;
    267         else if (prop.vt != VT_BSTR)
    268           return E_INVALIDARG;
    269         else
    270         {
    271           ui.Name = NItemName::MakeLegalName(prop.bstrVal);
    272           nameIsDefined = true;
    273         }
    274       }
    275       {
    276         NCOM::CPropVariant prop;
    277         RINOK(updateCallback->GetProperty(i, kpidIsDir, &prop));
    278         if (prop.vt == VT_EMPTY)
    279           folderStatusIsDefined = false;
    280         else if (prop.vt != VT_BOOL)
    281           return E_INVALIDARG;
    282         else
    283         {
    284           ui.IsDir = (prop.boolVal != VARIANT_FALSE);
    285           folderStatusIsDefined = true;
    286         }
    287       }
    288 
    289       {
    290         NCOM::CPropVariant prop;
    291         RINOK(updateCallback->GetProperty(i, kpidIsAnti, &prop));
    292         if (prop.vt == VT_EMPTY)
    293           ui.IsAnti = false;
    294         else if (prop.vt != VT_BOOL)
    295           return E_INVALIDARG;
    296         else
    297           ui.IsAnti = (prop.boolVal != VARIANT_FALSE);
    298       }
    299 
    300       if (ui.IsAnti)
    301       {
    302         ui.AttribDefined = false;
    303 
    304         ui.CTimeDefined = false;
    305         ui.ATimeDefined = false;
    306         ui.MTimeDefined = false;
    307 
    308         ui.Size = 0;
    309       }
    310 
    311       if (!folderStatusIsDefined && ui.AttribDefined)
    312         ui.SetDirStatusFromAttrib();
    313     }
    314 
    315     if (ui.NewData)
    316     {
    317       NCOM::CPropVariant prop;
    318       RINOK(updateCallback->GetProperty(i, kpidSize, &prop));
    319       if (prop.vt != VT_UI8)
    320         return E_INVALIDARG;
    321       ui.Size = (UInt64)prop.uhVal.QuadPart;
    322       if (ui.Size != 0 && ui.IsAnti)
    323         return E_INVALIDARG;
    324     }
    325     updateItems.Add(ui);
    326   }
    327 
    328   CCompressionMethodMode methodMode, headerMethod;
    329   RINOK(SetCompressionMethod(methodMode, headerMethod));
    330   #ifndef _7ZIP_ST
    331   methodMode.NumThreads = _numThreads;
    332   headerMethod.NumThreads = 1;
    333   #endif
    334 
    335   CMyComPtr<ICryptoGetTextPassword2> getPassword2;
    336   updateCallback->QueryInterface(IID_ICryptoGetTextPassword2, (void **)&getPassword2);
    337 
    338   if (getPassword2)
    339   {
    340     CMyComBSTR password;
    341     Int32 passwordIsDefined;
    342     RINOK(getPassword2->CryptoGetTextPassword2(&passwordIsDefined, &password));
    343     methodMode.PasswordIsDefined = IntToBool(passwordIsDefined);
    344     if (methodMode.PasswordIsDefined)
    345       methodMode.Password = password;
    346   }
    347   else
    348     methodMode.PasswordIsDefined = false;
    349 
    350   bool compressMainHeader = _compressHeaders;  // check it
    351 
    352   bool encryptHeaders = false;
    353 
    354   if (methodMode.PasswordIsDefined)
    355   {
    356     if (_encryptHeadersSpecified)
    357       encryptHeaders = _encryptHeaders;
    358     #ifndef _NO_CRYPTO
    359     else
    360       encryptHeaders = _passwordIsDefined;
    361     #endif
    362     compressMainHeader = true;
    363     if (encryptHeaders)
    364     {
    365       headerMethod.PasswordIsDefined = methodMode.PasswordIsDefined;
    366       headerMethod.Password = methodMode.Password;
    367     }
    368   }
    369 
    370   if (numItems < 2)
    371     compressMainHeader = false;
    372 
    373   CUpdateOptions options;
    374   options.Method = &methodMode;
    375   options.HeaderMethod = (_compressHeaders || encryptHeaders) ? &headerMethod : 0;
    376   options.UseFilters = _level != 0 && _autoFilter;
    377   options.MaxFilter = _level >= 8;
    378 
    379   options.HeaderOptions.CompressMainHeader = compressMainHeader;
    380   options.HeaderOptions.WriteCTime = WriteCTime;
    381   options.HeaderOptions.WriteATime = WriteATime;
    382   options.HeaderOptions.WriteMTime = WriteMTime;
    383 
    384   options.NumSolidFiles = _numSolidFiles;
    385   options.NumSolidBytes = _numSolidBytes;
    386   options.SolidExtension = _solidExtension;
    387   options.RemoveSfxBlock = _removeSfxBlock;
    388   options.VolumeMode = _volumeMode;
    389 
    390   COutArchive archive;
    391   CArchiveDatabase newDatabase;
    392 
    393   CMyComPtr<ICryptoGetTextPassword> getPassword;
    394   updateCallback->QueryInterface(IID_ICryptoGetTextPassword, (void **)&getPassword);
    395 
    396   HRESULT res = Update(
    397       EXTERNAL_CODECS_VARS
    398       #ifdef _7Z_VOL
    399       volume ? volume->Stream: 0,
    400       volume ? db : 0,
    401       #else
    402       _inStream,
    403       db,
    404       #endif
    405       updateItems,
    406       archive, newDatabase, outStream, updateCallback, options
    407       #ifndef _NO_CRYPTO
    408       , getPassword
    409       #endif
    410       );
    411 
    412   RINOK(res);
    413 
    414   updateItems.ClearAndFree();
    415 
    416   return archive.WriteDatabase(EXTERNAL_CODECS_VARS
    417       newDatabase, options.HeaderMethod, options.HeaderOptions);
    418 
    419   COM_TRY_END
    420 }
    421 
    422 static HRESULT GetBindInfoPart(UString &srcString, UInt32 &coder, UInt32 &stream)
    423 {
    424   stream = 0;
    425   int index = ParseStringToUInt32(srcString, coder);
    426   if (index == 0)
    427     return E_INVALIDARG;
    428   srcString.Delete(0, index);
    429   if (srcString[0] == 'S')
    430   {
    431     srcString.Delete(0);
    432     int index = ParseStringToUInt32(srcString, stream);
    433     if (index == 0)
    434       return E_INVALIDARG;
    435     srcString.Delete(0, index);
    436   }
    437   return S_OK;
    438 }
    439 
    440 static HRESULT GetBindInfo(UString &srcString, CBind &bind)
    441 {
    442   RINOK(GetBindInfoPart(srcString, bind.OutCoder, bind.OutStream));
    443   if (srcString[0] != ':')
    444     return E_INVALIDARG;
    445   srcString.Delete(0);
    446   RINOK(GetBindInfoPart(srcString, bind.InCoder, bind.InStream));
    447   if (!srcString.IsEmpty())
    448     return E_INVALIDARG;
    449   return S_OK;
    450 }
    451 
    452 STDMETHODIMP CHandler::SetProperties(const wchar_t **names, const PROPVARIANT *values, Int32 numProperties)
    453 {
    454   COM_TRY_BEGIN
    455   _binds.Clear();
    456   BeforeSetProperty();
    457 
    458   for (int i = 0; i < numProperties; i++)
    459   {
    460     UString name = names[i];
    461     name.MakeUpper();
    462     if (name.IsEmpty())
    463       return E_INVALIDARG;
    464 
    465     const PROPVARIANT &value = values[i];
    466 
    467     if (name[0] == 'B')
    468     {
    469       name.Delete(0);
    470       CBind bind;
    471       RINOK(GetBindInfo(name, bind));
    472       _binds.Add(bind);
    473       continue;
    474     }
    475 
    476     RINOK(SetProperty(name, value));
    477   }
    478 
    479   return S_OK;
    480   COM_TRY_END
    481 }
    482 
    483 }}
    484