From 37e2c59096cef27b7eeed7701a3a6f422d05de4c Mon Sep 17 00:00:00 2001 From: Whindmar Saksit Date: Tue, 29 Apr 2025 15:39:22 +0200 Subject: [PATCH] [RAPPS] Add ExeInZip installer type to support running installers in zip files (#7866) --- base/applications/rapps/appdb.cpp | 9 +++ base/applications/rapps/appinfo.cpp | 7 ++- base/applications/rapps/geninst.cpp | 74 +++++++++++++++++++---- base/applications/rapps/include/appdb.h | 3 + base/applications/rapps/include/appinfo.h | 8 +++ base/applications/rapps/include/misc.h | 6 ++ base/applications/rapps/loaddlg.cpp | 25 ++++++-- base/applications/rapps/unattended.cpp | 4 +- 8 files changed, 111 insertions(+), 25 deletions(-) diff --git a/base/applications/rapps/appdb.cpp b/base/applications/rapps/appdb.cpp index 8ee19edb8e2..35326830a3f 100644 --- a/base/applications/rapps/appdb.cpp +++ b/base/applications/rapps/appdb.cpp @@ -11,6 +11,7 @@ #include "appdb.h" #include "configparser.h" #include "settings.h" +#include "misc.h" static HKEY g_RootKeyEnum[3] = {HKEY_CURRENT_USER, HKEY_LOCAL_MACHINE, HKEY_LOCAL_MACHINE}; @@ -34,6 +35,14 @@ CAppDB::CAppDB(const CStringW &path) : m_BasePath(path) m_BasePath.Canonicalize(); } +CStringW +CAppDB::GetDefaultPath() +{ + CStringW path; + GetStorageDirectory(path); + return path; +} + CAvailableApplicationInfo * CAppDB::FindAvailableByPackageName(const CStringW &name) { diff --git a/base/applications/rapps/appinfo.cpp b/base/applications/rapps/appinfo.cpp index 5442380fbad..9587afb87b2 100644 --- a/base/applications/rapps/appinfo.cpp +++ b/base/applications/rapps/appinfo.cpp @@ -380,10 +380,11 @@ CAvailableApplicationInfo::GetInstallerType() const { CStringW str; m_Parser->GetString(DB_INSTALLER, str); - if (str.CompareNoCase(DB_GENINSTSECTION) == 0) + if (str.CompareNoCase(DB_INSTALLER_GENERATE) == 0) return INSTALLER_GENERATE; - else - return INSTALLER_UNKNOWN; + if (str.CompareNoCase(DB_INSTALLER_EXEINZIP) == 0) + return INSTALLER_EXEINZIP; + return INSTALLER_UNKNOWN; } BOOL diff --git a/base/applications/rapps/geninst.cpp b/base/applications/rapps/geninst.cpp index 9d00156f55c..9db371851f2 100644 --- a/base/applications/rapps/geninst.cpp +++ b/base/applications/rapps/geninst.cpp @@ -60,7 +60,7 @@ BOOL IsZipFile(PCWSTR Path) static int ExtractFilesFromZip(LPCWSTR Archive, const CStringW &OutputDir, - EXTRACTCALLBACK Callback, void *Cookie) + EXTRACTCALLBACK Callback, void *Context) { const UINT pkzefsutf8 = 1 << 11; // APPNOTE; APPENDIX D zlib_filefunc64_def zff; @@ -97,7 +97,7 @@ ExtractFilesFromZip(LPCWSTR Archive, const CStringW &OutputDir, fileatt = LOBYTE(fi.external_fa); if (!NotifyFileExtractCallback(path, fi.uncompressed_size, fileatt, - Callback, Cookie)) + Callback, Context)) continue; // Skip file path = BuildPath(OutputDir, path); @@ -136,22 +136,31 @@ ExtractFilesFromZip(LPCWSTR Archive, const CStringW &OutputDir, static UINT ExtractZip(LPCWSTR Archive, const CStringW &OutputDir, - EXTRACTCALLBACK Callback, void *Cookie) + EXTRACTCALLBACK Callback, void *Context) { - int zerr = ExtractFilesFromZip(Archive, OutputDir, Callback, Cookie); + int zerr = ExtractFilesFromZip(Archive, OutputDir, Callback, Context); return zerr == UNZ_ERRNO ? GetLastError() : zerr ? ERROR_INTERNAL_ERROR : 0; } static UINT ExtractCab(LPCWSTR Archive, const CStringW &OutputDir, - EXTRACTCALLBACK Callback, void *Cookie) + EXTRACTCALLBACK Callback, void *Context) { - if (ExtractFilesFromCab(Archive, OutputDir, Callback, Cookie)) + if (ExtractFilesFromCab(Archive, OutputDir, Callback, Context)) return ERROR_SUCCESS; UINT err = GetLastError(); return err ? err : ERROR_INTERNAL_ERROR; } + +static UINT +ExtractArchive(LPCWSTR Archive, const CStringW &OutputDir, EXTRACTCALLBACK Callback, void *Context) +{ + BOOL isCab = LOBYTE(ClassifyFile(Archive)) == 'C'; + return isCab ? ExtractCab(Archive, OutputDir, Callback, Context) + : ExtractZip(Archive, OutputDir, Callback, Context); +} + enum { IM_STARTPROGRESS = WM_APP, IM_PROGRESS, IM_END }; static struct CommonInfo @@ -430,9 +439,9 @@ AddUninstallOperationsFromDB(LPCWSTR Name, WCHAR UnOp, CStringW PathPrefix = CSt } static BOOL CALLBACK -ExtractCallback(const EXTRACTCALLBACKINFO &, void *Cookie) +ExtractCallback(const EXTRACTCALLBACKINFO &, void *Context) { - InstallInfo &Info = *(InstallInfo *) Cookie; + InstallInfo &Info = *(InstallInfo *)Context; Info.Count += 1; return TRUE; } @@ -501,11 +510,7 @@ ExtractAndInstallThread(LPVOID Parameter) ErrorBox(Info.Error); if (!Info.Error) - { - BOOL isCab = LOBYTE(ClassifyFile(tempdir)) == 'C'; - Info.Error = isCab ? ExtractCab(Archive, tempdir, ExtractCallback, &Info) - : ExtractZip(Archive, tempdir, ExtractCallback, &Info); - } + Info.Error = ExtractArchive(Archive, tempdir, ExtractCallback, &Info); if (!Info.Error) { @@ -835,3 +840,46 @@ UninstallGenerated(CInstalledApplicationInfo &AppInfo, UninstallCommandFlags Fla g_pInfo = &Info; return CreateUI(Info.Silent, UninstallThread) ? !Info.Error : FALSE; } + +HRESULT +ExtractArchiveForExecution(PCWSTR pszArchive, const CStringW &PackageName, CStringW &TempDir, CStringW &App) +{ + WCHAR TempDirBuf[MAX_PATH], UniqueDir[MAX_PATH]; + CAppDB db(CAppDB::GetDefaultPath()); + db.UpdateAvailable(); + CAvailableApplicationInfo *pAppInfo = db.FindAvailableByPackageName(PackageName); + if (!pAppInfo) + return HResultFromWin32(ERROR_NOT_FOUND); + CConfigParser *pCfg = pAppInfo->GetConfigParser(); + + if (!GetTempPathW(_countof(TempDirBuf), TempDirBuf)) + return E_FAIL; + wsprintfW(UniqueDir, L"~%s-%u", RAPPS_NAME, GetCurrentProcessId()); + TempDir = BuildPath(TempDirBuf, UniqueDir); + HRESULT hr = HResultFromWin32(CreateDirectoryTree(TempDir)); + if (FAILED(hr)) + return hr; + + hr = HResultFromWin32(ExtractArchive(pszArchive, TempDir, NULL, NULL)); + if (SUCCEEDED(hr)) + { + CStringW Exe; + if (pCfg->GetSectionString(DB_EXEINZIPSECTION, DB_EXEINZIP_EXE, Exe) <= 0) + { + WIN32_FIND_DATAW wfd; + HANDLE hFind = FindFirstFileW(Exe = BuildPath(TempDir, L"*.exe"), &wfd); + if (hFind != INVALID_HANDLE_VALUE) + { + FindClose(hFind); + Exe = wfd.cFileName; + } + } + App = BuildPath(TempDir, Exe); + if (GetFileAttributesW(App) & FILE_ATTRIBUTE_DIRECTORY) + hr = HResultFromWin32(ERROR_FILE_NOT_FOUND); + } + + if (FAILED(hr) && !TempDir.IsEmpty()) + DeleteDirectoryTree(TempDir, hMainWnd); + return hr; +} diff --git a/base/applications/rapps/include/appdb.h b/base/applications/rapps/include/appdb.h index 8beae3986ec..af927b55ab8 100644 --- a/base/applications/rapps/include/appdb.h +++ b/base/applications/rapps/include/appdb.h @@ -23,6 +23,9 @@ class CAppDB public: CAppDB(const CStringW &path); + static CStringW + GetDefaultPath(); + VOID GetApps(CAtlList &List, AppsCategories Type) const; CAvailableApplicationInfo * diff --git a/base/applications/rapps/include/appinfo.h b/base/applications/rapps/include/appinfo.h index 961ad0711bd..171d8edce9c 100644 --- a/base/applications/rapps/include/appinfo.h +++ b/base/applications/rapps/include/appinfo.h @@ -83,6 +83,7 @@ enum InstallerType { INSTALLER_UNKNOWN, INSTALLER_GENERATE, // .zip file automatically converted to installer by rapps + INSTALLER_EXEINZIP, }; #define DB_VERSION L"Version" @@ -90,12 +91,17 @@ enum InstallerType #define DB_PUBLISHER L"Publisher" #define DB_REGNAME L"RegName" #define DB_INSTALLER L"Installer" +#define DB_INSTALLER_GENERATE L"Generate" +#define DB_INSTALLER_EXEINZIP L"ExeInZip" #define DB_SCOPE L"Scope" // User or Machine #define DB_SAVEAS L"SaveAs" #define DB_GENINSTSECTION L"Generate" #define GENERATE_ARPSUBKEY L"RApps" // Our uninstall data is stored here +#define DB_EXEINZIPSECTION L"ExeInZip" +#define DB_EXEINZIP_EXE L"Exe" + class CAppRichEdit; class CConfigParser; @@ -234,3 +240,5 @@ BOOL UninstallGenerated(CInstalledApplicationInfo &AppInfo, UninstallCommandFlags Flags); BOOL ExtractAndRunGeneratedInstaller(const CAvailableApplicationInfo &AppInfo, LPCWSTR Archive); +HRESULT +ExtractArchiveForExecution(PCWSTR pszArchive, const CStringW &PackageName, CStringW &TempDir, CStringW &App); diff --git a/base/applications/rapps/include/misc.h b/base/applications/rapps/include/misc.h index 6e7814b8cee..b6861e7de7c 100644 --- a/base/applications/rapps/include/misc.h +++ b/base/applications/rapps/include/misc.h @@ -16,6 +16,12 @@ #define CurrentArchitecture L"ppc" #endif +static inline HRESULT +HResultFromWin32(UINT Error) +{ + return HRESULT_FROM_WIN32(Error); +} + static inline UINT ErrorFromHResult(HRESULT hr) { diff --git a/base/applications/rapps/loaddlg.cpp b/base/applications/rapps/loaddlg.cpp index 8d51fe78f8c..66d4fcca1d8 100644 --- a/base/applications/rapps/loaddlg.cpp +++ b/base/applications/rapps/loaddlg.cpp @@ -1058,10 +1058,8 @@ run: // run it if (Info.DLType == DLTYPE_APPLICATION) { - CStringW app, params; - SHELLEXECUTEINFOW shExInfo = {0}; - shExInfo.cbSize = sizeof(shExInfo); - shExInfo.fMask = SEE_MASK_NOCLOSEPROCESS; + CStringW app, params, tempdir; + SHELLEXECUTEINFOW shExInfo = { sizeof(shExInfo), SEE_MASK_NOCLOSEPROCESS, hDlg }; shExInfo.lpVerb = L"open"; shExInfo.lpFile = Path; shExInfo.lpParameters = L""; @@ -1077,6 +1075,16 @@ run: GetModuleFileNameW(NULL, const_cast(shExInfo.lpFile), MAX_PATH); app.ReleaseBuffer(); } + else if (Info.IType == INSTALLER_EXEINZIP) + { + HRESULT hr = ExtractArchiveForExecution(Path, Info.szPackageName, tempdir, app); + if (FAILED(hr)) + { + ShowLastError(hDlg, FALSE, hr); + goto end; + } + shExInfo.lpFile = app; + } /* FIXME: Do we want to log installer status? */ WriteLogMessage(EVENTLOG_SUCCESS, MSG_SUCCESS_INSTALL, Info.szName); @@ -1102,6 +1110,11 @@ run: { ShowLastError(hMainWnd, FALSE, GetLastError()); } + + if (!tempdir.IsEmpty()) + { + DeleteDirectoryTree(tempdir, hDlg); + } } end: @@ -1117,8 +1130,8 @@ end: if (bCancelled || (SettingsInfo.bDelInstaller && Info.DLType == DLTYPE_APPLICATION)) { // Don't delete .zip/.cab files so the user can extract from them - if (bCancelled || Info.IType == INSTALLER_GENERATE || !OpensWithExplorer(Path) || - HIBYTE(ClassifyFile(Path)) != PERCEIVED_TYPE_COMPRESSED) + if (bCancelled || Info.IType == INSTALLER_GENERATE || Info.IType == INSTALLER_EXEINZIP || + !OpensWithExplorer(Path) || HIBYTE(ClassifyFile(Path)) != PERCEIVED_TYPE_COMPRESSED) { DeleteFileW(Path); } diff --git a/base/applications/rapps/unattended.cpp b/base/applications/rapps/unattended.cpp index 06db5f0aba6..fd115385638 100644 --- a/base/applications/rapps/unattended.cpp +++ b/base/applications/rapps/unattended.cpp @@ -331,9 +331,7 @@ ParseCmdAndExecute(LPWSTR lpCmdLine, BOOL bIsFirstLaunch, int nCmdShow) if (!argv) return FALSE; - CStringW Directory; - GetStorageDirectory(Directory); - CAppDB db(Directory); + CAppDB db(CAppDB::GetDefaultPath()); BOOL bAppwizMode = (argc > 1 && MatchCmdOption(argv[1], CMD_KEY_APPWIZ)); if (!bAppwizMode)