// Compress.cpp #include "StdAfx.h" #include #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 &realNames, std::vector &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 setProperties; if (outArchive->QueryInterface(&setProperties) == S_OK) { CObjectVector realNames; std::vector 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 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 OutArchive; UString LibPath; CLSID ClassID; UString OutArchivePath; BYTE ActionSetByte[NUpdateArchive::NPairState::kNumValues]; bool SfxMode; UString SfxModule; UStringVector FileNames; CRecordVector FileNamePointers; CMyComPtr 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 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 outArchive; CMyComPtr 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 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; }