misc/libphysfs/lzma/CPP/7zip/UI/Common/Update.cpp
author nemo
Mon, 10 Apr 2017 12:06:43 -0400
changeset 12213 bb5522e88ab2
permissions -rw-r--r--
bulk copy of latest physfs to our misc/libphysfs since this seems to fix an off-by-1 error reliably hit in readln read of 1 byte probably introduced in the addition of the buffered read. Whether this is excessive or whether libphysfs should even be maintained by us is another matter. But at least we shouldn't crash

// Update.cpp

#include "StdAfx.h"

#ifdef _WIN32
#include <mapi.h>
#endif

#include "Update.h"

#include "Common/IntToString.h"
#include "Common/StringConvert.h"
#include "Common/CommandLineParser.h"

#ifdef _WIN32
#include "Windows/DLL.h"
#endif

#include "Windows/Defs.h"
#include "Windows/FileDir.h"
#include "Windows/FileFind.h"
#include "Windows/FileName.h"
#include "Windows/PropVariant.h"
#include "Windows/PropVariantConversions.h"
// #include "Windows/Synchronization.h"

#include "../../Common/FileStreams.h"
#include "../../Compress/Copy/CopyCoder.h"

#include "../Common/DirItem.h"
#include "../Common/EnumDirItems.h"
#include "../Common/UpdateProduce.h"
#include "../Common/OpenArchive.h"

#include "TempFiles.h"
#include "UpdateCallback.h"
#include "EnumDirItems.h"
#include "SetProperties.h"

static const char *kUpdateIsNotSupoorted = 
  "update operations are not supported for this archive";

using namespace NCommandLineParser;
using namespace NWindows;
using namespace NCOM;
using namespace NFile;
using namespace NName;

static const wchar_t *kTempFolderPrefix = L"7zE";

using namespace NUpdateArchive;

static HRESULT CopyBlock(ISequentialInStream *inStream, ISequentialOutStream *outStream)
{
  CMyComPtr<ICompressCoder> copyCoder = new NCompress::CCopyCoder;
  return copyCoder->Code(inStream, outStream, NULL, NULL, NULL);
}

class COutMultiVolStream: 
  public IOutStream,
  public CMyUnknownImp
{
  int _streamIndex; // required stream
  UInt64 _offsetPos; // offset from start of _streamIndex index
  UInt64 _absPos;
  UInt64 _length;

  struct CSubStreamInfo
  {
    COutFileStream *StreamSpec;
    CMyComPtr<IOutStream> Stream;
    UString Name;
    UInt64 Pos;
    UInt64 RealSize;
  };
  CObjectVector<CSubStreamInfo> Streams;
public:
  // CMyComPtr<IArchiveUpdateCallback2> VolumeCallback;
  CRecordVector<UInt64> Sizes;
  UString Prefix;
  CTempFiles *TempFiles;

  void Init()
  {
    _streamIndex = 0;
    _offsetPos = 0;
    _absPos = 0;
    _length = 0;
  }

  HRESULT Close(); 

  MY_UNKNOWN_IMP1(IOutStream)

  STDMETHOD(Write)(const void *data, UInt32 size, UInt32 *processedSize);
  STDMETHOD(Seek)(Int64 offset, UInt32 seekOrigin, UInt64 *newPosition);
  STDMETHOD(SetSize)(Int64 newSize);
};

// static NSynchronization::CCriticalSection g_TempPathsCS;

HRESULT COutMultiVolStream::Close()
{
  HRESULT res = S_OK;
  for (int i = 0; i < Streams.Size(); i++)
  {
    CSubStreamInfo &s = Streams[i];
    if (s.StreamSpec)
    {
      HRESULT res2 = s.StreamSpec->Close();
      if (res2 != S_OK)
        res = res2;
    }
  }
  return res;
}

