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