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