tor-browser

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

Update.cpp (43747B)


      1 // Update.cpp
      2 
      3 #include "StdAfx.h"
      4 
      5 #include "Update.h"
      6 
      7 #include "../../../Common/StringConvert.h"
      8 
      9 #include "../../../Windows/DLL.h"
     10 #include "../../../Windows/FileDir.h"
     11 #include "../../../Windows/FileFind.h"
     12 #include "../../../Windows/FileName.h"
     13 #include "../../../Windows/PropVariant.h"
     14 #include "../../../Windows/PropVariantConv.h"
     15 #include "../../../Windows/TimeUtils.h"
     16 
     17 #include "../../Common/FileStreams.h"
     18 #include "../../Common/LimitedStreams.h"
     19 
     20 #include "../../Compress/CopyCoder.h"
     21 
     22 #include "../Common/DirItem.h"
     23 #include "../Common/EnumDirItems.h"
     24 #include "../Common/OpenArchive.h"
     25 #include "../Common/UpdateProduce.h"
     26 
     27 #include "EnumDirItems.h"
     28 #include "SetProperties.h"
     29 #include "TempFiles.h"
     30 #include "UpdateCallback.h"
     31 
     32 static const char * const kUpdateIsNotSupoorted =
     33  "update operations are not supported for this archive";
     34 
     35 static const char * const kUpdateIsNotSupoorted_MultiVol =
     36  "Updating for multivolume archives is not implemented";
     37 
     38 using namespace NWindows;
     39 using namespace NCOM;
     40 using namespace NFile;
     41 using namespace NDir;
     42 using namespace NName;
     43 
     44 static CFSTR const kTempFolderPrefix = FTEXT("7zE");
     45 
     46 
     47 void CUpdateErrorInfo::SetFromLastError(const char *message)
     48 {
     49  SystemError = ::GetLastError();
     50  Message = message;
     51 }
     52 
     53 HRESULT CUpdateErrorInfo::SetFromLastError(const char *message, const FString &fileName)
     54 {
     55  SetFromLastError(message);
     56  FileNames.Add(fileName);
     57  return Get_HRESULT_Error();
     58 }
     59 
     60 static bool DeleteEmptyFolderAndEmptySubFolders(const FString &path)
     61 {
     62  NFind::CFileInfo fileInfo;
     63  FString pathPrefix = path + FCHAR_PATH_SEPARATOR;
     64  {
     65    NFind::CEnumerator enumerator;
     66    enumerator.SetDirPrefix(pathPrefix);
     67    while (enumerator.Next(fileInfo))
     68    {
     69      if (fileInfo.IsDir())
     70        if (!DeleteEmptyFolderAndEmptySubFolders(pathPrefix + fileInfo.Name))
     71          return false;
     72    }
     73  }
     74  /*
     75  // we don't need clear read-only for folders
     76  if (!MySetFileAttributes(path, 0))
     77    return false;
     78  */
     79  return RemoveDir(path);
     80 }
     81 
     82 
     83 using namespace NUpdateArchive;
     84 
     85 class COutMultiVolStream:
     86  public IOutStream,
     87  public CMyUnknownImp
     88 {
     89  unsigned _streamIndex; // required stream
     90  UInt64 _offsetPos; // offset from start of _streamIndex index
     91  UInt64 _absPos;
     92  UInt64 _length;
     93 
     94  struct CAltStreamInfo
     95  {
     96    COutFileStream *StreamSpec;
     97    CMyComPtr<IOutStream> Stream;
     98    FString Name;
     99    UInt64 Pos;
    100    UInt64 RealSize;
    101  };
    102  CObjectVector<CAltStreamInfo> Streams;
    103 public:
    104  // CMyComPtr<IArchiveUpdateCallback2> VolumeCallback;
    105  CRecordVector<UInt64> Sizes;
    106  FString Prefix;
    107  CTempFiles *TempFiles;
    108 
    109  void Init()
    110  {
    111    _streamIndex = 0;
    112    _offsetPos = 0;
    113    _absPos = 0;
    114    _length = 0;
    115  }
    116 
    117  bool SetMTime(const FILETIME *mTime);
    118  HRESULT Close();
    119 
    120  UInt64 GetSize() const { return _length; }
    121 
    122  MY_UNKNOWN_IMP1(IOutStream)
    123 
    124  STDMETHOD(Write)(const void *data, UInt32 size, UInt32 *processedSize);
    125  STDMETHOD(Seek)(Int64 offset, UInt32 seekOrigin, UInt64 *newPosition);
    126  STDMETHOD(SetSize)(UInt64 newSize);
    127 };
    128 
    129 // static NSynchronization::CCriticalSection g_TempPathsCS;
    130 
    131 HRESULT COutMultiVolStream::Close()
    132 {
    133  HRESULT res = S_OK;
    134  FOR_VECTOR (i, Streams)
    135  {
    136    COutFileStream *s = Streams[i].StreamSpec;
    137    if (s)
    138    {
    139      HRESULT res2 = s->Close();
    140      if (res2 != S_OK)
    141        res = res2;
    142    }
    143  }
    144  return res;
    145 }
    146 
    147 bool COutMultiVolStream::SetMTime(const FILETIME *mTime)
    148 {
    149  bool res = true;
    150  FOR_VECTOR (i, Streams)
    151  {
    152    COutFileStream *s = Streams[i].StreamSpec;
    153    if (s)
    154      if (!s->SetMTime(mTime))
    155        res = false;
    156  }
    157  return res;
    158 }
    159 
    160 STDMETHODIMP COutMultiVolStream::Write(const void *data, UInt32 size, UInt32 *processedSize)
    161 {
    162  if (processedSize)
    163    *processedSize = 0;
    164  while (size > 0)
    165  {
    166    if (_streamIndex >= Streams.Size())
    167    {
    168      CAltStreamInfo altStream;
    169 
    170      FString name;
    171      name.Add_UInt32(_streamIndex + 1);
    172      while (name.Len() < 3)
    173        name.InsertAtFront(FTEXT('0'));
    174      name.Insert(0, Prefix);
    175      altStream.StreamSpec = new COutFileStream;
    176      altStream.Stream = altStream.StreamSpec;
    177      if (!altStream.StreamSpec->Create(name, false))
    178        return ::GetLastError();
    179      {
    180        // NSynchronization::CCriticalSectionLock lock(g_TempPathsCS);
    181        TempFiles->Paths.Add(name);
    182      }
    183 
    184      altStream.Pos = 0;
    185      altStream.RealSize = 0;
    186      altStream.Name = name;
    187      Streams.Add(altStream);
    188      continue;
    189    }
    190    CAltStreamInfo &altStream = Streams[_streamIndex];
    191 
    192    unsigned index = _streamIndex;
    193    if (index >= Sizes.Size())
    194      index = Sizes.Size() - 1;
    195    UInt64 volSize = Sizes[index];
    196 
    197    if (_offsetPos >= volSize)
    198    {
    199      _offsetPos -= volSize;
    200      _streamIndex++;
    201      continue;
    202    }
    203    if (_offsetPos != altStream.Pos)
    204    {
    205      // CMyComPtr<IOutStream> outStream;
    206      // RINOK(altStream.Stream.QueryInterface(IID_IOutStream, &outStream));
    207      RINOK(altStream.Stream->Seek(_offsetPos, STREAM_SEEK_SET, NULL));
    208      altStream.Pos = _offsetPos;
    209    }
    210 
    211    UInt32 curSize = (UInt32)MyMin((UInt64)size, volSize - altStream.Pos);
    212    UInt32 realProcessed;
    213    RINOK(altStream.Stream->Write(data, curSize, &realProcessed));
    214    data = (void *)((Byte *)data + realProcessed);
    215    size -= realProcessed;
    216    altStream.Pos += realProcessed;
    217    _offsetPos += realProcessed;
    218    _absPos += realProcessed;
    219    if (_absPos > _length)
    220      _length = _absPos;
    221    if (_offsetPos > altStream.RealSize)
    222      altStream.RealSize = _offsetPos;
    223    if (processedSize)
    224      *processedSize += realProcessed;
    225    if (altStream.Pos == volSize)
    226    {
    227      _streamIndex++;
    228      _offsetPos = 0;
    229    }
    230    if (realProcessed == 0 && curSize != 0)
    231      return E_FAIL;
    232    break;
    233  }
    234  return S_OK;
    235 }
    236 
    237 STDMETHODIMP COutMultiVolStream::Seek(Int64 offset, UInt32 seekOrigin, UInt64 *newPosition)
    238 {
    239  if (seekOrigin >= 3)
    240    return STG_E_INVALIDFUNCTION;
    241  switch (seekOrigin)
    242  {
    243    case STREAM_SEEK_SET: _absPos = offset; break;
    244    case STREAM_SEEK_CUR: _absPos += offset; break;
    245    case STREAM_SEEK_END: _absPos = _length + offset; break;
    246  }
    247  _offsetPos = _absPos;
    248  if (newPosition)
    249    *newPosition = _absPos;
    250  _streamIndex = 0;
    251  return S_OK;
    252 }
    253 
    254 STDMETHODIMP COutMultiVolStream::SetSize(UInt64 newSize)
    255 {
    256  unsigned i = 0;
    257  while (i < Streams.Size())
    258  {
    259    CAltStreamInfo &altStream = Streams[i++];
    260    if ((UInt64)newSize < altStream.RealSize)
    261    {
    262      RINOK(altStream.Stream->SetSize(newSize));
    263      altStream.RealSize = newSize;
    264      break;
    265    }
    266    newSize -= altStream.RealSize;
    267  }
    268  while (i < Streams.Size())
    269  {
    270    {
    271      CAltStreamInfo &altStream = Streams.Back();
    272      altStream.Stream.Release();
    273      DeleteFileAlways(altStream.Name);
    274    }
    275    Streams.DeleteBack();
    276  }
    277  _offsetPos = _absPos;
    278  _streamIndex = 0;
    279  _length = newSize;
    280  return S_OK;
    281 }
    282 
    283 void CArchivePath::ParseFromPath(const UString &path, EArcNameMode mode)
    284 {
    285  OriginalPath = path;
    286  
    287  SplitPathToParts_2(path, Prefix, Name);
    288  
    289  if (mode == k_ArcNameMode_Add)
    290    return;
    291  if (mode == k_ArcNameMode_Exact)
    292  {
    293    BaseExtension.Empty();
    294    return;
    295  }
    296  
    297  int dotPos = Name.ReverseFind_Dot();
    298  if (dotPos < 0)
    299    return;
    300  if ((unsigned)dotPos == Name.Len() - 1)
    301  {
    302    Name.DeleteBack();
    303    BaseExtension.Empty();
    304    return;
    305  }
    306  const UString ext = Name.Ptr(dotPos + 1);
    307  if (BaseExtension.IsEqualTo_NoCase(ext))
    308  {
    309    BaseExtension = ext;
    310    Name.DeleteFrom(dotPos);
    311  }
    312  else
    313    BaseExtension.Empty();
    314 }
    315 
    316 UString CArchivePath::GetFinalPath() const
    317 {
    318  UString path = GetPathWithoutExt();
    319  if (!BaseExtension.IsEmpty())
    320  {
    321    path += '.';
    322    path += BaseExtension;
    323  }
    324  return path;
    325 }
    326 
    327 UString CArchivePath::GetFinalVolPath() const
    328 {
    329  UString path = GetPathWithoutExt();
    330  if (!BaseExtension.IsEmpty())
    331  {
    332    path += '.';
    333    path += VolExtension;
    334  }
    335  return path;
    336 }
    337 
    338 FString CArchivePath::GetTempPath() const
    339 {
    340  FString path = TempPrefix;
    341  path += us2fs(Name);
    342  if (!BaseExtension.IsEmpty())
    343  {
    344    path += '.';
    345    path += us2fs(BaseExtension);
    346  }
    347  path += ".tmp";
    348  path += TempPostfix;
    349  return path;
    350 }
    351 
    352 static const char * const kDefaultArcType = "7z";
    353 static const char * const kDefaultArcExt = "7z";
    354 static const char * const kSFXExtension =
    355  #ifdef _WIN32
    356    "exe";
    357  #else
    358    "";
    359  #endif
    360 
    361 bool CUpdateOptions::InitFormatIndex(const CCodecs *codecs,
    362    const CObjectVector<COpenType> &types, const UString &arcPath)
    363 {
    364  if (types.Size() > 1)
    365    return false;
    366  // int arcTypeIndex = -1;
    367  if (types.Size() != 0)
    368  {
    369    MethodMode.Type = types[0];
    370    MethodMode.Type_Defined = true;
    371  }
    372  if (MethodMode.Type.FormatIndex < 0)
    373  {
    374    // MethodMode.Type = -1;
    375    MethodMode.Type = COpenType();
    376    if (ArcNameMode != k_ArcNameMode_Add)
    377    {
    378      MethodMode.Type.FormatIndex = codecs->FindFormatForArchiveName(arcPath);
    379      if (MethodMode.Type.FormatIndex >= 0)
    380        MethodMode.Type_Defined = true;
    381    }
    382  }
    383  return true;
    384 }
    385 
    386 bool CUpdateOptions::SetArcPath(const CCodecs *codecs, const UString &arcPath)
    387 {
    388  UString typeExt;
    389  int formatIndex = MethodMode.Type.FormatIndex;
    390  if (formatIndex < 0)
    391  {
    392    typeExt = kDefaultArcExt;
    393  }
    394  else
    395  {
    396    const CArcInfoEx &arcInfo = codecs->Formats[formatIndex];
    397    if (!arcInfo.UpdateEnabled)
    398      return false;
    399    typeExt = arcInfo.GetMainExt();
    400  }
    401  UString ext = typeExt;
    402  if (SfxMode)
    403    ext = kSFXExtension;
    404  ArchivePath.BaseExtension = ext;
    405  ArchivePath.VolExtension = typeExt;
    406  ArchivePath.ParseFromPath(arcPath, ArcNameMode);
    407  FOR_VECTOR (i, Commands)
    408  {
    409    CUpdateArchiveCommand &uc = Commands[i];
    410    uc.ArchivePath.BaseExtension = ext;
    411    uc.ArchivePath.VolExtension = typeExt;
    412    uc.ArchivePath.ParseFromPath(uc.UserArchivePath, ArcNameMode);
    413  }
    414  return true;
    415 }
    416 
    417 
    418 struct CUpdateProduceCallbackImp: public IUpdateProduceCallback
    419 {
    420  const CObjectVector<CArcItem> *_arcItems;
    421  IUpdateCallbackUI *_callback;
    422  CDirItemsStat *_stat;
    423  
    424  CUpdateProduceCallbackImp(
    425      const CObjectVector<CArcItem> *a,
    426      CDirItemsStat *stat,
    427      IUpdateCallbackUI *callback):
    428    _arcItems(a),
    429    _stat(stat),
    430    _callback(callback) {}
    431  
    432  virtual HRESULT ShowDeleteFile(unsigned arcIndex);
    433 };
    434 
    435 
    436 HRESULT CUpdateProduceCallbackImp::ShowDeleteFile(unsigned arcIndex)
    437 {
    438  const CArcItem &ai = (*_arcItems)[arcIndex];
    439  {
    440    CDirItemsStat &stat = *_stat;
    441    if (ai.IsDir)
    442      stat.NumDirs++;
    443    else if (ai.IsAltStream)
    444    {
    445      stat.NumAltStreams++;
    446      stat.AltStreamsSize += ai.Size;
    447    }
    448    else
    449    {
    450      stat.NumFiles++;
    451      stat.FilesSize += ai.Size;
    452    }
    453  }
    454  return _callback->ShowDeleteFile(ai.Name, ai.IsDir);
    455 }
    456 
    457 bool CRenamePair::Prepare()
    458 {
    459  if (RecursedType != NRecursedType::kNonRecursed)
    460    return false;
    461  if (!WildcardParsing)
    462    return true;
    463  return !DoesNameContainWildcard(OldName);
    464 }
    465 
    466 extern bool g_CaseSensitive;
    467 
    468 static unsigned CompareTwoNames(const wchar_t *s1, const wchar_t *s2)
    469 {
    470  for (unsigned i = 0;; i++)
    471  {
    472    wchar_t c1 = s1[i];
    473    wchar_t c2 = s2[i];
    474    if (c1 == 0 || c2 == 0)
    475      return i;
    476    if (c1 == c2)
    477      continue;
    478    if (!g_CaseSensitive && (MyCharUpper(c1) == MyCharUpper(c2)))
    479      continue;
    480    if (IsPathSepar(c1) && IsPathSepar(c2))
    481      continue;
    482    return i;
    483  }
    484 }
    485 
    486 bool CRenamePair::GetNewPath(bool isFolder, const UString &src, UString &dest) const
    487 {
    488  unsigned num = CompareTwoNames(OldName, src);
    489  if (OldName[num] == 0)
    490  {
    491    if (src[num] != 0 && !IsPathSepar(src[num]) && num != 0 && !IsPathSepar(src[num - 1]))
    492      return false;
    493  }
    494  else
    495  {
    496    // OldName[num] != 0
    497    // OldName = "1\1a.txt"
    498    // src = "1"
    499 
    500    if (!isFolder
    501        || src[num] != 0
    502        || !IsPathSepar(OldName[num])
    503        || OldName[num + 1] != 0)
    504      return false;
    505  }
    506  dest = NewName + src.Ptr(num);
    507  return true;
    508 }
    509 
    510 #ifdef SUPPORT_ALT_STREAMS
    511 int FindAltStreamColon_in_Path(const wchar_t *path);
    512 #endif
    513 
    514 static HRESULT Compress(
    515    const CUpdateOptions &options,
    516    bool isUpdatingItself,
    517    CCodecs *codecs,
    518    const CActionSet &actionSet,
    519    const CArc *arc,
    520    CArchivePath &archivePath,
    521    const CObjectVector<CArcItem> &arcItems,
    522    Byte *processedItemsStatuses,
    523    const CDirItems &dirItems,
    524    const CDirItem *parentDirItem,
    525    CTempFiles &tempFiles,
    526    CUpdateErrorInfo &errorInfo,
    527    IUpdateCallbackUI *callback,
    528    CFinishArchiveStat &st)
    529 {
    530  CMyComPtr<IOutArchive> outArchive;
    531  int formatIndex = options.MethodMode.Type.FormatIndex;
    532  
    533  if (arc)
    534  {
    535    formatIndex = arc->FormatIndex;
    536    if (formatIndex < 0)
    537      return E_NOTIMPL;
    538    CMyComPtr<IInArchive> archive2 = arc->Archive;
    539    HRESULT result = archive2.QueryInterface(IID_IOutArchive, &outArchive);
    540    if (result != S_OK)
    541      throw kUpdateIsNotSupoorted;
    542  }
    543  else
    544  {
    545    RINOK(codecs->CreateOutArchive(formatIndex, outArchive));
    546 
    547    #ifdef EXTERNAL_CODECS
    548    {
    549      CMyComPtr<ISetCompressCodecsInfo> setCompressCodecsInfo;
    550      outArchive.QueryInterface(IID_ISetCompressCodecsInfo, (void **)&setCompressCodecsInfo);
    551      if (setCompressCodecsInfo)
    552      {
    553        RINOK(setCompressCodecsInfo->SetCompressCodecsInfo(codecs));
    554      }
    555    }
    556    #endif
    557  }
    558  
    559  if (outArchive == 0)
    560    throw kUpdateIsNotSupoorted;
    561  
    562  NFileTimeType::EEnum fileTimeType;
    563  {
    564    UInt32 value;
    565    RINOK(outArchive->GetFileTimeType(&value));
    566    
    567    switch (value)
    568    {
    569      case NFileTimeType::kWindows:
    570      case NFileTimeType::kUnix:
    571      case NFileTimeType::kDOS:
    572        fileTimeType = (NFileTimeType::EEnum)value;
    573        break;
    574      default:
    575        return E_FAIL;
    576    }
    577  }
    578 
    579  {
    580    const CArcInfoEx &arcInfo = codecs->Formats[formatIndex];
    581    if (options.AltStreams.Val && !arcInfo.Flags_AltStreams())
    582      return E_NOTIMPL;
    583    if (options.NtSecurity.Val && !arcInfo.Flags_NtSecure())
    584      return E_NOTIMPL;
    585  }
    586 
    587  CRecordVector<CUpdatePair2> updatePairs2;
    588 
    589  UStringVector newNames;
    590 
    591  CArcToDoStat stat2;
    592 
    593  if (options.RenamePairs.Size() != 0)
    594  {
    595    FOR_VECTOR (i, arcItems)
    596    {
    597      const CArcItem &ai = arcItems[i];
    598      bool needRename = false;
    599      UString dest;
    600      
    601      if (ai.Censored)
    602      {
    603        FOR_VECTOR (j, options.RenamePairs)
    604        {
    605          const CRenamePair &rp = options.RenamePairs[j];
    606          if (rp.GetNewPath(ai.IsDir, ai.Name, dest))
    607          {
    608            needRename = true;
    609            break;
    610          }
    611          
    612          #ifdef SUPPORT_ALT_STREAMS
    613          if (ai.IsAltStream)
    614          {
    615            int colonPos = FindAltStreamColon_in_Path(ai.Name);
    616            if (colonPos >= 0)
    617            {
    618              UString mainName = ai.Name.Left(colonPos);
    619              /*
    620              actually we must improve that code to support cases
    621              with folder renaming like: rn arc dir1\ dir2\
    622              */
    623              if (rp.GetNewPath(false, mainName, dest))
    624              {
    625                needRename = true;
    626                dest += ':';
    627                dest += ai.Name.Ptr(colonPos + 1);
    628                break;
    629              }
    630            }
    631          }
    632          #endif
    633        }
    634      }
    635      
    636      CUpdatePair2 up2;
    637      up2.SetAs_NoChangeArcItem(ai.IndexInServer);
    638      if (needRename)
    639      {
    640        up2.NewProps = true;
    641        RINOK(arc->IsItemAnti(i, up2.IsAnti));
    642        up2.NewNameIndex = newNames.Add(dest);
    643      }
    644      updatePairs2.Add(up2);
    645    }
    646  }
    647  else
    648  {
    649    CRecordVector<CUpdatePair> updatePairs;
    650    GetUpdatePairInfoList(dirItems, arcItems, fileTimeType, updatePairs); // must be done only once!!!
    651    CUpdateProduceCallbackImp upCallback(&arcItems, &stat2.DeleteData, callback);
    652    
    653    UpdateProduce(updatePairs, actionSet, updatePairs2, isUpdatingItself ? &upCallback : NULL);
    654  }
    655 
    656  {
    657    FOR_VECTOR (i, updatePairs2)
    658    {
    659      const CUpdatePair2 &up = updatePairs2[i];
    660 
    661      // 17.01: anti-item is (up.NewData && (p.UseArcProps in most cases))
    662 
    663      if (up.NewData && !up.UseArcProps)
    664      {
    665        if (up.ExistOnDisk())
    666        {
    667          CDirItemsStat2 &stat = stat2.NewData;
    668          const CDirItem &di = dirItems.Items[up.DirIndex];
    669          if (di.IsDir())
    670          {
    671            if (up.IsAnti)
    672              stat.Anti_NumDirs++;
    673            else
    674              stat.NumDirs++;
    675          }
    676          else if (di.IsAltStream)
    677          {
    678            if (up.IsAnti)
    679              stat.Anti_NumAltStreams++;
    680            else
    681            {
    682              stat.NumAltStreams++;
    683              stat.AltStreamsSize += di.Size;
    684            }
    685          }
    686          else
    687          {
    688            if (up.IsAnti)
    689              stat.Anti_NumFiles++;
    690            else
    691            {
    692              stat.NumFiles++;
    693              stat.FilesSize += di.Size;
    694            }
    695          }
    696        }
    697      }
    698      else if (up.ArcIndex >= 0)
    699      {
    700        CDirItemsStat2 &stat = *(up.NewData ? &stat2.NewData : &stat2.OldData);
    701        const CArcItem &ai = arcItems[up.ArcIndex];
    702        if (ai.IsDir)
    703        {
    704          if (up.IsAnti)
    705            stat.Anti_NumDirs++;
    706          else
    707            stat.NumDirs++;
    708        }
    709        else if (ai.IsAltStream)
    710        {
    711          if (up.IsAnti)
    712            stat.Anti_NumAltStreams++;
    713          else
    714          {
    715            stat.NumAltStreams++;
    716            stat.AltStreamsSize += ai.Size;
    717          }
    718        }
    719        else
    720        {
    721          if (up.IsAnti)
    722            stat.Anti_NumFiles++;
    723          else
    724          {
    725            stat.NumFiles++;
    726            stat.FilesSize += ai.Size;
    727          }
    728        }
    729      }
    730    }
    731    RINOK(callback->SetNumItems(stat2));
    732  }
    733  
    734  CArchiveUpdateCallback *updateCallbackSpec = new CArchiveUpdateCallback;
    735  CMyComPtr<IArchiveUpdateCallback> updateCallback(updateCallbackSpec);
    736  
    737  updateCallbackSpec->ShareForWrite = options.OpenShareForWrite;
    738  updateCallbackSpec->StopAfterOpenError = options.StopAfterOpenError;
    739  updateCallbackSpec->StdInMode = options.StdInMode;
    740  updateCallbackSpec->Callback = callback;
    741 
    742  if (arc)
    743  {
    744    // we set Archive to allow to transfer GetProperty requests back to DLL.
    745    updateCallbackSpec->Archive = arc->Archive;
    746  }
    747  
    748  updateCallbackSpec->DirItems = &dirItems;
    749  updateCallbackSpec->ParentDirItem = parentDirItem;
    750 
    751  updateCallbackSpec->StoreNtSecurity = options.NtSecurity.Val;
    752  updateCallbackSpec->StoreHardLinks = options.HardLinks.Val;
    753  updateCallbackSpec->StoreSymLinks = options.SymLinks.Val;
    754 
    755  updateCallbackSpec->Arc = arc;
    756  updateCallbackSpec->ArcItems = &arcItems;
    757  updateCallbackSpec->UpdatePairs = &updatePairs2;
    758 
    759  updateCallbackSpec->ProcessedItemsStatuses = processedItemsStatuses;
    760 
    761  if (options.RenamePairs.Size() != 0)
    762    updateCallbackSpec->NewNames = &newNames;
    763 
    764  CMyComPtr<IOutStream> outSeekStream;
    765  CMyComPtr<ISequentialOutStream> outStream;
    766 
    767  if (!options.StdOutMode)
    768  {
    769    FString dirPrefix;
    770    if (!GetOnlyDirPrefix(us2fs(archivePath.GetFinalPath()), dirPrefix))
    771      throw 1417161;
    772    CreateComplexDir(dirPrefix);
    773  }
    774 
    775  COutFileStream *outStreamSpec = NULL;
    776  CStdOutFileStream *stdOutFileStreamSpec = NULL;
    777  COutMultiVolStream *volStreamSpec = NULL;
    778 
    779  if (options.VolumesSizes.Size() == 0)
    780  {
    781    if (options.StdOutMode)
    782    {
    783      stdOutFileStreamSpec = new CStdOutFileStream;
    784      outStream = stdOutFileStreamSpec;
    785    }
    786    else
    787    {
    788      outStreamSpec = new COutFileStream;
    789      outSeekStream = outStreamSpec;
    790      outStream = outSeekStream;
    791      bool isOK = false;
    792      FString realPath;
    793      
    794      for (unsigned i = 0; i < (1 << 16); i++)
    795      {
    796        if (archivePath.Temp)
    797        {
    798          if (i > 0)
    799          {
    800            archivePath.TempPostfix.Empty();
    801            archivePath.TempPostfix.Add_UInt32(i);
    802          }
    803          realPath = archivePath.GetTempPath();
    804        }
    805        else
    806          realPath = us2fs(archivePath.GetFinalPath());
    807        if (outStreamSpec->Create(realPath, false))
    808        {
    809          tempFiles.Paths.Add(realPath);
    810          isOK = true;
    811          break;
    812        }
    813        if (::GetLastError() != ERROR_FILE_EXISTS)
    814          break;
    815        if (!archivePath.Temp)
    816          break;
    817      }
    818      
    819      if (!isOK)
    820        return errorInfo.SetFromLastError("cannot open file", realPath);
    821    }
    822  }
    823  else
    824  {
    825    if (options.StdOutMode)
    826      return E_FAIL;
    827    if (arc && arc->GetGlobalOffset() > 0)
    828      return E_NOTIMPL;
    829      
    830    volStreamSpec = new COutMultiVolStream;
    831    outSeekStream = volStreamSpec;
    832    outStream = outSeekStream;
    833    volStreamSpec->Sizes = options.VolumesSizes;
    834    volStreamSpec->Prefix = us2fs(archivePath.GetFinalVolPath());
    835    volStreamSpec->Prefix += '.';
    836    volStreamSpec->TempFiles = &tempFiles;
    837    volStreamSpec->Init();
    838 
    839    /*
    840    updateCallbackSpec->VolumesSizes = volumesSizes;
    841    updateCallbackSpec->VolName = archivePath.Prefix + archivePath.Name;
    842    if (!archivePath.VolExtension.IsEmpty())
    843      updateCallbackSpec->VolExt = UString('.') + archivePath.VolExtension;
    844    */
    845  }
    846 
    847  RINOK(SetProperties(outArchive, options.MethodMode.Properties));
    848 
    849  if (options.SfxMode)
    850  {
    851    CInFileStream *sfxStreamSpec = new CInFileStream;
    852    CMyComPtr<IInStream> sfxStream(sfxStreamSpec);
    853    if (!sfxStreamSpec->Open(options.SfxModule))
    854      return errorInfo.SetFromLastError("cannot open SFX module", options.SfxModule);
    855 
    856    CMyComPtr<ISequentialOutStream> sfxOutStream;
    857    COutFileStream *outStreamSpec2 = NULL;
    858    if (options.VolumesSizes.Size() == 0)
    859      sfxOutStream = outStream;
    860    else
    861    {
    862      outStreamSpec2 = new COutFileStream;
    863      sfxOutStream = outStreamSpec2;
    864      FString realPath = us2fs(archivePath.GetFinalPath());
    865      if (!outStreamSpec2->Create(realPath, false))
    866        return errorInfo.SetFromLastError("cannot open file", realPath);
    867    }
    868 
    869    {
    870      UInt64 sfxSize;
    871      RINOK(sfxStreamSpec->GetSize(&sfxSize));
    872      RINOK(callback->WriteSfx(fs2us(options.SfxModule), sfxSize));
    873    }
    874 
    875    RINOK(NCompress::CopyStream(sfxStream, sfxOutStream, NULL));
    876    
    877    if (outStreamSpec2)
    878    {
    879      RINOK(outStreamSpec2->Close());
    880    }
    881  }
    882 
    883  CMyComPtr<ISequentialOutStream> tailStream;
    884 
    885  if (options.SfxMode || !arc || arc->ArcStreamOffset == 0)
    886    tailStream = outStream;
    887  else
    888  {
    889    // Int64 globalOffset = arc->GetGlobalOffset();
    890    RINOK(arc->InStream->Seek(0, STREAM_SEEK_SET, NULL));
    891    RINOK(NCompress::CopyStream_ExactSize(arc->InStream, outStream, arc->ArcStreamOffset, NULL));
    892    if (options.StdOutMode)
    893      tailStream = outStream;
    894    else
    895    {
    896      CTailOutStream *tailStreamSpec = new CTailOutStream;
    897      tailStream = tailStreamSpec;
    898      tailStreamSpec->Stream = outSeekStream;
    899      tailStreamSpec->Offset = arc->ArcStreamOffset;
    900      tailStreamSpec->Init();
    901    }
    902  }
    903 
    904 
    905  HRESULT result = outArchive->UpdateItems(tailStream, updatePairs2.Size(), updateCallback);
    906  // callback->Finalize();
    907  RINOK(result);
    908 
    909  if (!updateCallbackSpec->AreAllFilesClosed())
    910  {
    911    errorInfo.Message = "There are unclosed input file:";
    912    errorInfo.FileNames = updateCallbackSpec->_openFiles_Paths;
    913    return E_FAIL;
    914  }
    915 
    916  if (options.SetArcMTime)
    917  {
    918    FILETIME ft;
    919    ft.dwLowDateTime = 0;
    920    ft.dwHighDateTime = 0;
    921    FOR_VECTOR (i, updatePairs2)
    922    {
    923      CUpdatePair2 &pair2 = updatePairs2[i];
    924      const FILETIME *ft2 = NULL;
    925      if (pair2.NewProps && pair2.DirIndex >= 0)
    926        ft2 = &dirItems.Items[pair2.DirIndex].MTime;
    927      else if (pair2.UseArcProps && pair2.ArcIndex >= 0)
    928        ft2 = &arcItems[pair2.ArcIndex].MTime;
    929      if (ft2)
    930      {
    931        if (::CompareFileTime(&ft, ft2) < 0)
    932          ft = *ft2;
    933      }
    934    }
    935    if (ft.dwLowDateTime != 0 || ft.dwHighDateTime != 0)
    936    {
    937      if (outStreamSpec)
    938        outStreamSpec->SetMTime(&ft);
    939      else if (volStreamSpec)
    940        volStreamSpec->SetMTime(&ft);;
    941    }
    942  }
    943 
    944  if (callback)
    945  {
    946    UInt64 size = 0;
    947    if (outStreamSpec)
    948      outStreamSpec->GetSize(&size);
    949    else if (stdOutFileStreamSpec)
    950      size = stdOutFileStreamSpec->GetSize();
    951    else
    952      size = volStreamSpec->GetSize();
    953 
    954    st.OutArcFileSize = size;
    955  }
    956 
    957  if (outStreamSpec)
    958    result = outStreamSpec->Close();
    959  else if (volStreamSpec)
    960    result = volStreamSpec->Close();
    961  return result;
    962 }
    963 
    964 bool CensorNode_CheckPath2(const NWildcard::CCensorNode &node, const CReadArcItem &item, bool &include);
    965 
    966 static bool Censor_CheckPath(const NWildcard::CCensor &censor, const CReadArcItem &item)
    967 {
    968  bool finded = false;
    969  FOR_VECTOR (i, censor.Pairs)
    970  {
    971    bool include;
    972    if (CensorNode_CheckPath2(censor.Pairs[i].Head, item, include))
    973    {
    974      if (!include)
    975        return false;
    976      finded = true;
    977    }
    978  }
    979  return finded;
    980 }
    981 
    982 static HRESULT EnumerateInArchiveItems(
    983    // bool storeStreamsMode,
    984    const NWildcard::CCensor &censor,
    985    const CArc &arc,
    986    CObjectVector<CArcItem> &arcItems)
    987 {
    988  arcItems.Clear();
    989  UInt32 numItems;
    990  IInArchive *archive = arc.Archive;
    991  RINOK(archive->GetNumberOfItems(&numItems));
    992  arcItems.ClearAndReserve(numItems);
    993 
    994  CReadArcItem item;
    995 
    996  for (UInt32 i = 0; i < numItems; i++)
    997  {
    998    CArcItem ai;
    999 
   1000    RINOK(arc.GetItem(i, item));
   1001    ai.Name = item.Path;
   1002    ai.IsDir = item.IsDir;
   1003    ai.IsAltStream =
   1004        #ifdef SUPPORT_ALT_STREAMS
   1005          item.IsAltStream;
   1006        #else
   1007          false;
   1008        #endif
   1009 
   1010    /*
   1011    if (!storeStreamsMode && ai.IsAltStream)
   1012      continue;
   1013    */
   1014    ai.Censored = Censor_CheckPath(censor, item);
   1015 
   1016    RINOK(arc.GetItemMTime(i, ai.MTime, ai.MTimeDefined));
   1017    RINOK(arc.GetItemSize(i, ai.Size, ai.SizeDefined));
   1018 
   1019    {
   1020      CPropVariant prop;
   1021      RINOK(archive->GetProperty(i, kpidTimeType, &prop));
   1022      if (prop.vt == VT_UI4)
   1023      {
   1024        ai.TimeType = (int)(NFileTimeType::EEnum)prop.ulVal;
   1025        switch (ai.TimeType)
   1026        {
   1027          case NFileTimeType::kWindows:
   1028          case NFileTimeType::kUnix:
   1029          case NFileTimeType::kDOS:
   1030            break;
   1031          default:
   1032            return E_FAIL;
   1033        }
   1034      }
   1035    }
   1036 
   1037    ai.IndexInServer = i;
   1038    arcItems.AddInReserved(ai);
   1039  }
   1040  return S_OK;
   1041 }
   1042 
   1043 #if defined(_WIN32) && !defined(UNDER_CE)
   1044 
   1045 #include <mapi.h>
   1046 
   1047 #endif
   1048 
   1049 HRESULT UpdateArchive(
   1050    CCodecs *codecs,
   1051    const CObjectVector<COpenType> &types,
   1052    const UString &cmdArcPath2,
   1053    NWildcard::CCensor &censor,
   1054    CUpdateOptions &options,
   1055    CUpdateErrorInfo &errorInfo,
   1056    IOpenCallbackUI *openCallback,
   1057    IUpdateCallbackUI2 *callback,
   1058    bool needSetPath)
   1059 {
   1060  if (options.StdOutMode && options.EMailMode)
   1061    return E_FAIL;
   1062 
   1063  if (types.Size() > 1)
   1064    return E_NOTIMPL;
   1065 
   1066  bool renameMode = !options.RenamePairs.IsEmpty();
   1067  if (renameMode)
   1068  {
   1069    if (options.Commands.Size() != 1)
   1070      return E_FAIL;
   1071  }
   1072 
   1073  if (options.DeleteAfterCompressing)
   1074  {
   1075    if (options.Commands.Size() != 1)
   1076      return E_NOTIMPL;
   1077    const CActionSet &as = options.Commands[0].ActionSet;
   1078    for (int i = 2; i < NPairState::kNumValues; i++)
   1079      if (as.StateActions[i] != NPairAction::kCompress)
   1080        return E_NOTIMPL;
   1081  }
   1082 
   1083  censor.AddPathsToCensor(options.PathMode);
   1084  #ifdef _WIN32
   1085  ConvertToLongNames(censor);
   1086  #endif
   1087  censor.ExtendExclude();
   1088 
   1089  
   1090  if (options.VolumesSizes.Size() > 0 && (options.EMailMode /* || options.SfxMode */))
   1091    return E_NOTIMPL;
   1092 
   1093  if (options.SfxMode)
   1094  {
   1095    CProperty property;
   1096    property.Name = "rsfx";
   1097    options.MethodMode.Properties.Add(property);
   1098    if (options.SfxModule.IsEmpty())
   1099    {
   1100      errorInfo.Message = "SFX file is not specified";
   1101      return E_FAIL;
   1102    }
   1103    bool found = false;
   1104    if (options.SfxModule.Find(FCHAR_PATH_SEPARATOR) < 0)
   1105    {
   1106      const FString fullName = NDLL::GetModuleDirPrefix() + options.SfxModule;
   1107      if (NFind::DoesFileExist(fullName))
   1108      {
   1109        options.SfxModule = fullName;
   1110        found = true;
   1111      }
   1112    }
   1113    if (!found)
   1114    {
   1115      if (!NFind::DoesFileExist(options.SfxModule))
   1116        return errorInfo.SetFromLastError("cannot find specified SFX module", options.SfxModule);
   1117    }
   1118  }
   1119 
   1120  CArchiveLink arcLink;
   1121 
   1122  
   1123  if (needSetPath)
   1124  {
   1125    if (!options.InitFormatIndex(codecs, types, cmdArcPath2) ||
   1126        !options.SetArcPath(codecs, cmdArcPath2))
   1127      return E_NOTIMPL;
   1128  }
   1129  
   1130  UString arcPath = options.ArchivePath.GetFinalPath();
   1131 
   1132  if (!options.VolumesSizes.IsEmpty())
   1133  {
   1134    arcPath = options.ArchivePath.GetFinalVolPath();
   1135    arcPath += '.';
   1136    arcPath += "001";
   1137  }
   1138 
   1139  if (cmdArcPath2.IsEmpty())
   1140  {
   1141    if (options.MethodMode.Type.FormatIndex < 0)
   1142      throw "type of archive is not specified";
   1143  }
   1144  else
   1145  {
   1146    NFind::CFileInfo fi;
   1147    if (!fi.Find(us2fs(arcPath)))
   1148    {
   1149      if (renameMode)
   1150        throw "can't find archive";;
   1151      if (options.MethodMode.Type.FormatIndex < 0)
   1152      {
   1153        if (!options.SetArcPath(codecs, cmdArcPath2))
   1154          return E_NOTIMPL;
   1155      }
   1156    }
   1157    else
   1158    {
   1159      if (fi.IsDir())
   1160        throw "there is no such archive";
   1161      if (fi.IsDevice)
   1162        return E_NOTIMPL;
   1163 
   1164      if (!options.StdOutMode && options.UpdateArchiveItself)
   1165        if (fi.IsReadOnly())
   1166        {
   1167          errorInfo.SystemError = ERROR_ACCESS_DENIED;
   1168          errorInfo.Message = "The file is read-only";
   1169          errorInfo.FileNames.Add(arcPath);
   1170          return errorInfo.Get_HRESULT_Error();
   1171        }
   1172 
   1173      if (options.VolumesSizes.Size() > 0)
   1174      {
   1175        errorInfo.FileNames.Add(us2fs(arcPath));
   1176        errorInfo.SystemError = (DWORD)E_NOTIMPL;
   1177        errorInfo.Message = kUpdateIsNotSupoorted_MultiVol;
   1178        return E_NOTIMPL;
   1179      }
   1180      CObjectVector<COpenType> types2;
   1181      // change it.
   1182      if (options.MethodMode.Type_Defined)
   1183        types2.Add(options.MethodMode.Type);
   1184      // We need to set Properties to open archive only in some cases (WIM archives).
   1185 
   1186      CIntVector excl;
   1187      COpenOptions op;
   1188      #ifndef _SFX
   1189      op.props = &options.MethodMode.Properties;
   1190      #endif
   1191      op.codecs = codecs;
   1192      op.types = &types2;
   1193      op.excludedFormats = &excl;
   1194      op.stdInMode = false;
   1195      op.stream = NULL;
   1196      op.filePath = arcPath;
   1197 
   1198      RINOK(callback->StartOpenArchive(arcPath));
   1199 
   1200      HRESULT result = arcLink.Open_Strict(op, openCallback);
   1201 
   1202      if (result == E_ABORT)
   1203        return result;
   1204      
   1205      HRESULT res2 = callback->OpenResult(codecs, arcLink, arcPath, result);
   1206      /*
   1207      if (result == S_FALSE)
   1208        return E_FAIL;
   1209      */
   1210      RINOK(res2);
   1211      RINOK(result);
   1212 
   1213      if (arcLink.VolumePaths.Size() > 1)
   1214      {
   1215        errorInfo.SystemError = (DWORD)E_NOTIMPL;
   1216        errorInfo.Message = kUpdateIsNotSupoorted_MultiVol;
   1217        return E_NOTIMPL;
   1218      }
   1219      
   1220      CArc &arc = arcLink.Arcs.Back();
   1221      arc.MTimeDefined = !fi.IsDevice;
   1222      arc.MTime = fi.MTime;
   1223 
   1224      if (arc.ErrorInfo.ThereIsTail)
   1225      {
   1226        errorInfo.SystemError = (DWORD)E_NOTIMPL;
   1227        errorInfo.Message = "There is some data block after the end of the archive";
   1228        return E_NOTIMPL;
   1229      }
   1230      if (options.MethodMode.Type.FormatIndex < 0)
   1231      {
   1232        options.MethodMode.Type.FormatIndex = arcLink.GetArc()->FormatIndex;
   1233        if (!options.SetArcPath(codecs, cmdArcPath2))
   1234          return E_NOTIMPL;
   1235      }
   1236    }
   1237  }
   1238 
   1239  if (options.MethodMode.Type.FormatIndex < 0)
   1240  {
   1241    options.MethodMode.Type.FormatIndex = codecs->FindFormatForArchiveType((UString)kDefaultArcType);
   1242    if (options.MethodMode.Type.FormatIndex < 0)
   1243      return E_NOTIMPL;
   1244  }
   1245 
   1246  bool thereIsInArchive = arcLink.IsOpen;
   1247  if (!thereIsInArchive && renameMode)
   1248    return E_FAIL;
   1249  
   1250  CDirItems dirItems;
   1251  dirItems.Callback = callback;
   1252 
   1253  CDirItem parentDirItem;
   1254  CDirItem *parentDirItem_Ptr = NULL;
   1255  
   1256  /*
   1257  FStringVector requestedPaths;
   1258  FStringVector *requestedPaths_Ptr = NULL;
   1259  if (options.DeleteAfterCompressing)
   1260    requestedPaths_Ptr = &requestedPaths;
   1261  */
   1262 
   1263  if (options.StdInMode)
   1264  {
   1265    CDirItem di;
   1266    di.Name = options.StdInFileName;
   1267    di.Size = (UInt64)(Int64)-1;
   1268    di.Attrib = 0;
   1269    NTime::GetCurUtcFileTime(di.MTime);
   1270    di.CTime = di.ATime = di.MTime;
   1271    dirItems.Items.Add(di);
   1272  }
   1273  else
   1274  {
   1275    bool needScanning = false;
   1276    
   1277    if (!renameMode)
   1278    FOR_VECTOR (i, options.Commands)
   1279      if (options.Commands[i].ActionSet.NeedScanning())
   1280        needScanning = true;
   1281 
   1282    if (needScanning)
   1283    {
   1284      RINOK(callback->StartScanning());
   1285 
   1286      dirItems.SymLinks = options.SymLinks.Val;
   1287 
   1288      #if defined(_WIN32) && !defined(UNDER_CE)
   1289      dirItems.ReadSecure = options.NtSecurity.Val;
   1290      #endif
   1291 
   1292      dirItems.ScanAltStreams = options.AltStreams.Val;
   1293 
   1294      HRESULT res = EnumerateItems(censor,
   1295          options.PathMode,
   1296          options.AddPathPrefix,
   1297          dirItems);
   1298 
   1299      if (res != S_OK)
   1300      {
   1301        if (res != E_ABORT)
   1302          errorInfo.Message = "Scanning error";
   1303        return res;
   1304      }
   1305      
   1306      RINOK(callback->FinishScanning(dirItems.Stat));
   1307 
   1308      if (censor.Pairs.Size() == 1)
   1309      {
   1310        NFind::CFileInfo fi;
   1311        FString prefix = us2fs(censor.Pairs[0].Prefix);
   1312        prefix += '.';
   1313        // UString prefix = censor.Pairs[0].Prefix;
   1314        /*
   1315        if (prefix.Back() == WCHAR_PATH_SEPARATOR)
   1316        {
   1317          prefix.DeleteBack();
   1318        }
   1319        */
   1320        if (fi.Find(prefix))
   1321          if (fi.IsDir())
   1322          {
   1323            parentDirItem.Size = fi.Size;
   1324            parentDirItem.CTime = fi.CTime;
   1325            parentDirItem.ATime = fi.ATime;
   1326            parentDirItem.MTime = fi.MTime;
   1327            parentDirItem.Attrib = fi.Attrib;
   1328            parentDirItem_Ptr = &parentDirItem;
   1329 
   1330            int secureIndex = -1;
   1331            #if defined(_WIN32) && !defined(UNDER_CE)
   1332            if (options.NtSecurity.Val)
   1333              dirItems.AddSecurityItem(prefix, secureIndex);
   1334            #endif
   1335            parentDirItem.SecureIndex = secureIndex;
   1336 
   1337            parentDirItem_Ptr = &parentDirItem;
   1338          }
   1339      }
   1340    }
   1341  }
   1342 
   1343  FString tempDirPrefix;
   1344  bool usesTempDir = false;
   1345  
   1346  #ifdef _WIN32
   1347  CTempDir tempDirectory;
   1348  if (options.EMailMode && options.EMailRemoveAfter)
   1349  {
   1350    tempDirectory.Create(kTempFolderPrefix);
   1351    tempDirPrefix = tempDirectory.GetPath();
   1352    NormalizeDirPathPrefix(tempDirPrefix);
   1353    usesTempDir = true;
   1354  }
   1355  #endif
   1356 
   1357  CTempFiles tempFiles;
   1358 
   1359  bool createTempFile = false;
   1360 
   1361  if (!options.StdOutMode && options.UpdateArchiveItself)
   1362  {
   1363    CArchivePath &ap = options.Commands[0].ArchivePath;
   1364    ap = options.ArchivePath;
   1365    // if ((archive != 0 && !usesTempDir) || !options.WorkingDir.IsEmpty())
   1366    if ((thereIsInArchive || !options.WorkingDir.IsEmpty()) && !usesTempDir && options.VolumesSizes.Size() == 0)
   1367    {
   1368      createTempFile = true;
   1369      ap.Temp = true;
   1370      if (!options.WorkingDir.IsEmpty())
   1371        ap.TempPrefix = options.WorkingDir;
   1372      else
   1373        ap.TempPrefix = us2fs(ap.Prefix);
   1374      NormalizeDirPathPrefix(ap.TempPrefix);
   1375    }
   1376  }
   1377 
   1378  unsigned ci;
   1379 
   1380  for (ci = 0; ci < options.Commands.Size(); ci++)
   1381  {
   1382    CArchivePath &ap = options.Commands[ci].ArchivePath;
   1383    if (usesTempDir)
   1384    {
   1385      // Check it
   1386      ap.Prefix = fs2us(tempDirPrefix);
   1387      // ap.Temp = true;
   1388      // ap.TempPrefix = tempDirPrefix;
   1389    }
   1390    if (!options.StdOutMode &&
   1391        (ci > 0 || !createTempFile))
   1392    {
   1393      const FString path = us2fs(ap.GetFinalPath());
   1394      if (NFind::DoesFileOrDirExist(path))
   1395      {
   1396        errorInfo.SystemError = ERROR_FILE_EXISTS;
   1397        errorInfo.Message = "The file already exists";
   1398        errorInfo.FileNames.Add(path);
   1399        return errorInfo.Get_HRESULT_Error();
   1400      }
   1401    }
   1402  }
   1403 
   1404  CObjectVector<CArcItem> arcItems;
   1405  if (thereIsInArchive)
   1406  {
   1407    RINOK(EnumerateInArchiveItems(
   1408      // options.StoreAltStreams,
   1409      censor, arcLink.Arcs.Back(), arcItems));
   1410  }
   1411 
   1412  /*
   1413  FStringVector processedFilePaths;
   1414  FStringVector *processedFilePaths_Ptr = NULL;
   1415  if (options.DeleteAfterCompressing)
   1416    processedFilePaths_Ptr = &processedFilePaths;
   1417  */
   1418 
   1419  CByteBuffer processedItems;
   1420  if (options.DeleteAfterCompressing)
   1421  {
   1422    unsigned num = dirItems.Items.Size();
   1423    processedItems.Alloc(num);
   1424    for (unsigned i = 0; i < num; i++)
   1425      processedItems[i] = 0;
   1426  }
   1427 
   1428  /*
   1429  #ifndef _NO_CRYPTO
   1430  if (arcLink.PasswordWasAsked)
   1431  {
   1432    // We set password, if open have requested password
   1433    RINOK(callback->SetPassword(arcLink.Password));
   1434  }
   1435  #endif
   1436  */
   1437 
   1438  for (ci = 0; ci < options.Commands.Size(); ci++)
   1439  {
   1440    const CArc *arc = thereIsInArchive ? arcLink.GetArc() : NULL;
   1441    CUpdateArchiveCommand &command = options.Commands[ci];
   1442    UString name;
   1443    bool isUpdating;
   1444    
   1445    if (options.StdOutMode)
   1446    {
   1447      name = "stdout";
   1448      isUpdating = thereIsInArchive;
   1449    }
   1450    else
   1451    {
   1452      name = command.ArchivePath.GetFinalPath();
   1453      isUpdating = (ci == 0 && options.UpdateArchiveItself && thereIsInArchive);
   1454    }
   1455    
   1456    RINOK(callback->StartArchive(name, isUpdating))
   1457 
   1458    CFinishArchiveStat st;
   1459 
   1460    RINOK(Compress(options,
   1461        isUpdating,
   1462        codecs,
   1463        command.ActionSet,
   1464        arc,
   1465        command.ArchivePath,
   1466        arcItems,
   1467        options.DeleteAfterCompressing ? (Byte *)processedItems : NULL,
   1468 
   1469        dirItems,
   1470        parentDirItem_Ptr,
   1471 
   1472        tempFiles,
   1473        errorInfo, callback, st));
   1474 
   1475    RINOK(callback->FinishArchive(st));
   1476  }
   1477 
   1478 
   1479  if (thereIsInArchive)
   1480  {
   1481    RINOK(arcLink.Close());
   1482    arcLink.Release();
   1483  }
   1484 
   1485  tempFiles.Paths.Clear();
   1486  if (createTempFile)
   1487  {
   1488    try
   1489    {
   1490      CArchivePath &ap = options.Commands[0].ArchivePath;
   1491      const FString &tempPath = ap.GetTempPath();
   1492      
   1493      // DWORD attrib = 0;
   1494      if (thereIsInArchive)
   1495      {
   1496        // attrib = NFind::GetFileAttrib(us2fs(arcPath));
   1497        if (!DeleteFileAlways(us2fs(arcPath)))
   1498          return errorInfo.SetFromLastError("cannot delete the file", us2fs(arcPath));
   1499      }
   1500      
   1501      if (!MyMoveFile(tempPath, us2fs(arcPath)))
   1502      {
   1503        errorInfo.SetFromLastError("cannot move the file", tempPath);
   1504        errorInfo.FileNames.Add(us2fs(arcPath));
   1505        return errorInfo.Get_HRESULT_Error();
   1506      }
   1507      
   1508      /*
   1509      if (attrib != INVALID_FILE_ATTRIBUTES && (attrib & FILE_ATTRIBUTE_READONLY))
   1510      {
   1511        DWORD attrib2 = NFind::GetFileAttrib(us2fs(arcPath));
   1512        if (attrib2 != INVALID_FILE_ATTRIBUTES)
   1513          NDir::SetFileAttrib(us2fs(arcPath), attrib2 | FILE_ATTRIBUTE_READONLY);
   1514      }
   1515      */
   1516    }
   1517    catch(...)
   1518    {
   1519      throw;
   1520    }
   1521  }
   1522 
   1523 
   1524  #if defined(_WIN32) && !defined(UNDER_CE)
   1525  
   1526  if (options.EMailMode)
   1527  {
   1528    NDLL::CLibrary mapiLib;
   1529    if (!mapiLib.Load(FTEXT("Mapi32.dll")))
   1530    {
   1531      errorInfo.SetFromLastError("cannot load Mapi32.dll");
   1532      return errorInfo.Get_HRESULT_Error();
   1533    }
   1534 
   1535    /*
   1536    LPMAPISENDDOCUMENTS fnSend = (LPMAPISENDDOCUMENTS)mapiLib.GetProc("MAPISendDocuments");
   1537    if (fnSend == 0)
   1538    {
   1539      errorInfo.SetFromLastError)("7-Zip cannot find MAPISendDocuments function");
   1540      return errorInfo.Get_HRESULT_Error();
   1541    }
   1542    */
   1543    
   1544    LPMAPISENDMAIL sendMail = (LPMAPISENDMAIL)mapiLib.GetProc("MAPISendMail");
   1545    if (sendMail == 0)
   1546    {
   1547      errorInfo.SetFromLastError("7-Zip cannot find MAPISendMail function");
   1548      return errorInfo.Get_HRESULT_Error();;
   1549    }
   1550 
   1551    FStringVector fullPaths;
   1552    unsigned i;
   1553    
   1554    for (i = 0; i < options.Commands.Size(); i++)
   1555    {
   1556      CArchivePath &ap = options.Commands[i].ArchivePath;
   1557      FString finalPath = us2fs(ap.GetFinalPath());
   1558      FString arcPath2;
   1559      if (!MyGetFullPathName(finalPath, arcPath2))
   1560        return errorInfo.SetFromLastError("GetFullPathName error", finalPath);
   1561      fullPaths.Add(arcPath2);
   1562    }
   1563 
   1564    CCurrentDirRestorer curDirRestorer;
   1565    
   1566    for (i = 0; i < fullPaths.Size(); i++)
   1567    {
   1568      const UString arcPath2 = fs2us(fullPaths[i]);
   1569      const UString fileName = ExtractFileNameFromPath(arcPath2);
   1570      const AString path (GetAnsiString(arcPath2));
   1571      const AString name (GetAnsiString(fileName));
   1572      // Warning!!! MAPISendDocuments function changes Current directory
   1573      // fnSend(0, ";", (LPSTR)(LPCSTR)path, (LPSTR)(LPCSTR)name, 0);
   1574 
   1575      MapiFileDesc f;
   1576      memset(&f, 0, sizeof(f));
   1577      f.nPosition = 0xFFFFFFFF;
   1578      f.lpszPathName = (char *)(const char *)path;
   1579      f.lpszFileName = (char *)(const char *)name;
   1580      
   1581      MapiMessage m;
   1582      memset(&m, 0, sizeof(m));
   1583      m.nFileCount = 1;
   1584      m.lpFiles = &f;
   1585      
   1586      const AString addr (GetAnsiString(options.EMailAddress));
   1587      MapiRecipDesc rec;
   1588      if (!addr.IsEmpty())
   1589      {
   1590        memset(&rec, 0, sizeof(rec));
   1591        rec.ulRecipClass = MAPI_TO;
   1592        rec.lpszAddress = (char *)(const char *)addr;
   1593        m.nRecipCount = 1;
   1594        m.lpRecips = &rec;
   1595      }
   1596      
   1597      sendMail((LHANDLE)0, 0, &m, MAPI_DIALOG, 0);
   1598    }
   1599  }
   1600  
   1601  #endif
   1602 
   1603  if (options.DeleteAfterCompressing)
   1604  {
   1605    CRecordVector<CDirPathSortPair> pairs;
   1606    FStringVector foldersNames;
   1607 
   1608    unsigned i;
   1609 
   1610    for (i = 0; i < dirItems.Items.Size(); i++)
   1611    {
   1612      const CDirItem &dirItem = dirItems.Items[i];
   1613      const FString phyPath = dirItems.GetPhyPath(i);
   1614      if (dirItem.IsDir())
   1615      {
   1616        CDirPathSortPair pair;
   1617        pair.Index = i;
   1618        pair.SetNumSlashes(phyPath);
   1619        pairs.Add(pair);
   1620      }
   1621      else
   1622      {
   1623        if (processedItems[i] != 0 || dirItem.Size == 0)
   1624        {
   1625          NFind::CFileInfo fileInfo;
   1626          if (fileInfo.Find(phyPath))
   1627          {
   1628            // maybe we must exclude also files with archive name: "a a.7z * -sdel"
   1629            if (fileInfo.Size == dirItem.Size
   1630                && CompareFileTime(&fileInfo.MTime, &dirItem.MTime) == 0
   1631                && CompareFileTime(&fileInfo.CTime, &dirItem.CTime) == 0)
   1632            {
   1633              RINOK(callback->DeletingAfterArchiving(phyPath, false));
   1634              DeleteFileAlways(phyPath);
   1635            }
   1636          }
   1637        }
   1638        else
   1639        {
   1640          // file was skipped
   1641          /*
   1642          errorInfo.SystemError = 0;
   1643          errorInfo.Message = "file was not processed";
   1644          errorInfo.FileName = phyPath;
   1645          return E_FAIL;
   1646          */
   1647        }
   1648      }
   1649    }
   1650 
   1651    pairs.Sort2();
   1652    
   1653    for (i = 0; i < pairs.Size(); i++)
   1654    {
   1655      const FString phyPath = dirItems.GetPhyPath(pairs[i].Index);
   1656      if (NFind::DoesDirExist(phyPath))
   1657      {
   1658        RINOK(callback->DeletingAfterArchiving(phyPath, true));
   1659        RemoveDir(phyPath);
   1660      }
   1661    }
   1662 
   1663    RINOK(callback->FinishDeletingAfterArchiving());
   1664  }
   1665 
   1666  return S_OK;
   1667 }