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