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