STDMETHODIMP COutMultiVolStream::Write(const void *data, UInt32 size, UInt32 *processedSize)
{
  if(processedSize != NULL)
    *processedSize = 0;
  while(size > 0)
  {
    if (_streamIndex >= Streams.Size())
    {
      CSubStreamInfo subStream;

      wchar_t temp[32];
      ConvertUInt64ToString(_streamIndex + 1, temp);
      UString res = temp;
      while (res.Length() < 3)
        res = UString(L'0') + res;
      UString name = Prefix + res;
      subStream.StreamSpec = new COutFileStream;
      subStream.Stream = subStream.StreamSpec;
      if(!subStream.StreamSpec->Create(name, false))
        return ::GetLastError();
      {
        // NSynchronization::CCriticalSectionLock lock(g_TempPathsCS);
        TempFiles->Paths.Add(name);
      }

      subStream.Pos = 0;
      subStream.RealSize = 0;
      subStream.Name = name;
      Streams.Add(subStream);
      continue;
    }
    CSubStreamInfo &subStream = Streams[_streamIndex];

    int index = _streamIndex;
    if (index >= Sizes.Size())
      index = Sizes.Size() - 1;
    UInt64 volSize = Sizes[index];

    if (_offsetPos >= volSize)
    {
      _offsetPos -= volSize;
      _streamIndex++;
      continue;
    }
    if (_offsetPos != subStream.Pos)
    {
      // CMyComPtr<IOutStream> outStream;
      // RINOK(subStream.Stream.QueryInterface(IID_IOutStream, &outStream));
      RINOK(subStream.Stream->Seek(_offsetPos, STREAM_SEEK_SET, NULL));
      subStream.Pos = _offsetPos;
    }

    UInt32 curSize = (UInt32)MyMin((UInt64)size, volSize - subStream.Pos);
    UInt32 realProcessed;
    RINOK(subStream.Stream->Write(data, curSize, &realProcessed));
    data = (void *)((Byte *)data + realProcessed);
    size -= realProcessed;
    subStream.Pos += realProcessed;
    _offsetPos += realProcessed;
    _absPos += realProcessed;
    if (_absPos > _length)
      _length = _absPos;
    if (_offsetPos > subStream.RealSize)
      subStream.RealSize = _offsetPos;
    if(processedSize != NULL)
      *processedSize += realProcessed;
    if (subStream.Pos == volSize)
    {
      _streamIndex++;
      _offsetPos = 0;
    }
    if (realProcessed == 0 && curSize != 0)
      return E_FAIL;
    break;
  }
  return S_OK;
}

STDMETHODIMP COutMultiVolStream::Seek(Int64 offset, UInt32 seekOrigin, UInt64 *newPosition)
{
  if(seekOrigin >= 3)
    return STG_E_INVALIDFUNCTION;
  switch(seekOrigin)
  {
    case STREAM_SEEK_SET:
      _absPos = offset;
      break;
    case STREAM_SEEK_CUR:
      _absPos += offset;
      break;
    case STREAM_SEEK_END:
      _absPos = _length + offset;
      break;
  }
  _offsetPos = _absPos;
  if (newPosition != NULL)
    *newPosition = _absPos;
  _streamIndex = 0;
  return S_OK;
}

STDMETHODIMP COutMultiVolStream::SetSize(Int64 newSize)
{
  if (newSize < 0)
    return E_INVALIDARG;
  int i = 0;
  while (i < Streams.Size())
  {
    CSubStreamInfo &subStream = Streams[i++];
    if ((UInt64)newSize < subStream.RealSize)
    {
      RINOK(subStream.Stream->SetSize(newSize));
      subStream.RealSize = newSize;
      break;
    }
    newSize -= subStream.RealSize;
  }
  while (i < Streams.Size())
  {
    {
      CSubStreamInfo &subStream = Streams.Back();
      subStream.Stream.Release();
      NDirectory::DeleteFileAlways(subStream.Name);
    }
    Streams.DeleteBack();
  }
  _offsetPos = _absPos;
  _streamIndex = 0;
  _length = newSize;
  return S_OK;
}

static const wchar_t *kDefaultArchiveType = L"7z";
static const wchar_t *kSFXExtension =
  #ifdef _WIN32
    L"exe";
  #else
    L"";
  #endif

