Example #1
0
STDMETHODIMP CHandler::UpdateItems(ISequentialOutStream *outStream, UInt32 numItems,
    IArchiveUpdateCallback *updateCallback)
{
  COM_TRY_BEGIN

  const CArchiveDatabaseEx *db = 0;
  #ifdef _7Z_VOL
  if (_volumes.Size() > 1)
    return E_FAIL;
  const CVolume *volume = 0;
  if (_volumes.Size() == 1)
  {
    volume = &_volumes.Front();
    db = &volume->Database;
  }
  #else
  if (_inStream != 0)
    db = &_db;
  #endif

  CObjectVector<CUpdateItem> updateItems;
  
  for (UInt32 i = 0; i < numItems; i++)
  {
    Int32 newData, newProps;
    UInt32 indexInArchive;
    if (!updateCallback)
      return E_FAIL;
    RINOK(updateCallback->GetUpdateItemInfo(i, &newData, &newProps, &indexInArchive));
    CUpdateItem ui;
    ui.NewProps = IntToBool(newProps);
    ui.NewData = IntToBool(newData);
    ui.IndexInArchive = indexInArchive;
    ui.IndexInClient = i;
    ui.IsAnti = false;
    ui.Size = 0;

    if (ui.IndexInArchive != -1)
    {
      if (db == 0 || ui.IndexInArchive >= db->Files.Size())
        return E_INVALIDARG;
      const CFileItem &fi = db->Files[ui.IndexInArchive];
      ui.Name = fi.Name;
      ui.IsDir = fi.IsDir;
      ui.Size = fi.Size;
      ui.IsAnti = db->IsItemAnti(ui.IndexInArchive);
      
      ui.CTimeDefined = db->CTime.GetItem(ui.IndexInArchive, ui.CTime);
      ui.ATimeDefined = db->ATime.GetItem(ui.IndexInArchive, ui.ATime);
      ui.MTimeDefined = db->MTime.GetItem(ui.IndexInArchive, ui.MTime);
    }

    if (ui.NewProps)
    {
      bool nameIsDefined;
      bool folderStatusIsDefined;
      {
        NCOM::CPropVariant prop;
        RINOK(updateCallback->GetProperty(i, kpidAttrib, &prop));
        if (prop.vt == VT_EMPTY)
          ui.AttribDefined = false;
        else if (prop.vt != VT_UI4)
          return E_INVALIDARG;
        else
        {
          ui.Attrib = prop.ulVal;
          ui.AttribDefined = true;
        }
      }
      
      // we need MTime to sort files.
      RINOK(GetTime(updateCallback, i, WriteCTime, kpidCTime, ui.CTime, ui.CTimeDefined));
      RINOK(GetTime(updateCallback, i, WriteATime, kpidATime, ui.ATime, ui.ATimeDefined));
      RINOK(GetTime(updateCallback, i, true,       kpidMTime, ui.MTime, ui.MTimeDefined));

      {
        NCOM::CPropVariant prop;
        RINOK(updateCallback->GetProperty(i, kpidPath, &prop));
        if (prop.vt == VT_EMPTY)
          nameIsDefined = false;
        else if (prop.vt != VT_BSTR)
          return E_INVALIDARG;
        else
        {
          ui.Name = NItemName::MakeLegalName(prop.bstrVal);
          nameIsDefined = true;
        }
      }
      {
        NCOM::CPropVariant prop;
        RINOK(updateCallback->GetProperty(i, kpidIsDir, &prop));
        if (prop.vt == VT_EMPTY)
          folderStatusIsDefined = false;
        else if (prop.vt != VT_BOOL)
          return E_INVALIDARG;
        else
        {
          ui.IsDir = (prop.boolVal != VARIANT_FALSE);
          folderStatusIsDefined = true;
        }
      }

      {
        NCOM::CPropVariant prop;
        RINOK(updateCallback->GetProperty(i, kpidIsAnti, &prop));
        if (prop.vt == VT_EMPTY)
          ui.IsAnti = false;
        else if (prop.vt != VT_BOOL)
          return E_INVALIDARG;
        else
          ui.IsAnti = (prop.boolVal != VARIANT_FALSE);
      }

      if (ui.IsAnti)
      {
        ui.AttribDefined = false;

        ui.CTimeDefined = false;
        ui.ATimeDefined = false;
        ui.MTimeDefined = false;
        
        ui.Size = 0;
      }

      if (!folderStatusIsDefined && ui.AttribDefined)
        ui.SetDirStatusFromAttrib();
    }

    if (ui.NewData)
    {
      NCOM::CPropVariant prop;
      RINOK(updateCallback->GetProperty(i, kpidSize, &prop));
      if (prop.vt != VT_UI8)
        return E_INVALIDARG;
      ui.Size = (UInt64)prop.uhVal.QuadPart;
      if (ui.Size != 0 && ui.IsAnti)
        return E_INVALIDARG;
    }
    updateItems.Add(ui);
  }

  CCompressionMethodMode methodMode, headerMethod;
  RINOK(SetCompressionMethod(methodMode, headerMethod));
  #ifdef COMPRESS_MT
  methodMode.NumThreads = _numThreads;
  headerMethod.NumThreads = 1;
  #endif

  CMyComPtr<ICryptoGetTextPassword2> getPassword2;
  updateCallback->QueryInterface(IID_ICryptoGetTextPassword2, (void **)&getPassword2);

  if (getPassword2)
  {
    CMyComBSTR password;
    Int32 passwordIsDefined;
    RINOK(getPassword2->CryptoGetTextPassword2(&passwordIsDefined, &password));
    methodMode.PasswordIsDefined = IntToBool(passwordIsDefined);
    if (methodMode.PasswordIsDefined)
      methodMode.Password = password;
  }
  else
    methodMode.PasswordIsDefined = false;

  bool compressMainHeader = _compressHeaders;  // check it

  bool encryptHeaders = false;

  if (methodMode.PasswordIsDefined)
  {
    if (_encryptHeadersSpecified)
      encryptHeaders = _encryptHeaders;
    #ifndef _NO_CRYPTO
    else
      encryptHeaders = _passwordIsDefined;
    #endif
    compressMainHeader = true;
    if (encryptHeaders)
    {
      headerMethod.PasswordIsDefined = methodMode.PasswordIsDefined;
      headerMethod.Password = methodMode.Password;
    }
  }

  if (numItems < 2)
    compressMainHeader = false;

  CUpdateOptions options;
  options.Method = &methodMode;
  options.HeaderMethod = (_compressHeaders || encryptHeaders) ? &headerMethod : 0;
  options.UseFilters = _level != 0 && _autoFilter;
  options.MaxFilter = _level >= 8;

  options.HeaderOptions.CompressMainHeader = compressMainHeader;
  options.HeaderOptions.WriteCTime = WriteCTime;
  options.HeaderOptions.WriteATime = WriteATime;
  options.HeaderOptions.WriteMTime = WriteMTime;
  
  options.NumSolidFiles = _numSolidFiles;
  options.NumSolidBytes = _numSolidBytes;
  options.SolidExtension = _solidExtension;
  options.RemoveSfxBlock = _removeSfxBlock;
  options.VolumeMode = _volumeMode;

  COutArchive archive;
  CArchiveDatabase newDatabase;

  CMyComPtr<ICryptoGetTextPassword> getPassword;
  updateCallback->QueryInterface(IID_ICryptoGetTextPassword, (void **)&getPassword);
  
  HRESULT res = Update(
      EXTERNAL_CODECS_VARS
      #ifdef _7Z_VOL
      volume ? volume->Stream: 0,
      volume ? db : 0,
      #else
      _inStream,
      db,
      #endif
      updateItems,
      archive, newDatabase, outStream, updateCallback, options
      #ifndef _NO_CRYPTO
      , getPassword
      #endif
      );

  RINOK(res);

  updateItems.ClearAndFree();

  return archive.WriteDatabase(EXTERNAL_CODECS_VARS
      newDatabase, options.HeaderMethod, options.HeaderOptions);

  COM_TRY_END
}