Files
easy7zip/7zip/UI/GUI/Compress.cpp
Igor Pavlov 8c1b5c7b7e 3.13
2016-05-28 00:15:41 +01:00

637 lines
16 KiB
C++
Executable File

// Compress.cpp
#include "StdAfx.h"
#include <mapi.h>
#include "Compress.h"
#include "CompressDialog.h"
#include "resource.h"
#include "Common/StringConvert.h"
#include "Common/IntToString.h"
#include "Windows/FileName.h"
#include "Windows/FileFind.h"
#include "Windows/FileDir.h"
#include "Windows/Thread.h"
#include "Windows/COM.h"
#include "Windows/PropVariant.h"
#include "../../FileManager/ProgramLocation.h"
#include "../../FileManager/FormatUtils.h"
#include "../../FileManager/UpdateCallback100.h"
#include "../Agent/Agent.h"
#include "../Common/UpdateAction.h"
#include "../Common/WorkDir.h"
#include "../Common/ZipRegistry.h"
#include "../Common/OpenArchive.h"
#include "../Resource/Extract/resource.h"
#include "../Explorer/MyMessages.h"
using namespace NWindows;
using namespace NFile;
using namespace NDirectory;
using namespace NName;
static LPCWSTR kTempArchivePrefix = L"7zA";
static LPCWSTR kTempFolderPrefix = L"7zE";
static LPCWSTR kDefaultSfxModule = L"7zC.sfx";
static void SplitString(const UString &srcString, UStringVector &destStrings)
{
destStrings.Clear();
for (int pos = 0; pos < srcString.Length();)
{
int spacePos = srcString.Find(L' ', pos);
if (spacePos < 0)
{
destStrings.Add(srcString.Mid(pos));
return;
}
if (spacePos != pos)
destStrings.Add(srcString.Mid(pos, spacePos - pos));
pos = spacePos + 1;
}
}
static bool ParseNumberString(const UString &string, UINT32 &number)
{
wchar_t *endPtr;
number = wcstoul(string, &endPtr, 10);
return (endPtr - string == string.Length());
}
static void SetOptions(const UString &options,
CObjectVector<CMyComBSTR> &realNames,
std::vector<NCOM::CPropVariant> &values)
{
UStringVector strings;
SplitString(options, strings);
for(int i = 0; i < strings.Size(); i++)
{
const UString &s = strings[i];
int index = s.Find(L'=');
CMyComBSTR name;
NCOM::CPropVariant propVariant;
if (index < 0)
name = s;
else
{
name = s.Left(index);
UString value = s.Mid(index + 1);
if (!value.IsEmpty())
{
UINT32 number;
if (ParseNumberString(value, number))
propVariant = number;
else
propVariant = value;
}
}
realNames.Add(name);
values.push_back(propVariant);
}
}
static HRESULT SetOutProperties(IOutFolderArchive * outArchive,
bool is7z,
UINT32 level,
const UString &method,
UINT32 dictionary,
bool orderMode,
UINT32 order,
bool solidModeIsAllowed, bool solidMode,
bool multiThreadIsAllowed, bool multiThread,
bool encryptHeadersIsAllowed, bool encryptHeaders,
bool sfxMode,
const UString &options)
{
CMyComPtr<ISetProperties> setProperties;
if (outArchive->QueryInterface(&setProperties) == S_OK)
{
CObjectVector<CMyComBSTR> realNames;
std::vector<NCOM::CPropVariant> values;
if (level != (UINT32)(INT32)-1)
{
CMyComBSTR comBSTR = L"x";
realNames.Add(comBSTR);
values.push_back(NCOM::CPropVariant((UINT32)level));
}
if (!method.IsEmpty())
{
CMyComBSTR comBSTR;
if (is7z)
comBSTR = L"0";
else
comBSTR = L"m";
realNames.Add(comBSTR);
values.push_back(NCOM::CPropVariant(method));
}
if (dictionary != (UINT32)(INT32)-1)
{
CMyComBSTR comBSTR;
if (is7z)
if (orderMode)
comBSTR = L"0mem";
else
comBSTR = L"0d";
else
if (orderMode)
comBSTR = L"mem";
else
comBSTR = L"d";
realNames.Add(comBSTR);
wchar_t s[32];
ConvertUINT64ToString(dictionary, s);
wcscat(s, L"B");
values.push_back(NCOM::CPropVariant(s));
}
if (order != (UINT32)(INT32)-1)
{
CMyComBSTR comBSTR;
if (is7z)
if (orderMode)
comBSTR = L"0o";
else
comBSTR = L"0fb";
else
if (orderMode)
comBSTR = L"o";
else
comBSTR = L"fb";
realNames.Add(comBSTR);
values.push_back(NCOM::CPropVariant((UINT32)order));
}
if (sfxMode)
{
realNames.Add(L"rsfx");
values.push_back(NCOM::CPropVariant(L"on"));
}
if (encryptHeadersIsAllowed)
{
if (encryptHeaders)
{
realNames.Add(L"he");
values.push_back(NCOM::CPropVariant(L"on"));
}
}
// Solid
if (solidModeIsAllowed)
{
realNames.Add(L"s");
values.push_back(NCOM::CPropVariant(solidMode ? L"on": L"off"));
}
if (multiThreadIsAllowed)
{
realNames.Add(L"mt");
values.push_back(NCOM::CPropVariant(multiThread ? L"on": L"off"));
}
// Options
SetOptions(options, realNames, values);
std::vector<BSTR> names;
for(int i = 0; i < realNames.Size(); i++)
names.push_back(realNames[i]);
RINOK(setProperties->SetProperties(&names.front(),
&values.front(), names.size()));
}
return S_OK;
}
struct CThreadUpdateCompress
{
CMyComPtr<IOutFolderArchive> OutArchive;
UString LibPath;
CLSID ClassID;
UString OutArchivePath;
BYTE ActionSetByte[NUpdateArchive::NPairState::kNumValues];
bool SfxMode;
UString SfxModule;
UStringVector FileNames;
CRecordVector<const wchar_t *> FileNamePointers;
CMyComPtr<IFolderArchiveUpdateCallback> UpdateCallback;
CUpdateCallback100Imp *UpdateCallbackSpec;
HRESULT Result;
DWORD Process()
{
NCOM::CComInitializer comInitializer;
UpdateCallbackSpec->ProgressDialog.WaitCreating();
try
{
Result = OutArchive->DoOperation(
LibPath, &ClassID,
OutArchivePath, ActionSetByte,
(SfxMode ? (const wchar_t *)SfxModule: NULL),
UpdateCallback);
}
catch(const UString &s)
{
MyMessageBox(s);
Result = E_FAIL;
}
catch(...)
{
Result = E_FAIL;
}
UpdateCallbackSpec->ProgressDialog.MyClose();
return 0;
}
static DWORD WINAPI MyThreadFunction(void *param)
{
return ((CThreadUpdateCompress *)param)->Process();
}
};
static UString MakeFullArchiveName(const UString &name,
const UString &extension, bool sfx)
{
if (sfx)
{
UString sfxExt = L".exe";
if (sfxExt.CollateNoCase(name.Right(sfxExt.Length())) == 0)
return name;
return name + sfxExt;
}
if (extension.IsEmpty())
return name;
if (name.IsEmpty())
return name;
if (name[name.Length() - 1] == '.')
return name.Left(name.Length() - 1);
int slash1Pos = name.ReverseFind(L'\\');
int slash2Pos = name.ReverseFind(L'/');
int slashPos = MyMax(slash1Pos, slash2Pos);
int dotPos = name.ReverseFind(L'.');
if (dotPos >= 0 && (dotPos > slashPos || slashPos < 0))
return name;
return name + UString(L'.') + extension;
}
HRESULT CompressArchive(
const UString &archivePath,
const UStringVector &fileNames,
const UString &archiveType,
bool email,
bool showDialog)
{
if (fileNames.Size() == 0)
return S_OK;
CObjectVector<CArchiverInfo> archivers;
ReadArchiverInfoList(archivers);
CArchiverInfo archiverInfo;
UString password;
bool encryptHeadersIsAllowed = false;
bool encryptHeaders = false;
const NUpdateArchive::CActionSet *actionSet;
NCompressDialog::CInfo compressInfo;
UString tempDirPath;
UString currentDirPrefix;
bool needTempFile = true;
NDirectory::CTempDirectoryW tempDirectory;
UString archiveName;
int pos = archivePath.ReverseFind(L'\\');
if (pos < 0)
{
archiveName = archivePath;
MyGetCurrentDirectory(currentDirPrefix);
}
else
{
currentDirPrefix = archivePath.Left(pos + 1);
archiveName = archivePath.Mid(pos + 1);
}
if (email)
{
tempDirectory.Create(kTempFolderPrefix);
currentDirPrefix = tempDirectory.GetPath();
NormalizeDirPathPrefix(currentDirPrefix);
needTempFile = false;
}
if (showDialog)
{
bool oneFile = false;
NFind::CFileInfoW fileInfo;
if (!NFind::FindFile(fileNames.Front(), fileInfo))
return ::GetLastError();
if (fileNames.Size() == 1)
oneFile = !fileInfo.IsDirectory();
CCompressDialog dialog;
for(int i = 0; i < archivers.Size(); i++)
{
const CArchiverInfo &archiverInfo = archivers[i];
if (archiverInfo.UpdateEnabled &&
(oneFile || !archiverInfo.KeepName))
dialog.m_ArchiverInfoList.Add(archiverInfo);
}
if(dialog.m_ArchiverInfoList.Size() == 0)
{
MyMessageBox(L"No Update Engines");
return E_FAIL;
}
dialog.m_Info.ArchiveName = archiveName;
dialog.OriginalFileName = fileInfo.Name;
dialog.m_Info.CurrentDirPrefix = currentDirPrefix;
dialog.m_Info.SFXMode = false;
dialog.m_Info.Solid = true;
dialog.m_Info.MultiThread = false;
dialog.m_Info.KeepName = !oneFile;
if(dialog.Create(0) != IDOK)
return S_OK;
if (dialog.m_Info.VolumeSizeIsDefined)
{
MyMessageBox(L"Splitting to volumes is not supported");
return E_FAIL;
}
switch(dialog.m_Info.UpdateMode)
{
case NCompressDialog::NUpdateMode::kAdd:
actionSet = &NUpdateArchive::kAddActionSet;
break;
case NCompressDialog::NUpdateMode::kUpdate:
actionSet = &NUpdateArchive::kUpdateActionSet;
break;
case NCompressDialog::NUpdateMode::kFresh:
actionSet = &NUpdateArchive::kFreshActionSet;
break;
case NCompressDialog::NUpdateMode::kSynchronize:
actionSet = &NUpdateArchive::kSynchronizeActionSet;
break;
default:
throw 1091756;
}
archiverInfo = dialog.m_ArchiverInfoList[dialog.m_Info.ArchiverInfoIndex];
password = GetUnicodeString(dialog.Password);
encryptHeadersIsAllowed = dialog.EncryptHeadersIsAllowed;
encryptHeaders = dialog.EncryptHeaders;
compressInfo = dialog.m_Info;
compressInfo.ArchiveName = MakeFullArchiveName(
compressInfo.ArchiveName,
archiverInfo.GetMainExtension(), compressInfo.SFXMode);
}
else
{
int i;
for(i = 0; i < archivers.Size(); i++)
{
if (archivers[i].Name.CollateNoCase(archiveType) == 0)
{
archiverInfo = archivers[i];
break;
}
}
if (i == archivers.Size())
{
MyMessageBox(L"No archiver");
return E_FAIL;
}
actionSet = &NUpdateArchive::kAddActionSet;
bool is7z = (archiveType.CollateNoCase(L"7z") == 0);
compressInfo.SolidIsAllowed = is7z;
compressInfo.Solid = true;
compressInfo.MultiThreadIsAllowed = is7z;
compressInfo.MultiThread = false;
compressInfo.SFXMode = false;
compressInfo.KeepName = false;
compressInfo.ArchiveName = archiveName;
compressInfo.CurrentDirPrefix = currentDirPrefix;
compressInfo.Level = 5;
}
UString arcPath;
if (!compressInfo.GetFullPathName(arcPath))
{
MyMessageBox(L"Incorrect archive path");
return E_FAIL;
}
if (compressInfo.ArchiveName.Find('\\') >= 0)
{
needTempFile = true;
}
// MessageBox(0, arcPath, 0, 0);
NWorkDir::CInfo workDirInfo;
ReadWorkDirInfo(workDirInfo);
UString workDir = GetWorkDir(workDirInfo, arcPath);
NFile::NDirectory::CreateComplexDirectory(workDir);
NFile::NDirectory::CTempFileW tempFile;
UString tempFileName;
if (needTempFile)
{
if (tempFile.Create(workDir, kTempArchivePrefix, tempFileName) == 0)
return E_FAIL;
}
else
tempFileName = arcPath;
/*
const CLSID &classID =
dialog.m_ArchiverInfoList[dialog.m_Info.ArchiverInfoIndex].ClassID;
*/
NFind::CFileInfoW fileInfo;
CMyComPtr<IOutFolderArchive> outArchive;
CMyComPtr<IInFolderArchive> archiveHandler;
if(NFind::FindFile(arcPath, fileInfo))
{
if (fileInfo.IsDirectory())
{
MyMessageBox(L"There is a folder with such name");
return E_FAIL;
}
CAgent *agentSpec = new CAgent;
archiveHandler = agentSpec;
// CLSID realClassID;
CMyComBSTR archiveType;
HRESULT result = agentSpec->Open(
GetUnicodeString(arcPath), &archiveType, NULL);
if (result == S_FALSE)
{
MyMessageBox(IDS_OPEN_IS_NOT_SUPORTED_ARCHIVE, 0x02000604);
return E_FAIL;
}
/*
HRESULT result = OpenArchive(arcPath, &archiveHandler,
archiverInfoResult, defaultName, NULL);
if (result == S_FALSE)
{
MyMessageBox(IDS_OPEN_IS_NOT_SUPORTED_ARCHIVE, 0x02000604);
return E_FAIL;
}
*/
if (result != S_OK)
{
MyMessageBox(L"Open error");
return E_FAIL;
}
if (archiverInfo.Name.CollateNoCase((const wchar_t *)archiveType) != 0)
{
MyMessageBox(L"Type of existing archive differs from specified type");
return E_FAIL;
}
result = archiveHandler.QueryInterface(IID_IOutFolderArchive, &outArchive);
if(result != S_OK)
{
MyMessageBox(MyFormatNew(IDS_CANT_UPDATE_ARCHIVE, 0x02000602,
GetUnicodeString(arcPath)));
return E_FAIL;
}
}
else
{
CAgent *agentSpec = new CAgent;
outArchive = agentSpec;
}
CRecordVector<const wchar_t *> fileNamePointers;
fileNamePointers.Reserve(fileNames.Size());
int i;
for(i = 0; i < fileNames.Size(); i++)
fileNamePointers.Add(fileNames[i]);
outArchive->SetFolder(NULL);
// Don't uses CurrentFolder here, since files are absolute paths;
// MyGetCurrentDirectory(aCurrentFolder);
UINT codePage = AreFileApisANSI() ? CP_ACP : CP_OEMCP;
outArchive->SetFiles(L"",
&fileNamePointers.Front(), fileNamePointers.Size());
CThreadUpdateCompress updater;
for (i = 0; i < NUpdateArchive::NPairState::kNumValues; i++)
updater.ActionSetByte[i] = actionSet->StateActions[i];
updater.UpdateCallbackSpec = new CUpdateCallback100Imp;
updater.UpdateCallback = updater.UpdateCallbackSpec;
updater.OutArchive = outArchive;
// updater.SrcFolderPrefix = srcPanel._currentFolderPrefix;
UString title = LangLoadStringW(IDS_PROGRESS_COMPRESSING, 0x02000DC0);
updater.UpdateCallbackSpec->Init(0, !password.IsEmpty(), password);
// UINT32 level = MyMin(compressInfo.Level, UINT32(9));
UINT32 level = compressInfo.Level;
HRESULT result = SetOutProperties(outArchive,
archiverInfo.Name.CompareNoCase(L"7z") == 0,
level,
compressInfo.Method,
compressInfo.Dictionary,
compressInfo.OrderMode, compressInfo.Order,
compressInfo.SolidIsAllowed, compressInfo.Solid,
compressInfo.MultiThreadIsAllowed, compressInfo.MultiThread,
encryptHeadersIsAllowed, encryptHeaders,
compressInfo.SFXMode,
GetUnicodeString(compressInfo.Options));
if (result != S_OK)
{
if (result != E_ABORT)
ShowErrorMessage(result);
return result;
}
UString sfxModule;
if (compressInfo.SFXMode)
{
UString sfxModule2;
LPCWSTR path = NULL;
UString sfxModule3;
if (GetProgramFolderPath(sfxModule3))
path = sfxModule3;
if (!NDirectory::MySearchPath(path, kDefaultSfxModule, NULL, sfxModule2))
{
MyMessageBox(L"can't find sfx module");
return E_FAIL;
}
sfxModule = sfxModule2;
}
updater.OutArchivePath = GetUnicodeString(tempFileName, codePage);
updater.SfxMode = compressInfo.SFXMode;
updater.SfxModule = sfxModule;
updater.LibPath = GetUnicodeString(archiverInfo.FilePath);
updater.ClassID = archiverInfo.ClassID;
CThread thread;
if (!thread.Create(CThreadUpdateCompress::MyThreadFunction, &updater))
throw 271824;
updater.UpdateCallbackSpec->StartProgressDialog(title);
result = updater.Result;
updater.UpdateCallback.Release();
updater.OutArchive.Release();
outArchive.Release();
if (result != S_OK)
{
if (result != E_ABORT)
ShowErrorMessage(result);
return result;
}
if(archiveHandler)
{
archiveHandler->Close();
if (!DeleteFileAlways(arcPath))
{
ShowLastErrorMessage();
return E_FAIL;
}
}
if (needTempFile)
{
tempFile.DisableDeleting();
if (!NDirectory::MyMoveFile(tempFileName, arcPath))
{
ShowLastErrorMessage();
return E_FAIL;
}
}
if (email)
{
NDLL::CLibrary mapiLib;
if (!mapiLib.Load(TEXT("Mapi32.dll")))
return E_FAIL;
LPMAPISENDDOCUMENTS fnSend = (LPMAPISENDDOCUMENTS)
mapiLib.GetProcAddress("MAPISendDocuments");
if (fnSend == 0)
return E_FAIL;
UString fileName;
GetOnlyName(arcPath, fileName);
AString path = GetAnsiString(arcPath);
AString name = GetAnsiString(fileName);
fnSend(0, ";", (LPSTR)(LPCSTR)path, (LPSTR)(LPCSTR)name, 0);
}
return S_OK;
}