bool CUpdateOptions::Init(const CCodecs *codecs, const UString &arcPath, const UString &arcType)
{
  if (!arcType.IsEmpty())
    MethodMode.FormatIndex = codecs->FindFormatForArchiveType(arcType);
  else
  {
    MethodMode.FormatIndex = codecs->FindFormatForArchiveName(arcPath);
    if (MethodMode.FormatIndex < 0)
      MethodMode.FormatIndex = codecs->FindFormatForArchiveType(kDefaultArchiveType);
  }
  if (MethodMode.FormatIndex < 0)
    return false;
  const CArcInfoEx &arcInfo = codecs->Formats[MethodMode.FormatIndex];
  UString typeExt = arcInfo.GetMainExt();
  UString ext = typeExt;
  if (SfxMode)
    ext = kSFXExtension;
  ArchivePath.BaseExtension = ext;
  ArchivePath.VolExtension = typeExt;
  ArchivePath.ParseFromPath(arcPath);
  for (int i = 0; i < Commands.Size(); i++)
  {
    CUpdateArchiveCommand &uc = Commands[i];
    uc.ArchivePath.BaseExtension = ext;
    uc.ArchivePath.VolExtension = typeExt;
    uc.ArchivePath.ParseFromPath(uc.UserArchivePath);
  }
  return true;
}


