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