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/PropVariantConversions.h"
16 #include "Windows/Time.h"
17 
18 #include "../../Common/FileStreams.h"
19 
20 #include "../../Compress/CopyCoder.h"
21 
22 #include "../Common/DirItem.h"
23 #include "../Common/EnumDirItems.h"
24 #include "../Common/OpenArchive.h"
25 #include "../Common/UpdateProduce.h"
26 
27 #include "EnumDirItems.h"
28 #include "SetProperties.h"
29 #include "TempFiles.h"
30 #include "UpdateCallback.h"
31 
32 static const char *kUpdateIsNotSupoorted =
33   "update operations are not supported for this archive";
34 
35 using namespace NWindows;
36 using namespace NCOM;
37 using namespace NFile;
38 using namespace NName;
39 
40 static CFSTR kTempFolderPrefix = FTEXT("7zE");
41 
42 using namespace NUpdateArchive;
43 
44 class COutMultiVolStream:
45   public IOutStream,
46   public CMyUnknownImp
47 {
48   int _streamIndex; // required stream
49   UInt64 _offsetPos; // offset from start of _streamIndex index
50   UInt64 _absPos;
51   UInt64 _length;
52 
53   struct CSubStreamInfo
54   {
55     COutFileStream *StreamSpec;
56     CMyComPtr<IOutStream> Stream;
57     FString Name;
58     UInt64 Pos;
59     UInt64 RealSize;
60   };
61   CObjectVector<CSubStreamInfo> Streams;
62 public:
63   // CMyComPtr<IArchiveUpdateCallback2> VolumeCallback;
64   CRecordVector<UInt64> Sizes;
65   FString Prefix;
66   CTempFiles *TempFiles;
67 
Init()68   void Init()
69   {
70     _streamIndex = 0;
71     _offsetPos = 0;
72     _absPos = 0;
73     _length = 0;
74   }
75 
76   HRESULT Close();
77 
78   MY_UNKNOWN_IMP1(IOutStream)
79 
80   STDMETHOD(Write)(const void *data, UInt32 size, UInt32 *processedSize);
81   STDMETHOD(Seek)(Int64 offset, UInt32 seekOrigin, UInt64 *newPosition);
82   STDMETHOD(SetSize)(UInt64 newSize);
83 };
84 
85 // static NSynchronization::CCriticalSection g_TempPathsCS;
86 
Close()87 HRESULT COutMultiVolStream::Close()
88 {
89   HRESULT res = S_OK;
90   for (int i = 0; i < Streams.Size(); i++)
91   {
92     CSubStreamInfo &s = Streams[i];
93     if (s.StreamSpec)
94     {
95       HRESULT res2 = s.StreamSpec->Close();
96       if (res2 != S_OK)
97         res = res2;
98     }
99   }
100   return res;
101 }
102 
Write(const void * data,UInt32 size,UInt32 * processedSize)103 STDMETHODIMP COutMultiVolStream::Write(const void *data, UInt32 size, UInt32 *processedSize)
104 {
105   if (processedSize != NULL)
106     *processedSize = 0;
107   while (size > 0)
108   {
109     if (_streamIndex >= Streams.Size())
110     {
111       CSubStreamInfo subStream;
112 
113       FChar temp[16];
114       ConvertUInt32ToString(_streamIndex + 1, temp);
115       FString res = temp;
116       while (res.Length() < 3)
117         res = FString(FTEXT('0')) + res;
118       FString name = Prefix + res;
119       subStream.StreamSpec = new COutFileStream;
120       subStream.Stream = subStream.StreamSpec;
121       if (!subStream.StreamSpec->Create(name, false))
122         return ::GetLastError();
123       {
124         // NSynchronization::CCriticalSectionLock lock(g_TempPathsCS);
125         TempFiles->Paths.Add(name);
126       }
127 
128       subStream.Pos = 0;
129       subStream.RealSize = 0;
130       subStream.Name = name;
131       Streams.Add(subStream);
132       continue;
133     }
134     CSubStreamInfo &subStream = Streams[_streamIndex];
135 
136     int index = _streamIndex;
137     if (index >= Sizes.Size())
138       index = Sizes.Size() - 1;
139     UInt64 volSize = Sizes[index];
140 
141     if (_offsetPos >= volSize)
142     {
143       _offsetPos -= volSize;
144       _streamIndex++;
145       continue;
146     }
147     if (_offsetPos != subStream.Pos)
148     {
149       // CMyComPtr<IOutStream> outStream;
150       // RINOK(subStream.Stream.QueryInterface(IID_IOutStream, &outStream));
151       RINOK(subStream.Stream->Seek(_offsetPos, STREAM_SEEK_SET, NULL));
152       subStream.Pos = _offsetPos;
153     }
154 
155     UInt32 curSize = (UInt32)MyMin((UInt64)size, volSize - subStream.Pos);
156     UInt32 realProcessed;
157     RINOK(subStream.Stream->Write(data, curSize, &realProcessed));
158     data = (void *)((Byte *)data + realProcessed);
159     size -= realProcessed;
160     subStream.Pos += realProcessed;
161     _offsetPos += realProcessed;
162     _absPos += realProcessed;
163     if (_absPos > _length)
164       _length = _absPos;
165     if (_offsetPos > subStream.RealSize)
166       subStream.RealSize = _offsetPos;
167     if (processedSize != NULL)
168       *processedSize += realProcessed;
169     if (subStream.Pos == volSize)
170     {
171       _streamIndex++;
172       _offsetPos = 0;
173     }
174     if (realProcessed == 0 && curSize != 0)
175       return E_FAIL;
176     break;
177   }
178   return S_OK;
179 }
180 
Seek(Int64 offset,UInt32 seekOrigin,UInt64 * newPosition)181 STDMETHODIMP COutMultiVolStream::Seek(Int64 offset, UInt32 seekOrigin, UInt64 *newPosition)
182 {
183   if (seekOrigin >= 3)
184     return STG_E_INVALIDFUNCTION;
185   switch(seekOrigin)
186   {
187     case STREAM_SEEK_SET:
188       _absPos = offset;
189       break;
190     case STREAM_SEEK_CUR:
191       _absPos += offset;
192       break;
193     case STREAM_SEEK_END:
194       _absPos = _length + offset;
195       break;
196   }
197   _offsetPos = _absPos;
198   if (newPosition != NULL)
199     *newPosition = _absPos;
200   _streamIndex = 0;
201   return S_OK;
202 }
203 
SetSize(UInt64 newSize)204 STDMETHODIMP COutMultiVolStream::SetSize(UInt64 newSize)
205 {
206   if (newSize < 0)
207     return E_INVALIDARG;
208   int i = 0;
209   while (i < Streams.Size())
210   {
211     CSubStreamInfo &subStream = Streams[i++];
212     if ((UInt64)newSize < subStream.RealSize)
213     {
214       RINOK(subStream.Stream->SetSize(newSize));
215       subStream.RealSize = newSize;
216       break;
217     }
218     newSize -= subStream.RealSize;
219   }
220   while (i < Streams.Size())
221   {
222     {
223       CSubStreamInfo &subStream = Streams.Back();
224       subStream.Stream.Release();
225       NDirectory::DeleteFileAlways(subStream.Name);
226     }
227     Streams.DeleteBack();
228   }
229   _offsetPos = _absPos;
230   _streamIndex = 0;
231   _length = newSize;
232   return S_OK;
233 }
234 
235 static const wchar_t *kDefaultArchiveType = L"7z";
236 static const wchar_t *kSFXExtension =
237   #ifdef _WIN32
238     L"exe";
239   #else
240     L"";
241   #endif
242 
Init(const CCodecs * codecs,const CIntVector & formatIndices,const UString & arcPath)243 bool CUpdateOptions::Init(const CCodecs *codecs, const CIntVector &formatIndices, const UString &arcPath)
244 {
245   if (formatIndices.Size() > 1)
246     return false;
247   int arcTypeIndex = -1;
248   if (formatIndices.Size() != 0)
249     arcTypeIndex = formatIndices[0];
250   if (arcTypeIndex >= 0)
251     MethodMode.FormatIndex = arcTypeIndex;
252   else
253   {
254     MethodMode.FormatIndex = codecs->FindFormatForArchiveName(arcPath);
255     // It works incorrectly for update command if archive has some non-default extension!
256     if (MethodMode.FormatIndex < 0)
257       MethodMode.FormatIndex = codecs->FindFormatForArchiveType(kDefaultArchiveType);
258   }
259   if (MethodMode.FormatIndex < 0)
260     return false;
261   const CArcInfoEx &arcInfo = codecs->Formats[MethodMode.FormatIndex];
262   if (!arcInfo.UpdateEnabled)
263     return false;
264   UString typeExt = arcInfo.GetMainExt();
265   UString ext = typeExt;
266   if (SfxMode)
267     ext = kSFXExtension;
268   ArchivePath.BaseExtension = ext;
269   ArchivePath.VolExtension = typeExt;
270   ArchivePath.ParseFromPath(arcPath);
271   for (int i = 0; i < Commands.Size(); i++)
272   {
273     CUpdateArchiveCommand &uc = Commands[i];
274     uc.ArchivePath.BaseExtension = ext;
275     uc.ArchivePath.VolExtension = typeExt;
276     uc.ArchivePath.ParseFromPath(uc.UserArchivePath);
277   }
278   return true;
279 }
280 
281 /*
282 struct CUpdateProduceCallbackImp: public IUpdateProduceCallback
283 {
284   const CObjectVector<CArcItem> *_arcItems;
285   IUpdateCallbackUI *_callback;
286 
287   CUpdateProduceCallbackImp(const CObjectVector<CArcItem> *a,
288       IUpdateCallbackUI *callback): _arcItems(a), _callback(callback) {}
289   virtual HRESULT ShowDeleteFile(int arcIndex);
290 };
291 
292 HRESULT CUpdateProduceCallbackImp::ShowDeleteFile(int arcIndex)
293 {
294   return _callback->ShowDeleteFile((*_arcItems)[arcIndex].Name);
295 }
296 */
297 
Compress(CCodecs * codecs,const CActionSet & actionSet,IInArchive * archive,const CCompressionMethodMode & compressionMethod,CArchivePath & archivePath,const CObjectVector<CArcItem> & arcItems,bool shareForWrite,bool stdInMode,bool stdOutMode,const CDirItems & dirItems,bool sfxMode,const FString & sfxModule,const CRecordVector<UInt64> & volumesSizes,CTempFiles & tempFiles,CUpdateErrorInfo & errorInfo,IUpdateCallbackUI * callback)298 static HRESULT Compress(
299     CCodecs *codecs,
300     const CActionSet &actionSet,
301     IInArchive *archive,
302     const CCompressionMethodMode &compressionMethod,
303     CArchivePath &archivePath,
304     const CObjectVector<CArcItem> &arcItems,
305     bool shareForWrite,
306     bool stdInMode,
307     /* const UString & stdInFileName, */
308     bool stdOutMode,
309     const CDirItems &dirItems,
310     bool sfxMode,
311     const FString &sfxModule,
312     const CRecordVector<UInt64> &volumesSizes,
313     CTempFiles &tempFiles,
314     CUpdateErrorInfo &errorInfo,
315     IUpdateCallbackUI *callback)
316 {
317   CMyComPtr<IOutArchive> outArchive;
318   if (archive != NULL)
319   {
320     CMyComPtr<IInArchive> archive2 = archive;
321     HRESULT result = archive2.QueryInterface(IID_IOutArchive, &outArchive);
322     if (result != S_OK)
323       throw kUpdateIsNotSupoorted;
324   }
325   else
326   {
327     RINOK(codecs->CreateOutArchive(compressionMethod.FormatIndex, outArchive));
328 
329     #ifdef EXTERNAL_CODECS
330     {
331       CMyComPtr<ISetCompressCodecsInfo> setCompressCodecsInfo;
332       outArchive.QueryInterface(IID_ISetCompressCodecsInfo, (void **)&setCompressCodecsInfo);
333       if (setCompressCodecsInfo)
334       {
335         RINOK(setCompressCodecsInfo->SetCompressCodecsInfo(codecs));
336       }
337     }
338     #endif
339   }
340   if (outArchive == 0)
341     throw kUpdateIsNotSupoorted;
342 
343   NFileTimeType::EEnum fileTimeType;
344   UInt32 value;
345   RINOK(outArchive->GetFileTimeType(&value));
346 
347   switch(value)
348   {
349     case NFileTimeType::kWindows:
350     case NFileTimeType::kUnix:
351     case NFileTimeType::kDOS:
352       fileTimeType = (NFileTimeType::EEnum)value;
353       break;
354     default:
355       return E_FAIL;
356   }
357 
358   CRecordVector<CUpdatePair2> updatePairs2;
359 
360   {
361     CRecordVector<CUpdatePair> updatePairs;
362     GetUpdatePairInfoList(dirItems, arcItems, fileTimeType, updatePairs); // must be done only once!!!
363     // CUpdateProduceCallbackImp upCallback(&arcItems, callback);
364     UpdateProduce(updatePairs, actionSet, updatePairs2, NULL /* &upCallback */);
365   }
366 
367   UInt32 numFiles = 0;
368   for (int i = 0; i < updatePairs2.Size(); i++)
369     if (updatePairs2[i].NewData)
370       numFiles++;
371 
372   RINOK(callback->SetNumFiles(numFiles));
373 
374 
375   CArchiveUpdateCallback *updateCallbackSpec = new CArchiveUpdateCallback;
376   CMyComPtr<IArchiveUpdateCallback> updateCallback(updateCallbackSpec);
377 
378   updateCallbackSpec->ShareForWrite = shareForWrite;
379   updateCallbackSpec->StdInMode = stdInMode;
380   updateCallbackSpec->Callback = callback;
381   updateCallbackSpec->DirItems = &dirItems;
382   updateCallbackSpec->ArcItems = &arcItems;
383   updateCallbackSpec->UpdatePairs = &updatePairs2;
384 
385   CMyComPtr<ISequentialOutStream> outStream;
386 
387   if (!stdOutMode)
388   {
389     FString dirPrefix;
390     if (!NFile::NDirectory::GetOnlyDirPrefix(us2fs(archivePath.GetFinalPath()), dirPrefix))
391       throw 1417161;
392     NFile::NDirectory::CreateComplexDirectory(dirPrefix);
393   }
394 
395   COutFileStream *outStreamSpec = NULL;
396   COutMultiVolStream *volStreamSpec = NULL;
397 
398   if (volumesSizes.Size() == 0)
399   {
400     if (stdOutMode)
401       outStream = new CStdOutFileStream;
402     else
403     {
404       outStreamSpec = new COutFileStream;
405       outStream = outStreamSpec;
406       bool isOK = false;
407       FString realPath;
408       for (int i = 0; i < (1 << 16); i++)
409       {
410         if (archivePath.Temp)
411         {
412           if (i > 0)
413           {
414             FChar s[16];
415             ConvertUInt32ToString(i, s);
416             archivePath.TempPostfix = s;
417           }
418           realPath = archivePath.GetTempPath();
419         }
420         else
421           realPath = us2fs(archivePath.GetFinalPath());
422         if (outStreamSpec->Create(realPath, false))
423         {
424           tempFiles.Paths.Add(realPath);
425           isOK = true;
426           break;
427         }
428         if (::GetLastError() != ERROR_FILE_EXISTS)
429           break;
430         if (!archivePath.Temp)
431           break;
432       }
433       if (!isOK)
434       {
435         errorInfo.SystemError = ::GetLastError();
436         errorInfo.FileName = realPath;
437         errorInfo.Message = L"7-Zip cannot open file";
438         return E_FAIL;
439       }
440     }
441   }
442   else
443   {
444     if (stdOutMode)
445       return E_FAIL;
446     volStreamSpec = new COutMultiVolStream;
447     outStream = volStreamSpec;
448     volStreamSpec->Sizes = volumesSizes;
449     volStreamSpec->Prefix = us2fs(archivePath.GetFinalPath() + L".");
450     volStreamSpec->TempFiles = &tempFiles;
451     volStreamSpec->Init();
452 
453     /*
454     updateCallbackSpec->VolumesSizes = volumesSizes;
455     updateCallbackSpec->VolName = archivePath.Prefix + archivePath.Name;
456     if (!archivePath.VolExtension.IsEmpty())
457       updateCallbackSpec->VolExt = UString(L'.') + archivePath.VolExtension;
458     */
459   }
460 
461   RINOK(SetProperties(outArchive, compressionMethod.Properties));
462 
463   if (sfxMode)
464   {
465     CInFileStream *sfxStreamSpec = new CInFileStream;
466     CMyComPtr<IInStream> sfxStream(sfxStreamSpec);
467     if (!sfxStreamSpec->Open(sfxModule))
468     {
469       errorInfo.SystemError = ::GetLastError();
470       errorInfo.Message = L"7-Zip cannot open SFX module";
471       errorInfo.FileName = sfxModule;
472       return E_FAIL;
473     }
474 
475     CMyComPtr<ISequentialOutStream> sfxOutStream;
476     COutFileStream *outStreamSpec = NULL;
477     if (volumesSizes.Size() == 0)
478       sfxOutStream = outStream;
479     else
480     {
481       outStreamSpec = new COutFileStream;
482       sfxOutStream = outStreamSpec;
483       FString realPath = us2fs(archivePath.GetFinalPath());
484       if (!outStreamSpec->Create(realPath, false))
485       {
486         errorInfo.SystemError = ::GetLastError();
487         errorInfo.FileName = realPath;
488         errorInfo.Message = L"7-Zip cannot open file";
489         return E_FAIL;
490       }
491     }
492     RINOK(NCompress::CopyStream(sfxStream, sfxOutStream, NULL));
493     if (outStreamSpec)
494     {
495       RINOK(outStreamSpec->Close());
496     }
497   }
498 
499   HRESULT result = outArchive->UpdateItems(outStream, updatePairs2.Size(), updateCallback);
500   callback->Finilize();
501   RINOK(result);
502   if (outStreamSpec)
503     result = outStreamSpec->Close();
504   else if (volStreamSpec)
505     result = volStreamSpec->Close();
506   return result;
507 }
508 
EnumerateInArchiveItems(const NWildcard::CCensor & censor,const CArc & arc,CObjectVector<CArcItem> & arcItems)509 HRESULT EnumerateInArchiveItems(const NWildcard::CCensor &censor,
510     const CArc &arc,
511     CObjectVector<CArcItem> &arcItems)
512 {
513   arcItems.Clear();
514   UInt32 numItems;
515   IInArchive *archive = arc.Archive;
516   RINOK(archive->GetNumberOfItems(&numItems));
517   arcItems.Reserve(numItems);
518   for (UInt32 i = 0; i < numItems; i++)
519   {
520     CArcItem ai;
521 
522     RINOK(arc.GetItemPath(i, ai.Name));
523     RINOK(IsArchiveItemFolder(archive, i, ai.IsDir));
524     ai.Censored = censor.CheckPath(ai.Name, !ai.IsDir);
525     RINOK(arc.GetItemMTime(i, ai.MTime, ai.MTimeDefined));
526     RINOK(arc.GetItemSize(i, ai.Size, ai.SizeDefined));
527 
528     {
529       CPropVariant prop;
530       RINOK(archive->GetProperty(i, kpidTimeType, &prop));
531       if (prop.vt == VT_UI4)
532       {
533         ai.TimeType = (int)(NFileTimeType::EEnum)prop.ulVal;
534         switch(ai.TimeType)
535         {
536           case NFileTimeType::kWindows:
537           case NFileTimeType::kUnix:
538           case NFileTimeType::kDOS:
539             break;
540           default:
541             return E_FAIL;
542         }
543       }
544     }
545 
546     ai.IndexInServer = i;
547     arcItems.Add(ai);
548   }
549   return S_OK;
550 }
551 
552 
UpdateWithItemLists(CCodecs * codecs,CUpdateOptions & options,IInArchive * archive,const CObjectVector<CArcItem> & arcItems,CDirItems & dirItems,CTempFiles & tempFiles,CUpdateErrorInfo & errorInfo,IUpdateCallbackUI2 * callback)553 static HRESULT UpdateWithItemLists(
554     CCodecs *codecs,
555     CUpdateOptions &options,
556     IInArchive *archive,
557     const CObjectVector<CArcItem> &arcItems,
558     CDirItems &dirItems,
559     CTempFiles &tempFiles,
560     CUpdateErrorInfo &errorInfo,
561     IUpdateCallbackUI2 *callback)
562 {
563   for(int i = 0; i < options.Commands.Size(); i++)
564   {
565     CUpdateArchiveCommand &command = options.Commands[i];
566     if (options.StdOutMode)
567     {
568       RINOK(callback->StartArchive(L"stdout", archive != 0));
569     }
570     else
571     {
572       RINOK(callback->StartArchive(command.ArchivePath.GetFinalPath(),
573           i == 0 && options.UpdateArchiveItself && archive != 0));
574     }
575 
576     RINOK(Compress(
577         codecs,
578         command.ActionSet, archive,
579         options.MethodMode,
580         command.ArchivePath,
581         arcItems,
582         options.OpenShareForWrite,
583         options.StdInMode,
584         /* options.StdInFileName, */
585         options.StdOutMode,
586         dirItems,
587         options.SfxMode, options.SfxModule,
588         options.VolumesSizes,
589         tempFiles,
590         errorInfo, callback));
591 
592     RINOK(callback->FinishArchive());
593   }
594   return S_OK;
595 }
596 
597 #if defined(_WIN32) && !defined(UNDER_CE)
598 class CCurrentDirRestorer
599 {
600   FString _path;
601 public:
CCurrentDirRestorer()602   CCurrentDirRestorer() { NFile::NDirectory::MyGetCurrentDirectory(_path); }
~CCurrentDirRestorer()603   ~CCurrentDirRestorer() { RestoreDirectory();}
RestoreDirectory() const604   bool RestoreDirectory() const { return BOOLToBool(NFile::NDirectory::MySetCurrentDirectory(_path)); }
605 };
606 #endif
607 
608 struct CEnumDirItemUpdateCallback: public IEnumDirItemCallback
609 {
610   IUpdateCallbackUI2 *Callback;
ScanProgressCEnumDirItemUpdateCallback611   HRESULT ScanProgress(UInt64 numFolders, UInt64 numFiles, const wchar_t *path)
612   {
613     return Callback->ScanProgress(numFolders, numFiles, path);
614   }
615 };
616 
617 #ifdef _WIN32
618 typedef ULONG (FAR PASCAL MY_MAPISENDDOCUMENTS)(
619   ULONG_PTR ulUIParam,
620   LPSTR lpszDelimChar,
621   LPSTR lpszFilePaths,
622   LPSTR lpszFileNames,
623   ULONG ulReserved
624 );
625 typedef MY_MAPISENDDOCUMENTS FAR *MY_LPMAPISENDDOCUMENTS;
626 #endif
627 
UpdateArchive(CCodecs * codecs,const NWildcard::CCensor & censor,CUpdateOptions & options,CUpdateErrorInfo & errorInfo,IOpenCallbackUI * openCallback,IUpdateCallbackUI2 * callback)628 HRESULT UpdateArchive(
629     CCodecs *codecs,
630     const NWildcard::CCensor &censor,
631     CUpdateOptions &options,
632     CUpdateErrorInfo &errorInfo,
633     IOpenCallbackUI *openCallback,
634     IUpdateCallbackUI2 *callback)
635 {
636   if (options.StdOutMode && options.EMailMode)
637     return E_FAIL;
638 
639   if (options.VolumesSizes.Size() > 0 && (options.EMailMode || options.SfxMode))
640     return E_NOTIMPL;
641 
642   if (options.SfxMode)
643   {
644     CProperty property;
645     property.Name = L"rsfx";
646     property.Value = L"on";
647     options.MethodMode.Properties.Add(property);
648     if (options.SfxModule.IsEmpty())
649     {
650       errorInfo.Message = L"SFX file is not specified";
651       return E_FAIL;
652     }
653     bool found = false;
654     if (options.SfxModule.Find(FCHAR_PATH_SEPARATOR) < 0)
655     {
656       const FString fullName = NDLL::GetModuleDirPrefix() + options.SfxModule;
657       if (NFind::DoesFileExist(fullName))
658       {
659         options.SfxModule = fullName;
660         found = true;
661       }
662     }
663     if (!found)
664     {
665       if (!NFind::DoesFileExist(options.SfxModule))
666       {
667         errorInfo.SystemError = ::GetLastError();
668         errorInfo.Message = L"7-Zip cannot find specified SFX module";
669         errorInfo.FileName = options.SfxModule;
670         return E_FAIL;
671       }
672     }
673   }
674 
675 
676   CArchiveLink arcLink;
677   const UString arcPath = options.ArchivePath.GetFinalPath();
678 
679   if (!options.ArchivePath.OriginalPath.IsEmpty())
680   {
681     NFind::CFileInfo fi;
682     if (fi.Find(us2fs(arcPath)))
683     {
684       if (fi.IsDir())
685         throw "there is no such archive";
686       if (options.VolumesSizes.Size() > 0)
687         return E_NOTIMPL;
688       CIntVector formatIndices;
689       if (options.MethodMode.FormatIndex >= 0)
690         formatIndices.Add(options.MethodMode.FormatIndex);
691       HRESULT result = arcLink.Open2(codecs, formatIndices, false, NULL, arcPath, openCallback);
692       if (result == E_ABORT)
693         return result;
694       RINOK(callback->OpenResult(arcPath, result));
695       RINOK(result);
696       if (arcLink.VolumePaths.Size() > 1)
697       {
698         errorInfo.SystemError = (DWORD)E_NOTIMPL;
699         errorInfo.Message = L"Updating for multivolume archives is not implemented";
700         return E_NOTIMPL;
701       }
702 
703       CArc &arc = arcLink.Arcs.Back();
704       arc.MTimeDefined = !fi.IsDevice;
705       arc.MTime = fi.MTime;
706     }
707   }
708   else
709   {
710     /*
711     if (archiveType.IsEmpty())
712       throw "type of archive is not specified";
713     */
714   }
715 
716   CDirItems dirItems;
717   if (options.StdInMode)
718   {
719     CDirItem di;
720     di.Name = options.StdInFileName;
721     di.Size = (UInt64)(Int64)-1;
722     di.Attrib = 0;
723     NTime::GetCurUtcFileTime(di.MTime);
724     di.CTime = di.ATime = di.MTime;
725     dirItems.Items.Add(di);
726   }
727   else
728   {
729     bool needScanning = false;
730     for(int i = 0; i < options.Commands.Size(); i++)
731       if (options.Commands[i].ActionSet.NeedScanning())
732         needScanning = true;
733     if (needScanning)
734     {
735       CEnumDirItemUpdateCallback enumCallback;
736       enumCallback.Callback = callback;
737       RINOK(callback->StartScanning());
738       FStringVector errorPaths;
739       CRecordVector<DWORD> errorCodes;
740       HRESULT res = EnumerateItems(censor, dirItems, &enumCallback, errorPaths, errorCodes);
741       for (int i = 0; i < errorPaths.Size(); i++)
742       {
743         RINOK(callback->CanNotFindError(fs2us(errorPaths[i]), errorCodes[i]));
744       }
745       if (res != S_OK)
746       {
747         if (res != E_ABORT)
748           errorInfo.Message = L"Scanning error";
749         return res;
750       }
751       RINOK(callback->FinishScanning());
752     }
753   }
754 
755   FString tempDirPrefix;
756   bool usesTempDir = false;
757 
758   #ifdef _WIN32
759   NDirectory::CTempDir tempDirectory;
760   if (options.EMailMode && options.EMailRemoveAfter)
761   {
762     tempDirectory.Create(kTempFolderPrefix);
763     tempDirPrefix = tempDirectory.GetPath();
764     NormalizeDirPathPrefix(tempDirPrefix);
765     usesTempDir = true;
766   }
767   #endif
768 
769   CTempFiles tempFiles;
770 
771   bool createTempFile = false;
772 
773   bool thereIsInArchive = arcLink.IsOpen;
774 
775   if (!options.StdOutMode && options.UpdateArchiveItself)
776   {
777     CArchivePath &ap = options.Commands[0].ArchivePath;
778     ap = options.ArchivePath;
779     // if ((archive != 0 && !usesTempDir) || !options.WorkingDir.IsEmpty())
780     if ((thereIsInArchive || !options.WorkingDir.IsEmpty()) && !usesTempDir && options.VolumesSizes.Size() == 0)
781     {
782       createTempFile = true;
783       ap.Temp = true;
784       if (!options.WorkingDir.IsEmpty())
785       {
786         ap.TempPrefix = options.WorkingDir;
787         NormalizeDirPathPrefix(ap.TempPrefix);
788       }
789     }
790   }
791 
792   for (int i = 0; i < options.Commands.Size(); i++)
793   {
794     CArchivePath &ap = options.Commands[i].ArchivePath;
795     if (usesTempDir)
796     {
797       // Check it
798       ap.Prefix = fs2us(tempDirPrefix);
799       // ap.Temp = true;
800       // ap.TempPrefix = tempDirPrefix;
801     }
802     if (!options.StdOutMode &&
803         (i > 0 || !createTempFile))
804     {
805       const FString path = us2fs(ap.GetFinalPath());
806       if (NFind::DoesFileOrDirExist(path))
807       {
808         errorInfo.SystemError = 0;
809         errorInfo.Message = L"The file already exists";
810         errorInfo.FileName = path;
811         return E_FAIL;
812       }
813     }
814   }
815 
816   CObjectVector<CArcItem> arcItems;
817   if (thereIsInArchive)
818   {
819     RINOK(EnumerateInArchiveItems(censor, arcLink.Arcs.Back(), arcItems));
820   }
821 
822   RINOK(UpdateWithItemLists(codecs, options,
823       thereIsInArchive ? arcLink.GetArchive() : 0,
824       arcItems, dirItems,
825       tempFiles, errorInfo, callback));
826 
827   if (thereIsInArchive)
828   {
829     RINOK(arcLink.Close());
830     arcLink.Release();
831   }
832 
833   tempFiles.Paths.Clear();
834   if (createTempFile)
835   {
836     try
837     {
838       CArchivePath &ap = options.Commands[0].ArchivePath;
839       const FString &tempPath = ap.GetTempPath();
840       if (thereIsInArchive)
841         if (!NDirectory::DeleteFileAlways(us2fs(arcPath)))
842         {
843           errorInfo.SystemError = ::GetLastError();
844           errorInfo.Message = L"7-Zip cannot delete the file";
845           errorInfo.FileName = us2fs(arcPath);
846           return E_FAIL;
847         }
848       if (!NDirectory::MyMoveFile(tempPath, us2fs(arcPath)))
849       {
850         errorInfo.SystemError = ::GetLastError();
851         errorInfo.Message = L"7-Zip cannot move the file";
852         errorInfo.FileName = tempPath;
853         errorInfo.FileName2 = us2fs(arcPath);
854         return E_FAIL;
855       }
856     }
857     catch(...)
858     {
859       throw;
860     }
861   }
862 
863   #if defined(_WIN32) && !defined(UNDER_CE)
864   if (options.EMailMode)
865   {
866     NDLL::CLibrary mapiLib;
867     if (!mapiLib.Load(FTEXT("Mapi32.dll")))
868     {
869       errorInfo.SystemError = ::GetLastError();
870       errorInfo.Message = L"7-Zip cannot load Mapi32.dll";
871       return E_FAIL;
872     }
873     MY_LPMAPISENDDOCUMENTS fnSend = (MY_LPMAPISENDDOCUMENTS)mapiLib.GetProc("MAPISendDocuments");
874     if (fnSend == 0)
875     {
876       errorInfo.SystemError = ::GetLastError();
877       errorInfo.Message = L"7-Zip cannot find MAPISendDocuments function";
878       return E_FAIL;
879     }
880     FStringVector fullPaths;
881     int i;
882     for (i = 0; i < options.Commands.Size(); i++)
883     {
884       CArchivePath &ap = options.Commands[i].ArchivePath;
885       FString arcPath;
886       if (!NFile::NDirectory::MyGetFullPathName(us2fs(ap.GetFinalPath()), arcPath))
887       {
888         errorInfo.SystemError = ::GetLastError();
889         errorInfo.Message = L"GetFullPathName error";
890         return E_FAIL;
891       }
892       fullPaths.Add(arcPath);
893     }
894     CCurrentDirRestorer curDirRestorer;
895     for (i = 0; i < fullPaths.Size(); i++)
896     {
897       UString arcPath = fs2us(fullPaths[i]);
898       UString fileName = ExtractFileNameFromPath(arcPath);
899       AString path = GetAnsiString(arcPath);
900       AString name = GetAnsiString(fileName);
901       // Warning!!! MAPISendDocuments function changes Current directory
902       fnSend(0, ";", (LPSTR)(LPCSTR)path, (LPSTR)(LPCSTR)name, 0);
903     }
904   }
905   #endif
906   return S_OK;
907 }
908