static HRESULT Compress(
    CCodecs *codecs,
    const CActionSet &actionSet, 
    IInArchive *archive,
    const CCompressionMethodMode &compressionMethod,
    CArchivePath &archivePath, 
    const CObjectVector<CArchiveItem> &archiveItems,
    bool shareForWrite,
    bool stdInMode,
    /* const UString & stdInFileName, */
    bool stdOutMode,
    const CObjectVector<CDirItem> &dirItems,
    bool sfxMode,
    const UString &sfxModule,
    const CRecordVector<UInt64> &volumesSizes,
    CTempFiles &tempFiles,
    CUpdateErrorInfo &errorInfo,
    IUpdateCallbackUI *callback)
{
  CMyComPtr<IOutArchive> outArchive;
  if(archive != NULL)
  {
    CMyComPtr<IInArchive> archive2 = archive;
    HRESULT result = archive2.QueryInterface(IID_IOutArchive, &outArchive);
    if(result != S_OK)
      throw kUpdateIsNotSupoorted;
  }
  else
  {
    RINOK(codecs->CreateOutArchive(compressionMethod.FormatIndex, outArchive));

    #ifdef EXTERNAL_CODECS
    {
      CMyComPtr<ISetCompressCodecsInfo> setCompressCodecsInfo;
      outArchive.QueryInterface(IID_ISetCompressCodecsInfo, (void **)&setCompressCodecsInfo);
      if (setCompressCodecsInfo)
      {
        RINOK(setCompressCodecsInfo->SetCompressCodecsInfo(codecs));
      }
    }
    #endif
  }
  if (outArchive == 0)
    throw kUpdateIsNotSupoorted;
  
  NFileTimeType::EEnum fileTimeType;
  UInt32 value;
  RINOK(outArchive->GetFileTimeType(&value));

  switch(value)
  {
    case NFileTimeType::kWindows:
    case NFileTimeType::kDOS:
    case NFileTimeType::kUnix:
      fileTimeType = NFileTimeType::EEnum(value);
      break;
    default:
      return E_FAIL;
  }

  CObjectVector<CUpdatePair> updatePairs;
  GetUpdatePairInfoList(dirItems, archiveItems, fileTimeType, updatePairs); // must be done only once!!!
  
  CObjectVector<CUpdatePair2> updatePairs2;
  UpdateProduce(updatePairs, actionSet, updatePairs2);

  UInt32 numFiles = 0;
  for (int i = 0; i < updatePairs2.Size(); i++)
    if (updatePairs2[i].NewData)
      numFiles++;
  
  RINOK(callback->SetNumFiles(numFiles));

  
  CArchiveUpdateCallback *updateCallbackSpec = new CArchiveUpdateCallback;
  CMyComPtr<IArchiveUpdateCallback> updateCallback(updateCallbackSpec);
  
  updateCallbackSpec->ShareForWrite = shareForWrite;
  updateCallbackSpec->StdInMode = stdInMode;
  updateCallbackSpec->Callback = callback;
  updateCallbackSpec->DirItems = &dirItems;
  updateCallbackSpec->ArchiveItems = &archiveItems;
  updateCallbackSpec->UpdatePairs = &updatePairs2;

  CMyComPtr<ISequentialOutStream> outStream;

  const UString &archiveName = archivePath.GetFinalPath();
  if (!stdOutMode)
  {
    UString resultPath;
    int pos;
    if(!NFile::NDirectory::MyGetFullPathName(archiveName, resultPath, pos))
      throw 1417161;
    NFile::NDirectory::CreateComplexDirectory(resultPath.Left(pos));
  }

  COutFileStream *outStreamSpec = NULL;
  COutMultiVolStream *volStreamSpec = NULL;

  if (volumesSizes.Size() == 0)
  {
    if (stdOutMode)
      outStream = new CStdOutFileStream;
    else
    {
      outStreamSpec = new COutFileStream;
      outStream = outStreamSpec;
      bool isOK = false;
      UString realPath;
      for (int i = 0; i < (1 << 16); i++)
      {
        if (archivePath.Temp)
        {
          if (i > 0)
          {
            wchar_t s[32];
            ConvertUInt64ToString(i, s);
            archivePath.TempPostfix = s;
          }
          realPath = archivePath.GetTempPath();
        }
        else
          realPath = archivePath.GetFinalPath();
        if (outStreamSpec->Create(realPath, false))
        {
          tempFiles.Paths.Add(realPath);
          isOK = true;
          break;
        }
        if (::GetLastError() != ERROR_FILE_EXISTS)
          break;
        if (!archivePath.Temp)
          break;
      }
      if (!isOK)
      {
        errorInfo.SystemError = ::GetLastError();
        errorInfo.FileName = realPath;
        errorInfo.Message = L"Can not open file";
        return E_FAIL;
      }
    }
  }
  else
  {
    if (stdOutMode)
      return E_FAIL;
    volStreamSpec = new COutMultiVolStream;
    outStream = volStreamSpec;
    volStreamSpec->Sizes = volumesSizes;
    volStreamSpec->Prefix = archivePath.GetFinalPath() + UString(L".");
    volStreamSpec->TempFiles = &tempFiles;
    volStreamSpec->Init();

    /*
    updateCallbackSpec->VolumesSizes = volumesSizes;
    updateCallbackSpec->VolName = archivePath.Prefix + archivePath.Name;
    if (!archivePath.VolExtension.IsEmpty())
      updateCallbackSpec->VolExt = UString(L'.') + archivePath.VolExtension;
    */
  }

  RINOK(SetProperties(outArchive, compressionMethod.Properties));

  if (sfxMode)
  {
    CInFileStream *sfxStreamSpec = new CInFileStream;
    CMyComPtr<IInStream> sfxStream(sfxStreamSpec);
    if (!sfxStreamSpec->Open(sfxModule))
    {
      errorInfo.SystemError = ::GetLastError();
      errorInfo.Message = L"Can't open sfx module";
      errorInfo.FileName = sfxModule;
      return E_FAIL;
    }

    CMyComPtr<ISequentialOutStream> sfxOutStream;
    COutFileStream *outStreamSpec = NULL;
    if (volumesSizes.Size() == 0)
      sfxOutStream = outStream;
    else
    {
      outStreamSpec = new COutFileStream;
      sfxOutStream = outStreamSpec;
      UString realPath = archivePath.GetFinalPath();
      if (!outStreamSpec->Create(realPath, false))
      {
        errorInfo.SystemError = ::GetLastError();
        errorInfo.FileName = realPath;
        errorInfo.Message = L"Can not open file";
        return E_FAIL;
      }
    }
    RINOK(CopyBlock(sfxStream, sfxOutStream));
    if (outStreamSpec)
    {
      RINOK(outStreamSpec->Close());
    }
  }

  HRESULT result = outArchive->UpdateItems(outStream, updatePairs2.Size(), updateCallback);
  callback->Finilize();
  RINOK(result);
  if (outStreamSpec)
    result = outStreamSpec->Close();
  else if (volStreamSpec)
    result = volStreamSpec->Close();
  return result;
}

