853 lines
22 KiB
C++
853 lines
22 KiB
C++
|
// 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;
|
||
|
}
|
||
|
|