mame/3rdparty/lzma/CPP/7zip/UI/Common/Update.cpp
2023-12-06 07:58:49 +11:00

1858 lines
47 KiB
C++

// Update.cpp
#include "StdAfx.h"
// #include <stdio.h>
#include "Update.h"
#include "../../../Common/StringConvert.h"
#include "../../../Windows/DLL.h"
#include "../../../Windows/FileDir.h"
#include "../../../Windows/FileFind.h"
#include "../../../Windows/FileName.h"
#include "../../../Windows/PropVariant.h"
#include "../../../Windows/PropVariantConv.h"
#include "../../../Windows/TimeUtils.h"
#include "../../Common/FileStreams.h"
#include "../../Common/LimitedStreams.h"
#include "../../Common/MultiOutStream.h"
#include "../../Common/StreamUtils.h"
#include "../../Compress/CopyCoder.h"
#include "../Common/DirItem.h"
#include "../Common/EnumDirItems.h"
#include "../Common/OpenArchive.h"
#include "../Common/UpdateProduce.h"
#include "EnumDirItems.h"
#include "SetProperties.h"
#include "TempFiles.h"
#include "UpdateCallback.h"
static const char * const kUpdateIsNotSupoorted =
"update operations are not supported for this archive";
static const char * const kUpdateIsNotSupported_MultiVol =
"Updating for multivolume archives is not implemented";
using namespace NWindows;
using namespace NCOM;
using namespace NFile;
using namespace NDir;
using namespace NName;
#ifdef _WIN32
static CFSTR const kTempFolderPrefix = FTEXT("7zE");
#endif
void CUpdateErrorInfo::SetFromLastError(const char *message)
{
SystemError = ::GetLastError();
Message = message;
}
HRESULT CUpdateErrorInfo::SetFromLastError(const char *message, const FString &fileName)
{
SetFromLastError(message);
FileNames.Add(fileName);
return Get_HRESULT_Error();
}
HRESULT CUpdateErrorInfo::SetFromError_DWORD(const char *message, const FString &fileName, DWORD error)
{
Message = message;
FileNames.Add(fileName);
SystemError = error;
return Get_HRESULT_Error();
}
using namespace NUpdateArchive;
struct CMultiOutStream_Rec
{
CMultiOutStream *Spec;
CMyComPtr<IOutStream> Ref;
};
struct CMultiOutStream_Bunch
{
CObjectVector<CMultiOutStream_Rec> Items;
HRESULT Destruct()
{
HRESULT hres = S_OK;
FOR_VECTOR (i, Items)
{
CMultiOutStream_Rec &rec = Items[i];
if (rec.Ref)
{
const HRESULT hres2 = rec.Spec->Destruct();
if (hres == S_OK)
hres = hres2;
}
}
Items.Clear();
return hres;
}
void DisableDeletion()
{
FOR_VECTOR (i, Items)
{
CMultiOutStream_Rec &rec = Items[i];
if (rec.Ref)
rec.Spec->NeedDelete = false;
}
}
};
void CArchivePath::ParseFromPath(const UString &path, EArcNameMode mode)
{
OriginalPath = path;
SplitPathToParts_2(path, Prefix, Name);
if (mode == k_ArcNameMode_Add)
return;
if (mode != k_ArcNameMode_Exact)
{
int dotPos = Name.ReverseFind_Dot();
if (dotPos < 0)
return;
if ((unsigned)dotPos == Name.Len() - 1)
Name.DeleteBack();
else
{
const UString ext = Name.Ptr((unsigned)(dotPos + 1));
if (BaseExtension.IsEqualTo_NoCase(ext))
{
BaseExtension = ext;
Name.DeleteFrom((unsigned)dotPos);
return;
}
}
}
BaseExtension.Empty();
}
UString CArchivePath::GetFinalPath() const
{
UString path = GetPathWithoutExt();
if (!BaseExtension.IsEmpty())
{
path.Add_Dot();
path += BaseExtension;
}
return path;
}
UString CArchivePath::GetFinalVolPath() const
{
UString path = GetPathWithoutExt();
// if BaseExtension is empty, we must ignore VolExtension also.
if (!BaseExtension.IsEmpty())
{
path.Add_Dot();
path += VolExtension;
}
return path;
}
FString CArchivePath::GetTempPath() const
{
FString path = TempPrefix;
path += us2fs(Name);
if (!BaseExtension.IsEmpty())
{
path.Add_Dot();
path += us2fs(BaseExtension);
}
path += ".tmp";
path += TempPostfix;
return path;
}
static const char * const kDefaultArcType = "7z";
static const char * const kDefaultArcExt = "7z";
static const char * const kSFXExtension =
#ifdef _WIN32
"exe";
#else
"";
#endif
bool CUpdateOptions::InitFormatIndex(const CCodecs *codecs,
const CObjectVector<COpenType> &types, const UString &arcPath)
{
if (types.Size() > 1)
return false;
// int arcTypeIndex = -1;
if (types.Size() != 0)
{
MethodMode.Type = types[0];
MethodMode.Type_Defined = true;
}
if (MethodMode.Type.FormatIndex < 0)
{
// MethodMode.Type = -1;
MethodMode.Type = COpenType();
if (ArcNameMode != k_ArcNameMode_Add)
{
MethodMode.Type.FormatIndex = codecs->FindFormatForArchiveName(arcPath);
if (MethodMode.Type.FormatIndex >= 0)
MethodMode.Type_Defined = true;
}
}
return true;
}
bool CUpdateOptions::SetArcPath(const CCodecs *codecs, const UString &arcPath)
{
UString typeExt;
int formatIndex = MethodMode.Type.FormatIndex;
if (formatIndex < 0)
{
typeExt = kDefaultArcExt;
}
else
{
const CArcInfoEx &arcInfo = codecs->Formats[(unsigned)formatIndex];
if (!arcInfo.UpdateEnabled)
return false;
typeExt = arcInfo.GetMainExt();
}
UString ext = typeExt;
if (SfxMode)
ext = kSFXExtension;
ArchivePath.BaseExtension = ext;
ArchivePath.VolExtension = typeExt;
ArchivePath.ParseFromPath(arcPath, ArcNameMode);
FOR_VECTOR (i, Commands)
{
CUpdateArchiveCommand &uc = Commands[i];
uc.ArchivePath.BaseExtension = ext;
uc.ArchivePath.VolExtension = typeExt;
uc.ArchivePath.ParseFromPath(uc.UserArchivePath, ArcNameMode);
}
return true;
}
struct CUpdateProduceCallbackImp Z7_final: public IUpdateProduceCallback
{
const CObjectVector<CArcItem> *_arcItems;
CDirItemsStat *_stat;
IUpdateCallbackUI *_callback;
CUpdateProduceCallbackImp(
const CObjectVector<CArcItem> *a,
CDirItemsStat *stat,
IUpdateCallbackUI *callback):
_arcItems(a),
_stat(stat),
_callback(callback) {}
virtual HRESULT ShowDeleteFile(unsigned arcIndex) Z7_override;
};
HRESULT CUpdateProduceCallbackImp::ShowDeleteFile(unsigned arcIndex)
{
const CArcItem &ai = (*_arcItems)[arcIndex];
{
CDirItemsStat &stat = *_stat;
if (ai.IsDir)
stat.NumDirs++;
else if (ai.IsAltStream)
{
stat.NumAltStreams++;
stat.AltStreamsSize += ai.Size;
}
else
{
stat.NumFiles++;
stat.FilesSize += ai.Size;
}
}
return _callback->ShowDeleteFile(ai.Name, ai.IsDir);
}
bool CRenamePair::Prepare()
{
if (RecursedType != NRecursedType::kNonRecursed)
return false;
if (!WildcardParsing)
return true;
return !DoesNameContainWildcard(OldName);
}
extern bool g_CaseSensitive;
static unsigned CompareTwoNames(const wchar_t *s1, const wchar_t *s2)
{
for (unsigned i = 0;; i++)
{
wchar_t c1 = s1[i];
wchar_t c2 = s2[i];
if (c1 == 0 || c2 == 0)
return i;
if (c1 == c2)
continue;
if (!g_CaseSensitive && (MyCharUpper(c1) == MyCharUpper(c2)))
continue;
if (IsPathSepar(c1) && IsPathSepar(c2))
continue;
return i;
}
}
bool CRenamePair::GetNewPath(bool isFolder, const UString &src, UString &dest) const
{
unsigned num = CompareTwoNames(OldName, src);
if (OldName[num] == 0)
{
if (src[num] != 0 && !IsPathSepar(src[num]) && num != 0 && !IsPathSepar(src[num - 1]))
return false;
}
else
{
// OldName[num] != 0
// OldName = "1\1a.txt"
// src = "1"
if (!isFolder
|| src[num] != 0
|| !IsPathSepar(OldName[num])
|| OldName[num + 1] != 0)
return false;
}
dest = NewName + src.Ptr(num);
return true;
}
#ifdef SUPPORT_ALT_STREAMS
int FindAltStreamColon_in_Path(const wchar_t *path);
#endif
static HRESULT Compress(
const CUpdateOptions &options,
bool isUpdatingItself,
CCodecs *codecs,
const CActionSet &actionSet,
const CArc *arc,
CArchivePath &archivePath,
const CObjectVector<CArcItem> &arcItems,
Byte *processedItemsStatuses,
const CDirItems &dirItems,
const CDirItem *parentDirItem,
CTempFiles &tempFiles,
CMultiOutStream_Bunch &multiStreams,
CUpdateErrorInfo &errorInfo,
IUpdateCallbackUI *callback,
CFinishArchiveStat &st)
{
CMyComPtr<IOutArchive> outArchive;
int formatIndex = options.MethodMode.Type.FormatIndex;
if (arc)
{
formatIndex = arc->FormatIndex;
if (formatIndex < 0)
return E_NOTIMPL;
CMyComPtr<IInArchive> archive2 = arc->Archive;
HRESULT result = archive2.QueryInterface(IID_IOutArchive, &outArchive);
if (result != S_OK)
throw kUpdateIsNotSupoorted;
}
else
{
RINOK(codecs->CreateOutArchive((unsigned)formatIndex, outArchive))
#ifdef Z7_EXTERNAL_CODECS
{
CMyComPtr<ISetCompressCodecsInfo> setCompressCodecsInfo;
outArchive.QueryInterface(IID_ISetCompressCodecsInfo, (void **)&setCompressCodecsInfo);
if (setCompressCodecsInfo)
{
RINOK(setCompressCodecsInfo->SetCompressCodecsInfo(codecs))
}
}
#endif
}
if (!outArchive)
throw kUpdateIsNotSupoorted;
// we need to set properties to get fileTimeType.
RINOK(SetProperties(outArchive, options.MethodMode.Properties))
NFileTimeType::EEnum fileTimeType;
{
/*
how we compare file_in_archive::MTime with dirItem.MTime
for GetUpdatePairInfoList():
if (kpidMTime is not defined), external MTime of archive is used.
before 22.00:
if (kpidTimeType is defined)
{
kpidTimeType is used as precision.
(kpidTimeType > kDOS) is not allowed.
}
else GetFileTimeType() value is used as precision.
22.00:
if (kpidMTime is defined)
{
if (kpidMTime::precision != 0), then kpidMTime::precision is used as precision.
else
{
if (kpidTimeType is defined), kpidTimeType is used as precision.
else GetFileTimeType() value is used as precision.
}
}
else external MTime of archive is used as precision.
*/
UInt32 value;
RINOK(outArchive->GetFileTimeType(&value))
// we support any future fileType here.
fileTimeType = (NFileTimeType::EEnum)value;
/*
old 21.07 code:
switch (value)
{
case NFileTimeType::kWindows:
case NFileTimeType::kUnix:
case NFileTimeType::kDOS:
fileTimeType = (NFileTimeType::EEnum)value;
break;
default:
return E_FAIL;
}
*/
}
// bool noTimestampExpected = false;
{
const CArcInfoEx &arcInfo = codecs->Formats[(unsigned)formatIndex];
// if (arcInfo.Flags_KeepName()) noTimestampExpected = true;
if (arcInfo.Is_Xz() ||
arcInfo.Is_BZip2())
{
/* 7-zip before 22.00 returns NFileTimeType::kUnix for xz and bzip2,
but we want to set timestamp without reduction to unix. */
// noTimestampExpected = true;
fileTimeType = NFileTimeType::kNotDefined; // it means not defined
}
if (options.AltStreams.Val && !arcInfo.Flags_AltStreams())
return E_NOTIMPL;
if (options.NtSecurity.Val && !arcInfo.Flags_NtSecurity())
return E_NOTIMPL;
if (options.DeleteAfterCompressing && arcInfo.Flags_HashHandler())
return E_NOTIMPL;
}
CRecordVector<CUpdatePair2> updatePairs2;
UStringVector newNames;
CArcToDoStat stat2;
if (options.RenamePairs.Size() != 0)
{
FOR_VECTOR (i, arcItems)
{
const CArcItem &ai = arcItems[i];
bool needRename = false;
UString dest;
if (ai.Censored)
{
FOR_VECTOR (j, options.RenamePairs)
{
const CRenamePair &rp = options.RenamePairs[j];
if (rp.GetNewPath(ai.IsDir, ai.Name, dest))
{
needRename = true;
break;
}
#ifdef SUPPORT_ALT_STREAMS
if (ai.IsAltStream)
{
int colonPos = FindAltStreamColon_in_Path(ai.Name);
if (colonPos >= 0)
{
UString mainName = ai.Name.Left((unsigned)colonPos);
/*
actually we must improve that code to support cases
with folder renaming like: rn arc dir1\ dir2\
*/
if (rp.GetNewPath(false, mainName, dest))
{
needRename = true;
dest += ':';
dest += ai.Name.Ptr((unsigned)(colonPos + 1));
break;
}
}
}
#endif
}
}
CUpdatePair2 up2;
up2.SetAs_NoChangeArcItem(ai.IndexInServer);
if (needRename)
{
up2.NewProps = true;
RINOK(arc->IsItem_Anti(i, up2.IsAnti))
up2.NewNameIndex = (int)newNames.Add(dest);
}
updatePairs2.Add(up2);
}
}
else
{
CRecordVector<CUpdatePair> updatePairs;
GetUpdatePairInfoList(dirItems, arcItems, fileTimeType, updatePairs); // must be done only once!!!
CUpdateProduceCallbackImp upCallback(&arcItems, &stat2.DeleteData, callback);
UpdateProduce(updatePairs, actionSet, updatePairs2, isUpdatingItself ? &upCallback : NULL);
}
{
FOR_VECTOR (i, updatePairs2)
{
const CUpdatePair2 &up = updatePairs2[i];
// 17.01: anti-item is (up.NewData && (p.UseArcProps in most cases))
if (up.NewData && !up.UseArcProps)
{
if (up.ExistOnDisk())
{
CDirItemsStat2 &stat = stat2.NewData;
const CDirItem &di = dirItems.Items[(unsigned)up.DirIndex];
if (di.IsDir())
{
if (up.IsAnti)
stat.Anti_NumDirs++;
else
stat.NumDirs++;
}
#ifdef _WIN32
else if (di.IsAltStream)
{
if (up.IsAnti)
stat.Anti_NumAltStreams++;
else
{
stat.NumAltStreams++;
stat.AltStreamsSize += di.Size;
}
}
#endif
else
{
if (up.IsAnti)
stat.Anti_NumFiles++;
else
{
stat.NumFiles++;
stat.FilesSize += di.Size;
}
}
}
}
else if (up.ArcIndex >= 0)
{
CDirItemsStat2 &stat = *(up.NewData ? &stat2.NewData : &stat2.OldData);
const CArcItem &ai = arcItems[(unsigned)up.ArcIndex];
if (ai.IsDir)
{
if (up.IsAnti)
stat.Anti_NumDirs++;
else
stat.NumDirs++;
}
else if (ai.IsAltStream)
{
if (up.IsAnti)
stat.Anti_NumAltStreams++;
else
{
stat.NumAltStreams++;
stat.AltStreamsSize += ai.Size;
}
}
else
{
if (up.IsAnti)
stat.Anti_NumFiles++;
else
{
stat.NumFiles++;
stat.FilesSize += ai.Size;
}
}
}
}
RINOK(callback->SetNumItems(stat2))
}
CArchiveUpdateCallback *updateCallbackSpec = new CArchiveUpdateCallback;
CMyComPtr<IArchiveUpdateCallback> updateCallback(updateCallbackSpec);
updateCallbackSpec->PreserveATime = options.PreserveATime;
updateCallbackSpec->ShareForWrite = options.OpenShareForWrite;
updateCallbackSpec->StopAfterOpenError = options.StopAfterOpenError;
updateCallbackSpec->StdInMode = options.StdInMode;
updateCallbackSpec->Callback = callback;
if (arc)
{
// we set Archive to allow to transfer GetProperty requests back to DLL.
updateCallbackSpec->Archive = arc->Archive;
}
updateCallbackSpec->DirItems = &dirItems;
updateCallbackSpec->ParentDirItem = parentDirItem;
updateCallbackSpec->StoreNtSecurity = options.NtSecurity.Val;
updateCallbackSpec->StoreHardLinks = options.HardLinks.Val;
updateCallbackSpec->StoreSymLinks = options.SymLinks.Val;
updateCallbackSpec->StoreOwnerName = options.StoreOwnerName.Val;
updateCallbackSpec->StoreOwnerId = options.StoreOwnerId.Val;
updateCallbackSpec->Arc = arc;
updateCallbackSpec->ArcItems = &arcItems;
updateCallbackSpec->UpdatePairs = &updatePairs2;
updateCallbackSpec->ProcessedItemsStatuses = processedItemsStatuses;
{
const UString arcPath = archivePath.GetFinalPath();
updateCallbackSpec->ArcFileName = ExtractFileNameFromPath(arcPath);
}
if (options.RenamePairs.Size() != 0)
updateCallbackSpec->NewNames = &newNames;
if (options.SetArcMTime)
{
// updateCallbackSpec->Need_ArcMTime_Report = true;
updateCallbackSpec->Need_LatestMTime = true;
}
CMyComPtr<IOutStream> outSeekStream;
CMyComPtr<ISequentialOutStream> outStream;
if (!options.StdOutMode)
{
FString dirPrefix;
if (!GetOnlyDirPrefix(us2fs(archivePath.GetFinalPath()), dirPrefix))
throw 1417161;
CreateComplexDir(dirPrefix);
}
COutFileStream *outStreamSpec = NULL;
CStdOutFileStream *stdOutFileStreamSpec = NULL;
CMultiOutStream *volStreamSpec = NULL;
if (options.VolumesSizes.Size() == 0)
{
if (options.StdOutMode)
{
stdOutFileStreamSpec = new CStdOutFileStream;
outStream = stdOutFileStreamSpec;
}
else
{
outStreamSpec = new COutFileStream;
outSeekStream = outStreamSpec;
outStream = outSeekStream;
bool isOK = false;
FString realPath;
for (unsigned i = 0; i < (1 << 16); i++)
{
if (archivePath.Temp)
{
if (i > 0)
{
archivePath.TempPostfix.Empty();
archivePath.TempPostfix.Add_UInt32(i);
}
realPath = archivePath.GetTempPath();
}
else
realPath = us2fs(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)
return errorInfo.SetFromLastError("cannot open file", realPath);
}
}
else
{
if (options.StdOutMode)
return E_FAIL;
if (arc && arc->GetGlobalOffset() > 0)
return E_NOTIMPL;
volStreamSpec = new CMultiOutStream();
outSeekStream = volStreamSpec;
outStream = outSeekStream;
volStreamSpec->Prefix = us2fs(archivePath.GetFinalVolPath());
volStreamSpec->Prefix.Add_Dot();
volStreamSpec->Init(options.VolumesSizes);
{
CMultiOutStream_Rec &rec = multiStreams.Items.AddNew();
rec.Spec = volStreamSpec;
rec.Ref = rec.Spec;
}
/*
updateCallbackSpec->VolumesSizes = volumesSizes;
updateCallbackSpec->VolName = archivePath.Prefix + archivePath.Name;
if (!archivePath.VolExtension.IsEmpty())
updateCallbackSpec->VolExt = UString('.') + archivePath.VolExtension;
*/
}
if (options.SfxMode)
{
CInFileStream *sfxStreamSpec = new CInFileStream;
CMyComPtr<IInStream> sfxStream(sfxStreamSpec);
if (!sfxStreamSpec->Open(options.SfxModule))
return errorInfo.SetFromLastError("cannot open SFX module", options.SfxModule);
CMyComPtr<ISequentialOutStream> sfxOutStream;
COutFileStream *outStreamSpec2 = NULL;
if (options.VolumesSizes.Size() == 0)
sfxOutStream = outStream;
else
{
outStreamSpec2 = new COutFileStream;
sfxOutStream = outStreamSpec2;
const FString realPath = us2fs(archivePath.GetFinalPath());
if (!outStreamSpec2->Create(realPath, false))
return errorInfo.SetFromLastError("cannot open file", realPath);
}
{
UInt64 sfxSize;
RINOK(sfxStreamSpec->GetSize(&sfxSize))
RINOK(callback->WriteSfx(fs2us(options.SfxModule), sfxSize))
}
RINOK(NCompress::CopyStream(sfxStream, sfxOutStream, NULL))
if (outStreamSpec2)
{
RINOK(outStreamSpec2->Close())
}
}
CMyComPtr<ISequentialOutStream> tailStream;
if (options.SfxMode || !arc || arc->ArcStreamOffset == 0)
tailStream = outStream;
else
{
// Int64 globalOffset = arc->GetGlobalOffset();
RINOK(InStream_SeekToBegin(arc->InStream))
RINOK(NCompress::CopyStream_ExactSize(arc->InStream, outStream, arc->ArcStreamOffset, NULL))
if (options.StdOutMode)
tailStream = outStream;
else
{
CTailOutStream *tailStreamSpec = new CTailOutStream;
tailStream = tailStreamSpec;
tailStreamSpec->Stream = outSeekStream;
tailStreamSpec->Offset = arc->ArcStreamOffset;
tailStreamSpec->Init();
}
}
CFiTime ft;
FiTime_Clear(ft);
bool ft_Defined = false;
{
FOR_VECTOR (i, updatePairs2)
{
const CUpdatePair2 &pair2 = updatePairs2[i];
CFiTime ft2;
FiTime_Clear(ft2);
bool ft2_Defined = false;
/* we use full precision of dirItem, if dirItem is defined
and (dirItem will be used or dirItem is sameTime in dir and arc */
if (pair2.DirIndex >= 0 &&
(pair2.NewProps || pair2.IsSameTime))
{
ft2 = dirItems.Items[(unsigned)pair2.DirIndex].MTime;
ft2_Defined = true;
}
else if (pair2.UseArcProps && pair2.ArcIndex >= 0)
{
const CArcItem &arcItem = arcItems[(unsigned)pair2.ArcIndex];
if (arcItem.MTime.Def)
{
arcItem.MTime.Write_To_FiTime(ft2);
ft2_Defined = true;
}
}
if (ft2_Defined)
{
if (!ft_Defined || Compare_FiTime(&ft, &ft2) < 0)
{
ft = ft2;
ft_Defined = true;
}
}
}
/*
if (fileTimeType != NFileTimeType::kNotDefined)
FiTime_Normalize_With_Prec(ft, fileTimeType);
*/
}
if (volStreamSpec && options.SetArcMTime && ft_Defined)
{
volStreamSpec->MTime = ft;
volStreamSpec->MTime_Defined = true;
}
HRESULT result = outArchive->UpdateItems(tailStream, updatePairs2.Size(), updateCallback);
// callback->Finalize();
RINOK(result)
if (!updateCallbackSpec->AreAllFilesClosed())
{
errorInfo.Message = "There are unclosed input file:";
errorInfo.FileNames = updateCallbackSpec->_openFiles_Paths;
return E_FAIL;
}
if (options.SetArcMTime)
{
// bool needNormalizeAfterStream;
// needParse;
/*
if (updateCallbackSpec->ArcMTime_WasReported)
{
isDefined = updateCallbackSpec->Reported_ArcMTime.Def;
if (isDefined)
updateCallbackSpec->Reported_ArcMTime.Write_To_FiTime(ft);
else
fileTimeType = NFileTimeType::kNotDefined;
}
if (!isDefined)
*/
{
if (updateCallbackSpec->LatestMTime_Defined)
{
// CArcTime at = StreamCallback_ArcMTime;
// updateCallbackSpec->StreamCallback_ArcMTime.Write_To_FiTime(ft);
// we must normalize with precision from archive;
if (!ft_Defined || Compare_FiTime(&ft, &updateCallbackSpec->LatestMTime) < 0)
ft = updateCallbackSpec->LatestMTime;
ft_Defined = true;
}
/*
if (fileTimeType != NFileTimeType::kNotDefined)
FiTime_Normalize_With_Prec(ft, fileTimeType);
*/
}
// if (ft.dwLowDateTime != 0 || ft.dwHighDateTime != 0)
if (ft_Defined)
{
// we ignore set time errors here.
// note that user could move some finished volumes to another folder.
if (outStreamSpec)
outStreamSpec->SetMTime(&ft);
else if (volStreamSpec)
volStreamSpec->SetMTime_Final(ft);
}
}
if (callback)
{
UInt64 size = 0;
if (outStreamSpec)
outStreamSpec->GetSize(&size);
else if (stdOutFileStreamSpec)
size = stdOutFileStreamSpec->GetSize();
else
size = volStreamSpec->GetSize();
st.OutArcFileSize = size;
}
if (outStreamSpec)
result = outStreamSpec->Close();
else if (volStreamSpec)
{
result = volStreamSpec->FinalFlush_and_CloseFiles(st.NumVolumes);
st.IsMultiVolMode = true;
}
RINOK(result)
if (processedItemsStatuses)
{
FOR_VECTOR (i, updatePairs2)
{
const CUpdatePair2 &up = updatePairs2[i];
if (up.NewData && up.DirIndex >= 0)
{
const CDirItem &di = dirItems.Items[(unsigned)up.DirIndex];
if (di.AreReparseData() || (!di.IsDir() && di.Size == 0))
processedItemsStatuses[(unsigned)up.DirIndex] = 1;
}
}
}
return result;
}
static bool Censor_AreAllAllowed(const NWildcard::CCensor &censor)
{
if (censor.Pairs.Size() != 1)
return false;
const NWildcard::CPair &pair = censor.Pairs[0];
/* Censor_CheckPath() ignores (CPair::Prefix).
So we also ignore (CPair::Prefix) here */
// if (!pair.Prefix.IsEmpty()) return false;
return pair.Head.AreAllAllowed();
}
bool CensorNode_CheckPath2(const NWildcard::CCensorNode &node, const CReadArcItem &item, bool &include);
static bool Censor_CheckPath(const NWildcard::CCensor &censor, const CReadArcItem &item)
{
bool finded = false;
FOR_VECTOR (i, censor.Pairs)
{
/* (CPair::Prefix) in not used for matching items in archive.
So we ignore (CPair::Prefix) here */
bool include;
if (CensorNode_CheckPath2(censor.Pairs[i].Head, item, include))
{
// Check it and FIXME !!!!
// here we can exclude item via some Pair, that is still allowed by another Pair
if (!include)
return false;
finded = true;
}
}
return finded;
}
static HRESULT EnumerateInArchiveItems(
// bool storeStreamsMode,
const NWildcard::CCensor &censor,
const CArc &arc,
CObjectVector<CArcItem> &arcItems)
{
arcItems.Clear();
UInt32 numItems;
IInArchive *archive = arc.Archive;
RINOK(archive->GetNumberOfItems(&numItems))
arcItems.ClearAndReserve(numItems);
CReadArcItem item;
const bool allFilesAreAllowed = Censor_AreAllAllowed(censor);
for (UInt32 i = 0; i < numItems; i++)
{
CArcItem ai;
RINOK(arc.GetItem(i, item))
ai.Name = item.Path;
ai.IsDir = item.IsDir;
ai.IsAltStream =
#ifdef SUPPORT_ALT_STREAMS
item.IsAltStream;
#else
false;
#endif
/*
if (!storeStreamsMode && ai.IsAltStream)
continue;
*/
if (allFilesAreAllowed)
ai.Censored = true;
else
ai.Censored = Censor_CheckPath(censor, item);
// ai.MTime will be set to archive MTime, if not present in archive item
RINOK(arc.GetItem_MTime(i, ai.MTime))
RINOK(arc.GetItem_Size(i, ai.Size, ai.Size_Defined))
ai.IndexInServer = i;
arcItems.AddInReserved(ai);
}
return S_OK;
}
#if defined(_WIN32) && !defined(UNDER_CE)
#if defined(__MINGW32__) || defined(__MINGW64__)
#include <mapi.h>
#else
#include <MAPI.h>
#endif
extern "C" {
#ifdef MAPI_FORCE_UNICODE
#define Z7_WIN_LPMAPISENDMAILW LPMAPISENDMAILW
#define Z7_WIN_MapiFileDescW MapiFileDescW
#define Z7_WIN_MapiMessageW MapiMessageW
#define Z7_WIN_MapiRecipDescW MapiRecipDescW
#else
typedef struct
{
ULONG ulReserved;
ULONG ulRecipClass;
PWSTR lpszName;
PWSTR lpszAddress;
ULONG ulEIDSize;
PVOID lpEntryID;
} Z7_WIN_MapiRecipDescW, *Z7_WIN_lpMapiRecipDescW;
typedef struct
{
ULONG ulReserved;
ULONG flFlags;
ULONG nPosition;
PWSTR lpszPathName;
PWSTR lpszFileName;
PVOID lpFileType;
} Z7_WIN_MapiFileDescW, *Z7_WIN_lpMapiFileDescW;
typedef struct
{
ULONG ulReserved;
PWSTR lpszSubject;
PWSTR lpszNoteText;
PWSTR lpszMessageType;
PWSTR lpszDateReceived;
PWSTR lpszConversationID;
FLAGS flFlags;
Z7_WIN_lpMapiRecipDescW lpOriginator;
ULONG nRecipCount;
Z7_WIN_lpMapiRecipDescW lpRecips;
ULONG nFileCount;
Z7_WIN_lpMapiFileDescW lpFiles;
} Z7_WIN_MapiMessageW, *Z7_WIN_lpMapiMessageW;
typedef ULONG (FAR PASCAL Z7_WIN_MAPISENDMAILW)(
LHANDLE lhSession,
ULONG_PTR ulUIParam,
Z7_WIN_lpMapiMessageW lpMessage,
FLAGS flFlags,
ULONG ulReserved
);
typedef Z7_WIN_MAPISENDMAILW FAR *Z7_WIN_LPMAPISENDMAILW;
#endif // MAPI_FORCE_UNICODE
}
#endif // _WIN32
HRESULT UpdateArchive(
CCodecs *codecs,
const CObjectVector<COpenType> &types,
const UString &cmdArcPath2,
NWildcard::CCensor &censor,
CUpdateOptions &options,
CUpdateErrorInfo &errorInfo,
IOpenCallbackUI *openCallback,
IUpdateCallbackUI2 *callback,
bool needSetPath)
{
if (options.StdOutMode && options.EMailMode)
return E_FAIL;
if (types.Size() > 1)
return E_NOTIMPL;
bool renameMode = !options.RenamePairs.IsEmpty();
if (renameMode)
{
if (options.Commands.Size() != 1)
return E_FAIL;
}
if (options.DeleteAfterCompressing)
{
if (options.Commands.Size() != 1)
return E_NOTIMPL;
const CActionSet &as = options.Commands[0].ActionSet;
for (unsigned i = 2; i < NPairState::kNumValues; i++)
if (as.StateActions[i] != NPairAction::kCompress)
return E_NOTIMPL;
}
censor.AddPathsToCensor(options.PathMode);
#ifdef _WIN32
ConvertToLongNames(censor);
#endif
censor.ExtendExclude();
if (options.VolumesSizes.Size() > 0 && (options.EMailMode /* || options.SfxMode */))
return E_NOTIMPL;
if (options.SfxMode)
{
CProperty property;
property.Name = "rsfx";
options.MethodMode.Properties.Add(property);
if (options.SfxModule.IsEmpty())
{
errorInfo.Message = "SFX file is not specified";
return E_FAIL;
}
bool found = false;
if (options.SfxModule.Find(FCHAR_PATH_SEPARATOR) < 0)
{
const FString fullName = NDLL::GetModuleDirPrefix() + options.SfxModule;
if (NFind::DoesFileExist_FollowLink(fullName))
{
options.SfxModule = fullName;
found = true;
}
}
if (!found)
{
if (!NFind::DoesFileExist_FollowLink(options.SfxModule))
return errorInfo.SetFromLastError("cannot find specified SFX module", options.SfxModule);
}
}
CArchiveLink arcLink;
if (needSetPath)
{
if (!options.InitFormatIndex(codecs, types, cmdArcPath2) ||
!options.SetArcPath(codecs, cmdArcPath2))
return E_NOTIMPL;
}
UString arcPath = options.ArchivePath.GetFinalPath();
if (!options.VolumesSizes.IsEmpty())
{
arcPath = options.ArchivePath.GetFinalVolPath();
arcPath += ".001";
}
if (cmdArcPath2.IsEmpty())
{
if (options.MethodMode.Type.FormatIndex < 0)
throw "type of archive is not specified";
}
else
{
NFind::CFileInfo fi;
if (!fi.Find_FollowLink(us2fs(arcPath)))
{
if (renameMode)
throw "can't find archive";
if (options.MethodMode.Type.FormatIndex < 0)
{
if (!options.SetArcPath(codecs, cmdArcPath2))
return E_NOTIMPL;
}
}
else
{
if (fi.IsDir())
return errorInfo.SetFromError_DWORD("There is a folder with the name of archive",
us2fs(arcPath),
#ifdef _WIN32
ERROR_ACCESS_DENIED
#else
EISDIR
#endif
);
#ifdef _WIN32
if (fi.IsDevice)
return E_NOTIMPL;
#endif
if (!options.StdOutMode && options.UpdateArchiveItself)
if (fi.IsReadOnly())
{
return errorInfo.SetFromError_DWORD("The file is read-only",
us2fs(arcPath),
#ifdef _WIN32
ERROR_ACCESS_DENIED
#else
EACCES
#endif
);
}
if (options.VolumesSizes.Size() > 0)
{
errorInfo.FileNames.Add(us2fs(arcPath));
// errorInfo.SystemError = (DWORD)E_NOTIMPL;
errorInfo.Message = kUpdateIsNotSupported_MultiVol;
return E_NOTIMPL;
}
CObjectVector<COpenType> types2;
// change it.
if (options.MethodMode.Type_Defined)
types2.Add(options.MethodMode.Type);
// We need to set Properties to open archive only in some cases (WIM archives).
CIntVector excl;
COpenOptions op;
#ifndef Z7_SFX
op.props = &options.MethodMode.Properties;
#endif
op.codecs = codecs;
op.types = &types2;
op.excludedFormats = &excl;
op.stdInMode = false;
op.stream = NULL;
op.filePath = arcPath;
RINOK(callback->StartOpenArchive(arcPath))
HRESULT result = arcLink.Open_Strict(op, openCallback);
if (result == E_ABORT)
return result;
HRESULT res2 = callback->OpenResult(codecs, arcLink, arcPath, result);
/*
if (result == S_FALSE)
return E_FAIL;
*/
RINOK(res2)
RINOK(result)
if (arcLink.VolumePaths.Size() > 1)
{
// errorInfo.SystemError = (DWORD)E_NOTIMPL;
errorInfo.Message = kUpdateIsNotSupported_MultiVol;
return E_NOTIMPL;
}
CArc &arc = arcLink.Arcs.Back();
arc.MTime.Def =
#ifdef _WIN32
!fi.IsDevice;
#else
true;
#endif
if (arc.MTime.Def)
arc.MTime.Set_From_FiTime(fi.MTime);
if (arc.ErrorInfo.ThereIsTail)
{
// errorInfo.SystemError = (DWORD)E_NOTIMPL;
errorInfo.Message = "There is some data block after the end of the archive";
return E_NOTIMPL;
}
if (options.MethodMode.Type.FormatIndex < 0)
{
options.MethodMode.Type.FormatIndex = arcLink.GetArc()->FormatIndex;
if (!options.SetArcPath(codecs, cmdArcPath2))
return E_NOTIMPL;
}
}
}
if (options.MethodMode.Type.FormatIndex < 0)
{
options.MethodMode.Type.FormatIndex = codecs->FindFormatForArchiveType((UString)kDefaultArcType);
if (options.MethodMode.Type.FormatIndex < 0)
return E_NOTIMPL;
}
bool thereIsInArchive = arcLink.IsOpen;
if (!thereIsInArchive && renameMode)
return E_FAIL;
CDirItems dirItems;
dirItems.Callback = callback;
CDirItem parentDirItem;
CDirItem *parentDirItem_Ptr = NULL;
/*
FStringVector requestedPaths;
FStringVector *requestedPaths_Ptr = NULL;
if (options.DeleteAfterCompressing)
requestedPaths_Ptr = &requestedPaths;
*/
if (options.StdInMode)
{
CDirItem di;
di.ClearBase();
di.Name = options.StdInFileName;
di.Size = (UInt64)(Int64)-1;
di.SetAsFile();
NTime::GetCurUtc_FiTime(di.MTime);
di.CTime = di.ATime = di.MTime;
dirItems.Items.Add(di);
}
else
{
bool needScanning = false;
if (!renameMode)
FOR_VECTOR (i, options.Commands)
if (options.Commands[i].ActionSet.NeedScanning())
needScanning = true;
if (needScanning)
{
RINOK(callback->StartScanning())
dirItems.SymLinks = options.SymLinks.Val;
#if defined(_WIN32) && !defined(UNDER_CE)
dirItems.ReadSecure = options.NtSecurity.Val;
#endif
dirItems.ScanAltStreams = options.AltStreams.Val;
dirItems.ExcludeDirItems = censor.ExcludeDirItems;
dirItems.ExcludeFileItems = censor.ExcludeFileItems;
dirItems.ShareForWrite = options.OpenShareForWrite;
#ifndef _WIN32
dirItems.StoreOwnerName = options.StoreOwnerName.Val;
#endif
const HRESULT res = EnumerateItems(censor,
options.PathMode,
UString(), // options.AddPathPrefix,
dirItems);
if (res != S_OK)
{
if (res != E_ABORT)
errorInfo.Message = "Scanning error";
return res;
}
RINOK(callback->FinishScanning(dirItems.Stat))
// 22.00: we don't need parent folder, if absolute path mode
if (options.PathMode != NWildcard::k_AbsPath)
if (censor.Pairs.Size() == 1)
{
NFind::CFileInfo fi;
FString prefix = us2fs(censor.Pairs[0].Prefix);
prefix.Add_Dot();
// UString prefix = censor.Pairs[0].Prefix;
/*
if (prefix.Back() == WCHAR_PATH_SEPARATOR)
{
prefix.DeleteBack();
}
*/
if (fi.Find(prefix))
if (fi.IsDir())
{
parentDirItem.Copy_From_FileInfoBase(fi);
parentDirItem_Ptr = &parentDirItem;
int secureIndex = -1;
#if defined(_WIN32) && !defined(UNDER_CE)
if (options.NtSecurity.Val)
dirItems.AddSecurityItem(prefix, secureIndex);
#endif
parentDirItem.SecureIndex = secureIndex;
}
}
}
}
FString tempDirPrefix;
bool usesTempDir = false;
#ifdef _WIN32
CTempDir 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 ((thereIsInArchive || !options.WorkingDir.IsEmpty()) && !usesTempDir && options.VolumesSizes.Size() == 0)
{
createTempFile = true;
ap.Temp = true;
if (!options.WorkingDir.IsEmpty())
ap.TempPrefix = options.WorkingDir;
else
ap.TempPrefix = us2fs(ap.Prefix);
NormalizeDirPathPrefix(ap.TempPrefix);
}
}
unsigned ci;
// self including protection
if (options.DeleteAfterCompressing)
{
for (ci = 0; ci < options.Commands.Size(); ci++)
{
CArchivePath &ap = options.Commands[ci].ArchivePath;
const FString path = us2fs(ap.GetFinalPath());
// maybe we must compare absolute paths path here
FOR_VECTOR (i, dirItems.Items)
{
const FString phyPath = dirItems.GetPhyPath(i);
if (phyPath == path)
{
UString s;
s = "It is not allowed to include archive to itself";
s.Add_LF();
s += fs2us(path);
throw s;
}
}
}
}
for (ci = 0; ci < options.Commands.Size(); ci++)
{
CArchivePath &ap = options.Commands[ci].ArchivePath;
if (usesTempDir)
{
// Check it
ap.Prefix = fs2us(tempDirPrefix);
// ap.Temp = true;
// ap.TempPrefix = tempDirPrefix;
}
if (!options.StdOutMode &&
(ci > 0 || !createTempFile))
{
const FString path = us2fs(ap.GetFinalPath());
if (NFind::DoesFileOrDirExist(path))
{
errorInfo.SystemError = ERROR_FILE_EXISTS;
errorInfo.Message = "The file already exists";
errorInfo.FileNames.Add(path);
return errorInfo.Get_HRESULT_Error();
}
}
}
CObjectVector<CArcItem> arcItems;
if (thereIsInArchive)
{
RINOK(EnumerateInArchiveItems(
// options.StoreAltStreams,
censor, arcLink.Arcs.Back(), arcItems))
}
/*
FStringVector processedFilePaths;
FStringVector *processedFilePaths_Ptr = NULL;
if (options.DeleteAfterCompressing)
processedFilePaths_Ptr = &processedFilePaths;
*/
CByteBuffer processedItems;
if (options.DeleteAfterCompressing)
{
const unsigned num = dirItems.Items.Size();
processedItems.Alloc(num);
for (unsigned i = 0; i < num; i++)
processedItems[i] = 0;
}
CMultiOutStream_Bunch multiStreams;
/*
#ifndef Z7_NO_CRYPTO
if (arcLink.PasswordWasAsked)
{
// We set password, if open have requested password
RINOK(callback->SetPassword(arcLink.Password));
}
#endif
*/
for (ci = 0; ci < options.Commands.Size(); ci++)
{
const CArc *arc = thereIsInArchive ? arcLink.GetArc() : NULL;
CUpdateArchiveCommand &command = options.Commands[ci];
UString name;
bool isUpdating;
if (options.StdOutMode)
{
name = "stdout";
isUpdating = thereIsInArchive;
}
else
{
name = command.ArchivePath.GetFinalPath();
isUpdating = (ci == 0 && options.UpdateArchiveItself && thereIsInArchive);
}
RINOK(callback->StartArchive(name, isUpdating))
CFinishArchiveStat st;
RINOK(Compress(options,
isUpdating,
codecs,
command.ActionSet,
arc,
command.ArchivePath,
arcItems,
options.DeleteAfterCompressing ? (Byte *)processedItems : NULL,
dirItems,
parentDirItem_Ptr,
tempFiles,
multiStreams,
errorInfo, callback, st))
RINOK(callback->FinishArchive(st))
}
if (thereIsInArchive)
{
RINOK(arcLink.Close())
arcLink.Release();
}
multiStreams.DisableDeletion();
RINOK(multiStreams.Destruct())
tempFiles.Paths.Clear();
if (createTempFile)
{
try
{
CArchivePath &ap = options.Commands[0].ArchivePath;
const FString &tempPath = ap.GetTempPath();
// DWORD attrib = 0;
if (thereIsInArchive)
{
// attrib = NFind::GetFileAttrib(us2fs(arcPath));
if (!DeleteFileAlways(us2fs(arcPath)))
return errorInfo.SetFromLastError("cannot delete the file", us2fs(arcPath));
}
if (!MyMoveFile(tempPath, us2fs(arcPath)))
{
errorInfo.SetFromLastError("cannot move the file", tempPath);
errorInfo.FileNames.Add(us2fs(arcPath));
return errorInfo.Get_HRESULT_Error();
}
/*
if (attrib != INVALID_FILE_ATTRIBUTES && (attrib & FILE_ATTRIBUTE_READONLY))
{
DWORD attrib2 = NFind::GetFileAttrib(us2fs(arcPath));
if (attrib2 != INVALID_FILE_ATTRIBUTES)
NDir::SetFileAttrib(us2fs(arcPath), attrib2 | FILE_ATTRIBUTE_READONLY);
}
*/
}
catch(...)
{
throw;
}
}
#if defined(_WIN32) && !defined(UNDER_CE)
if (options.EMailMode)
{
NDLL::CLibrary mapiLib;
if (!mapiLib.Load(FTEXT("Mapi32.dll")))
{
errorInfo.SetFromLastError("cannot load Mapi32.dll");
return errorInfo.Get_HRESULT_Error();
}
FStringVector fullPaths;
unsigned i;
for (i = 0; i < options.Commands.Size(); i++)
{
CArchivePath &ap = options.Commands[i].ArchivePath;
const FString finalPath = us2fs(ap.GetFinalPath());
FString arcPath2;
if (!MyGetFullPathName(finalPath, arcPath2))
return errorInfo.SetFromLastError("GetFullPathName error", finalPath);
fullPaths.Add(arcPath2);
}
/*
LPMAPISENDDOCUMENTS fnSend = (LPMAPISENDDOCUMENTS)mapiLib.GetProc("MAPISendDocuments");
if (fnSend == 0)
{
errorInfo.SetFromLastError)("7-Zip cannot find MAPISendDocuments function");
return errorInfo.Get_HRESULT_Error();
}
*/
const
Z7_WIN_LPMAPISENDMAILW sendMailW = Z7_GET_PROC_ADDRESS(
Z7_WIN_LPMAPISENDMAILW, mapiLib.Get_HMODULE(),
"MAPISendMailW");
if (sendMailW)
{
CCurrentDirRestorer curDirRestorer;
UStringVector paths;
UStringVector names;
for (i = 0; i < fullPaths.Size(); i++)
{
const UString arcPath2 = fs2us(fullPaths[i]);
const UString fileName = ExtractFileNameFromPath(arcPath2);
paths.Add(arcPath2);
names.Add(fileName);
// Warning!!! MAPISendDocuments function changes Current directory
// fnSend(0, ";", (LPSTR)(LPCSTR)path, (LPSTR)(LPCSTR)name, 0);
}
CRecordVector<Z7_WIN_MapiFileDescW> files;
files.ClearAndSetSize(paths.Size());
for (i = 0; i < paths.Size(); i++)
{
Z7_WIN_MapiFileDescW &f = files[i];
memset(&f, 0, sizeof(f));
f.nPosition = 0xFFFFFFFF;
f.lpszPathName = paths[i].Ptr_non_const();
f.lpszFileName = names[i].Ptr_non_const();
}
{
Z7_WIN_MapiMessageW m;
memset(&m, 0, sizeof(m));
m.nFileCount = files.Size();
m.lpFiles = &files.Front();
const UString addr (options.EMailAddress);
Z7_WIN_MapiRecipDescW rec;
if (!addr.IsEmpty())
{
memset(&rec, 0, sizeof(rec));
rec.ulRecipClass = MAPI_TO;
rec.lpszAddress = addr.Ptr_non_const();
m.nRecipCount = 1;
m.lpRecips = &rec;
}
sendMailW((LHANDLE)0, 0, &m, MAPI_DIALOG, 0);
}
}
else
{
const
LPMAPISENDMAIL sendMail = Z7_GET_PROC_ADDRESS(
LPMAPISENDMAIL, mapiLib.Get_HMODULE(),
"MAPISendMail");
if (!sendMail)
{
errorInfo.SetFromLastError("7-Zip cannot find MAPISendMail function");
return errorInfo.Get_HRESULT_Error();
}
CCurrentDirRestorer curDirRestorer;
AStringVector paths;
AStringVector names;
for (i = 0; i < fullPaths.Size(); i++)
{
const UString arcPath2 = fs2us(fullPaths[i]);
const UString fileName = ExtractFileNameFromPath(arcPath2);
paths.Add(GetAnsiString(arcPath2));
names.Add(GetAnsiString(fileName));
// const AString path (GetAnsiString(arcPath2));
// const AString name (GetAnsiString(fileName));
// Warning!!! MAPISendDocuments function changes Current directory
// fnSend(0, ";", (LPSTR)(LPCSTR)path, (LPSTR)(LPCSTR)name, 0);
}
CRecordVector<MapiFileDesc> files;
files.ClearAndSetSize(paths.Size());
for (i = 0; i < paths.Size(); i++)
{
MapiFileDesc &f = files[i];
memset(&f, 0, sizeof(f));
f.nPosition = 0xFFFFFFFF;
f.lpszPathName = paths[i].Ptr_non_const();
f.lpszFileName = names[i].Ptr_non_const();
}
{
MapiMessage m;
memset(&m, 0, sizeof(m));
m.nFileCount = files.Size();
m.lpFiles = &files.Front();
const AString addr (GetAnsiString(options.EMailAddress));
MapiRecipDesc rec;
if (!addr.IsEmpty())
{
memset(&rec, 0, sizeof(rec));
rec.ulRecipClass = MAPI_TO;
rec.lpszAddress = addr.Ptr_non_const();
m.nRecipCount = 1;
m.lpRecips = &rec;
}
sendMail((LHANDLE)0, 0, &m, MAPI_DIALOG, 0);
}
}
}
#endif
if (options.DeleteAfterCompressing)
{
CRecordVector<CDirPathSortPair> pairs;
FStringVector foldersNames;
unsigned i;
for (i = 0; i < dirItems.Items.Size(); i++)
{
const CDirItem &dirItem = dirItems.Items[i];
const FString phyPath = dirItems.GetPhyPath(i);
if (dirItem.IsDir())
{
CDirPathSortPair pair;
pair.Index = i;
pair.SetNumSlashes(phyPath);
pairs.Add(pair);
}
else
{
// 21.04: we have set processedItems[*] before for all required items
if (processedItems[i] != 0
// || dirItem.Size == 0
// || dirItem.AreReparseData()
)
{
NFind::CFileInfo fileInfo;
/* if (!SymLinks), we follow link here, similar to (dirItem) filling */
if (fileInfo.Find(phyPath, !options.SymLinks.Val))
{
bool is_SameSize = false;
if (options.SymLinks.Val && dirItem.AreReparseData())
{
/* (dirItem.Size = dirItem.ReparseData.Size()) was set before.
So we don't compare sizes for that case here */
is_SameSize = fileInfo.IsOsSymLink();
}
else
is_SameSize = (fileInfo.Size == dirItem.Size);
if (is_SameSize
&& Compare_FiTime(&fileInfo.MTime, &dirItem.MTime) == 0
&& Compare_FiTime(&fileInfo.CTime, &dirItem.CTime) == 0)
{
RINOK(callback->DeletingAfterArchiving(phyPath, false))
DeleteFileAlways(phyPath);
}
}
}
else
{
// file was skipped by some reason. We can throw error for debug:
/*
errorInfo.SystemError = 0;
errorInfo.Message = "file was not processed";
errorInfo.FileNames.Add(phyPath);
return E_FAIL;
*/
}
}
}
pairs.Sort2();
for (i = 0; i < pairs.Size(); i++)
{
const FString phyPath = dirItems.GetPhyPath(pairs[i].Index);
if (NFind::DoesDirExist(phyPath))
{
RINOK(callback->DeletingAfterArchiving(phyPath, true))
RemoveDir(phyPath);
}
}
RINOK(callback->FinishDeletingAfterArchiving())
}
return S_OK;
}