HRESULT EnumerateInArchiveItems(const NWildcard::CCensor &censor,
    IInArchive *archive,
    const UString &defaultItemName,
    const NWindows::NFile::NFind::CFileInfoW &archiveFileInfo,
    CObjectVector<CArchiveItem> &archiveItems)
{
  archiveItems.Clear();
  UInt32 numItems;
  RINOK(archive->GetNumberOfItems(&numItems));
  archiveItems.Reserve(numItems);
  for(UInt32 i = 0; i < numItems; i++)
  {
    CArchiveItem ai;

    RINOK(GetArchiveItemPath(archive, i, ai.Name));
    RINOK(IsArchiveItemFolder(archive, i, ai.IsDirectory));
    ai.Censored = censor.CheckPath(ai.Name.IsEmpty() ? defaultItemName : ai.Name, !ai.IsDirectory);
    RINOK(GetArchiveItemFileTime(archive, i, 
        archiveFileInfo.LastWriteTime, ai.LastWriteTime));

    CPropVariant propertySize;
    RINOK(archive->GetProperty(i, kpidSize, &propertySize));
    ai.SizeIsDefined = (propertySize.vt != VT_EMPTY);
    if (ai.SizeIsDefined)
      ai.Size = ConvertPropVariantToUInt64(propertySize);

    ai.IndexInServer = i;
    archiveItems.Add(ai);
  }
  return S_OK;
}


static HRESULT UpdateWithItemLists(
    CCodecs *codecs,
    CUpdateOptions &options,
    IInArchive *archive, 
    const CObjectVector<CArchiveItem> &archiveItems,
    const CObjectVector<CDirItem> &dirItems,
    CTempFiles &tempFiles,
    CUpdateErrorInfo &errorInfo,
    IUpdateCallbackUI2 *callback)
{
  for(int i = 0; i < options.Commands.Size(); i++)
  {
    CUpdateArchiveCommand &command = options.Commands[i];
    if (options.StdOutMode)
    {
      RINOK(callback->StartArchive(0, archive != 0));
    }
    else
    {
      RINOK(callback->StartArchive(command.ArchivePath.GetFinalPath(), 
          i == 0 && options.UpdateArchiveItself && archive != 0));
    }

    RINOK(Compress(
        codecs,
        command.ActionSet, archive,
        options.MethodMode, 
        command.ArchivePath, 
        archiveItems, 
        options.OpenShareForWrite,
        options.StdInMode, 
        /* options.StdInFileName, */
        options.StdOutMode,
        dirItems, 
        options.SfxMode, options.SfxModule, 
        options.VolumesSizes,
        tempFiles,
        errorInfo, callback));

    RINOK(callback->FinishArchive());
  }
  return S_OK;
}

#ifdef _WIN32
class CCurrentDirRestorer
{
  UString m_CurrentDirectory;
public:
  CCurrentDirRestorer()
    { NFile::NDirectory::MyGetCurrentDirectory(m_CurrentDirectory); }
  ~CCurrentDirRestorer()
    { RestoreDirectory();}
  bool RestoreDirectory()
    { return BOOLToBool(NFile::NDirectory::MySetCurrentDirectory(m_CurrentDirectory)); }
};
#endif

struct CEnumDirItemUpdateCallback: public IEnumDirItemCallback
{
  IUpdateCallbackUI2 *Callback;
  HRESULT CheckBreak() { return Callback->CheckBreak(); }
};

