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