HRESULT UpdateArchive(
    CCodecs *codecs,
    const NWildcard::CCensor &censor, 
    CUpdateOptions &options,
    CUpdateErrorInfo &errorInfo,
    IOpenCallbackUI *openCallback,
    IUpdateCallbackUI2 *callback)
{
  if (options.StdOutMode && options.EMailMode)
    return E_FAIL;

  if (options.VolumesSizes.Size() > 0 && (options.EMailMode || options.SfxMode))
    return E_NOTIMPL;

  if (options.SfxMode)
  {
    CProperty property;
    property.Name = L"rsfx";
    property.Value = L"on";
    options.MethodMode.Properties.Add(property);
    if (options.SfxModule.IsEmpty())
    {
      errorInfo.Message = L"sfx file is not specified";
      return E_FAIL;
    }
    UString name = options.SfxModule;
    if (!NDirectory::MySearchPath(NULL, name, NULL, options.SfxModule))
    {
      errorInfo.Message = L"can't find specified sfx module";
      return E_FAIL;
    }
  }

  const UString archiveName = options.ArchivePath.GetFinalPath();

  UString defaultItemName;
  NFind::CFileInfoW archiveFileInfo;

  CArchiveLink archiveLink;
  IInArchive *archive = 0;
  if (NFind::FindFile(archiveName, archiveFileInfo))
  {
    if (archiveFileInfo.IsDirectory())
      throw "there is no such archive";
    if (options.VolumesSizes.Size() > 0)
      return E_NOTIMPL;
    HRESULT result = MyOpenArchive(codecs, archiveName, archiveLink, openCallback);
    RINOK(callback->OpenResult(archiveName, result));
    RINOK(result);
    if (archiveLink.VolumePaths.Size() > 1)
    {
      errorInfo.SystemError = (DWORD)E_NOTIMPL;
      errorInfo.Message = L"Updating for multivolume archives is not implemented";
      return E_NOTIMPL;
    }
    archive = archiveLink.GetArchive();
    defaultItemName = archiveLink.GetDefaultItemName();
  }
  else
  {
    /*
    if (archiveType.IsEmpty())
      throw "type of archive is not specified";
    */
  }

  CObjectVector<CDirItem> dirItems;
  if (options.StdInMode)
  {
    CDirItem item;
    item.FullPath = item.Name = options.StdInFileName;
    item.Size = (UInt64)(Int64)-1;
    item.Attributes = 0;
    SYSTEMTIME st;
    FILETIME ft;
    GetSystemTime(&st);
    SystemTimeToFileTime(&st, &ft);
    item.CreationTime = item.LastAccessTime = item.LastWriteTime = ft;
    dirItems.Add(item);
  }
  else
  {
    bool needScanning = false;
    for(int i = 0; i < options.Commands.Size(); i++)
      if (options.Commands[i].ActionSet.NeedScanning())
        needScanning = true;
    if (needScanning)
    {
      CEnumDirItemUpdateCallback enumCallback;
      enumCallback.Callback = callback;
      RINOK(callback->StartScanning());
      UStringVector errorPaths;
      CRecordVector<DWORD> errorCodes;
      HRESULT res = EnumerateItems(censor, dirItems, &enumCallback, errorPaths, errorCodes);
      for (int i = 0; i < errorPaths.Size(); i++)
      {
        RINOK(callback->CanNotFindError(errorPaths[i], errorCodes[i]));
      }
      if(res != S_OK) 
      {
        errorInfo.Message = L"Scanning error";
        // errorInfo.FileName = errorPath;
        return res;
      }
      RINOK(callback->FinishScanning());
    }
  }

  UString tempDirPrefix;
  bool usesTempDir = false;
  
  #ifdef _WIN32
  NDirectory::CTempDirectoryW tempDirectory;
  if (options.EMailMode && options.EMailRemoveAfter)
  {
    tempDirectory.Create(kTempFolderPrefix);
    tempDirPrefix = tempDirectory.GetPath();
    NormalizeDirPathPrefix(tempDirPrefix);
    usesTempDir = true;
  }
  #endif

  CTempFiles tempFiles;

  bool createTempFile = false;
  if(!options.StdOutMode && options.UpdateArchiveItself)
  {
    CArchivePath &ap = options.Commands[0].ArchivePath;
    ap = options.ArchivePath;
    // if ((archive != 0 && !usesTempDir) || !options.WorkingDir.IsEmpty())
    if ((archive != 0 || !options.WorkingDir.IsEmpty()) && !usesTempDir && options.VolumesSizes.Size() == 0)
    {
      createTempFile = true;
      ap.Temp = true;
      if (!options.WorkingDir.IsEmpty())
      {
        ap.TempPrefix = options.WorkingDir;
        NormalizeDirPathPrefix(ap.TempPrefix);
      }
    }
  }

  for(int i = 0; i < options.Commands.Size(); i++)
  {
    CArchivePath &ap = options.Commands[i].ArchivePath;
    if (usesTempDir)
    {
      // Check it
      ap.Prefix = tempDirPrefix;
      // ap.Temp = true;
      // ap.TempPrefix = tempDirPrefix;
    }
    if (i > 0 || !createTempFile)
    {
      const UString &path = ap.GetFinalPath();
      if (NFind::DoesFileExist(path))
      {
        errorInfo.SystemError = 0;
        errorInfo.Message = L"File already exists";
        errorInfo.FileName = path;
        return E_FAIL;
      }
    }
  }

  CObjectVector<CArchiveItem> archiveItems;
  if (archive != NULL)
  {
    RINOK(EnumerateInArchiveItems(censor, 
        archive, defaultItemName, archiveFileInfo, archiveItems));
  }

  RINOK(UpdateWithItemLists(codecs, options, archive, archiveItems, dirItems, 
      tempFiles, errorInfo, callback));

  if (archive != NULL)
  {
    RINOK(archiveLink.Close());
    archiveLink.Release();
  }

  tempFiles.Paths.Clear();
  if(createTempFile)
  {
    try
    {
      CArchivePath &ap = options.Commands[0].ArchivePath;
      const UString &tempPath = ap.GetTempPath();
      if (archive != NULL)
        if (!NDirectory::DeleteFileAlways(archiveName))
        {
          errorInfo.SystemError = ::GetLastError();
          errorInfo.Message = L"delete file error";
          errorInfo.FileName = archiveName;
          return E_FAIL;
        }
      if (!NDirectory::MyMoveFile(tempPath, archiveName))
      {
        errorInfo.SystemError = ::GetLastError();
        errorInfo.Message = L"move file error";
        errorInfo.FileName = tempPath;
        errorInfo.FileName2 = archiveName;
        return E_FAIL;
      }
    }
    catch(...)
    {
      throw;
    }
  }

  #ifdef _WIN32
  if (options.EMailMode)
  {
    NDLL::CLibrary mapiLib;
    if (!mapiLib.Load(TEXT("Mapi32.dll")))
    {
      errorInfo.SystemError = ::GetLastError();
      errorInfo.Message = L"can not load Mapi32.dll";
      return E_FAIL;
    }
    LPMAPISENDDOCUMENTS fnSend = (LPMAPISENDDOCUMENTS)
        mapiLib.GetProcAddress("MAPISendDocuments");
    if (fnSend == 0)
    {
      errorInfo.SystemError = ::GetLastError();
      errorInfo.Message = L"can not find MAPISendDocuments function";
      return E_FAIL;
    }
    UStringVector fullPaths;
    int i;
    for(i = 0; i < options.Commands.Size(); i++)
    {
      CArchivePath &ap = options.Commands[i].ArchivePath;
      UString arcPath;
      if(!NFile::NDirectory::MyGetFullPathName(ap.GetFinalPath(), arcPath))
      {
        errorInfo.SystemError = ::GetLastError();
        return E_FAIL;
      }
      fullPaths.Add(arcPath);
    }
    CCurrentDirRestorer curDirRestorer;
    for(i = 0; i < fullPaths.Size(); i++)
    {
      UString arcPath = fullPaths[i];
      UString fileName = ExtractFileNameFromPath(arcPath);
      AString path = GetAnsiString(arcPath);
      AString name = GetAnsiString(fileName);
      // Warning!!! MAPISendDocuments function changes Current directory
      fnSend(0, ";", (LPSTR)(LPCSTR)path, (LPSTR)(LPCSTR)name, 0); 
    }
  }
  #endif
  return S_OK;
}