https://git.reactos.org/?p=reactos.git;a=commitdiff;h=95915501162e727900e077d97026a3e2e00f117c
commit 95915501162e727900e077d97026a3e2e00f117c Author: Alexander Shaposhnikov <sanch...@reactos.org> AuthorDate: Sun Feb 25 15:20:00 2018 +0200 Commit: Alexander Shaposhnikov <sanch...@reactos.org> CommitDate: Sun Mar 11 21:56:32 2018 +0200 [RAPPS] Replace Extract with FDI for handling .cab FDI allows to have user-defined callbacks for file handling. Since it doesn't provide support for Unicode we convert strings to multi-byte UTF-8 and handle them appropriately in the callbacks. They are properly null-terminated so FDI won't choke when doing operations with strings. Thanks to hbelusca and mjansen for the help. CORE-14466 --- base/applications/rapps/CMakeLists.txt | 2 +- base/applications/rapps/available.cpp | 8 +- base/applications/rapps/cabinet.cpp | 334 ++++++++++++++++++++++++++++ base/applications/rapps/include/available.h | 2 + base/applications/rapps/include/cabinet.h | 32 --- base/applications/rapps/include/misc.h | 6 +- base/applications/rapps/misc.cpp | 54 ----- 7 files changed, 348 insertions(+), 90 deletions(-) diff --git a/base/applications/rapps/CMakeLists.txt b/base/applications/rapps/CMakeLists.txt index cdbb6f97dc..ce8ba3a0ba 100644 --- a/base/applications/rapps/CMakeLists.txt +++ b/base/applications/rapps/CMakeLists.txt @@ -9,6 +9,7 @@ include_directories(include) list(APPEND SOURCE aboutdlg.cpp available.cpp + cabinet.cpp gui.cpp installed.cpp integrity.cpp @@ -22,7 +23,6 @@ list(APPEND SOURCE include/gui.h include/dialogs.h include/installed.h - include/cabinet.h include/crichedit.h include/defines.h include/misc.h diff --git a/base/applications/rapps/available.cpp b/base/applications/rapps/available.cpp index 04e5005e5a..5ed7119853 100644 --- a/base/applications/rapps/available.cpp +++ b/base/applications/rapps/available.cpp @@ -213,7 +213,9 @@ AvailableStrings::AvailableStrings() if (GetStorageDirectory(szPath)) { szAppsPath = szPath + L"\\rapps\\"; - szCabPath = szPath + L"\\rappmgr.cab"; + szCabName = L"rappmgr.cab"; + szCabDir = szPath; + szCabPath = (szCabDir + L"\\") + szCabName; szSearchPath = szAppsPath + L"*.txt"; } } @@ -282,7 +284,9 @@ BOOL CAvailableApps::UpdateAppsDB() CDownloadManager::DownloadApplicationsDB(APPLICATION_DATABASE_URL); - if (!ExtractFilesFromCab(m_Strings.szCabPath, m_Strings.szAppsPath)) + if (!ExtractFilesFromCab(m_Strings.szCabName, + m_Strings.szCabDir, + m_Strings.szAppsPath)) { return FALSE; } diff --git a/base/applications/rapps/cabinet.cpp b/base/applications/rapps/cabinet.cpp new file mode 100644 index 0000000000..11b301ed9c --- /dev/null +++ b/base/applications/rapps/cabinet.cpp @@ -0,0 +1,334 @@ +/* +* PROJECT: ReactOS Applications Manager +* LICENSE: GPL-2.0+ (https://spdx.org/licenses/GPL-2.0+) +* FILE: base/applications/rapps/cabinet.cpp +* PURPOSE: Cabinet extraction using FDI API +* COPYRIGHT: Copyright 2018 Alexander Shaposhnikov (sanch...@reactos.org) +*/ +#include "rapps.h" + +#include <fdi.h> +#include <fcntl.h> + +/* + * HACK: treat any input strings as Unicode (UTF-8) + * cabinet.dll lacks any sort of a Unicode API, but FCI/FDI + * provide an ability to use user-defined callbacks for any file or memory + * operations. This flexibility and the magic power of C/C++ casting allows + * us to treat input as we please. + * This is by far the best way to extract .cab using Unicode paths. + */ + +/* String conversion helper functions */ + +// converts CStringW to CStringA using a given codepage +inline BOOL WideToMultiByte(const CStringW& szSource, + CStringA& szDest, + UINT Codepage) +{ + // determine the needed size + INT sz = WideCharToMultiByte(Codepage, + 0, + szSource, + -1, + NULL, + NULL, + NULL, + NULL); + if (!sz) + return FALSE; + + // do the actual conversion + sz = WideCharToMultiByte(Codepage, + 0, + szSource, + -1, + szDest.GetBuffer(sz), + sz, + NULL, + NULL); + + szDest.ReleaseBuffer(); + return sz != 0; +} + +// converts CStringA to CStringW using a given codepage +inline BOOL MultiByteToWide(const CStringA& szSource, + CStringW& szDest, + UINT Codepage) +{ + // determine the needed size + INT sz = MultiByteToWideChar(Codepage, + 0, + szSource, + -1, + NULL, + NULL); + if (!sz) + return FALSE; + + // do the actual conversion + sz = MultiByteToWideChar(CP_UTF8, + 0, + szSource, + -1, + szDest.GetBuffer(sz), + sz); + + szDest.ReleaseBuffer(); + return sz != 0; +} + +/* FDICreate callbacks */ + +FNALLOC(fnMemAlloc) +{ + return HeapAlloc(GetProcessHeap(), NULL, cb); +} + +FNFREE(fnMemFree) +{ + HeapFree(GetProcessHeap(), NULL, pv); +} + +FNOPEN(fnFileOpen) +{ + HANDLE hFile = NULL; + DWORD dwDesiredAccess = 0; + DWORD dwCreationDisposition = 0; + ATL::CStringW szFileName; + + UNREFERENCED_PARAMETER(pmode); + + if (oflag & _O_RDWR) + { + dwDesiredAccess = GENERIC_READ | GENERIC_WRITE; + } + else if (oflag & _O_WRONLY) + { + dwDesiredAccess = GENERIC_WRITE; + } + else + { + dwDesiredAccess = GENERIC_READ; + } + + if (oflag & _O_CREAT) + { + dwCreationDisposition = CREATE_ALWAYS; + } + else + { + dwCreationDisposition = OPEN_EXISTING; + } + + MultiByteToWide(pszFile, szFileName, CP_UTF8); + + hFile = CreateFileW(szFileName, + dwDesiredAccess, + FILE_SHARE_READ, + NULL, + dwCreationDisposition, + FILE_ATTRIBUTE_NORMAL, + NULL); + + return (INT_PTR) hFile; +} + +FNREAD(fnFileRead) +{ + DWORD dwBytesRead = 0; + + if (ReadFile((HANDLE) hf, pv, cb, &dwBytesRead, NULL) == FALSE) + { + dwBytesRead = (DWORD) -1L; + } + + return dwBytesRead; +} + +FNWRITE(fnFileWrite) +{ + DWORD dwBytesWritten = 0; + + if (WriteFile((HANDLE) hf, pv, cb, &dwBytesWritten, NULL) == FALSE) + { + dwBytesWritten = (DWORD) -1; + } + + return dwBytesWritten; +} + +FNCLOSE(fnFileClose) +{ + return (CloseHandle((HANDLE) hf) != FALSE) ? 0 : -1; +} + +FNSEEK(fnFileSeek) +{ + return SetFilePointer((HANDLE) hf, dist, NULL, seektype); +} + +/* FDICopy callbacks */ + +FNFDINOTIFY(fnNotify) +{ + INT_PTR iResult = 0; + + switch (fdint) + { + case fdintCOPY_FILE: + { + ATL::CStringW szNewFileName, szExtractDir, szCabFileName; + ATL::CStringA szFilePathUTF8; + + // Append the destination directory to the file name. + MultiByteToWide((LPCSTR) pfdin->pv, szExtractDir, CP_UTF8); + MultiByteToWide(pfdin->psz1, szCabFileName, CP_ACP); + + szNewFileName = szExtractDir + L"\\" + szCabFileName; + + WideToMultiByte(szNewFileName, szFilePathUTF8, CP_UTF8); + + // Copy file + iResult = fnFileOpen((LPSTR) szFilePathUTF8.GetString(), + _O_WRONLY | _O_CREAT, + 0); + } + break; + + case fdintCLOSE_FILE_INFO: + iResult = !fnFileClose(pfdin->hf); + break; + + case fdintNEXT_CABINET: + if (pfdin->fdie != FDIERROR_NONE) + { + iResult = -1; + } + break; + + case fdintPARTIAL_FILE: + iResult = 0; + break; + + case fdintCABINET_INFO: + iResult = 0; + break; + + case fdintENUMERATE: + iResult = 0; + break; + + default: + iResult = -1; + break; + } + + return iResult; +} + +/* cabinet.dll FDI function pointers */ + +typedef HFDI(*fnFDICreate)(PFNALLOC, + PFNFREE, + PFNOPEN, + PFNREAD, + PFNWRITE, + PFNCLOSE, + PFNSEEK, + int, + PERF); + +typedef BOOL(*fnFDICopy)(HFDI, + LPSTR, + LPSTR, + INT, + PFNFDINOTIFY, + PFNFDIDECRYPT, + void FAR *pvUser); + +typedef BOOL(*fnFDIDestroy)(HFDI); + +/* + * Extraction function + * TODO: require only a full path to the cab as an argument + */ +BOOL ExtractFilesFromCab(const ATL::CStringW& szCabName, + const ATL::CStringW& szCabDir, + const ATL::CStringW& szOutputDir) +{ + HINSTANCE hCabinetDll; + HFDI ExtractHandler; + ERF ExtractErrors; + ATL::CStringA szCabNameUTF8, szCabDirUTF8, szOutputDirUTF8; + fnFDICreate pfnFDICreate; + fnFDICopy pfnFDICopy; + fnFDIDestroy pfnFDIDestroy; + BOOL bResult; + + // Load cabinet.dll and extract needed functions + hCabinetDll = LoadLibraryW(L"cabinet.dll"); + + if (!hCabinetDll) + { + return FALSE; + } + + pfnFDICreate = (fnFDICreate) GetProcAddress(hCabinetDll, "FDICreate"); + pfnFDICopy = (fnFDICopy) GetProcAddress(hCabinetDll, "FDICopy"); + pfnFDIDestroy = (fnFDIDestroy) GetProcAddress(hCabinetDll, "FDIDestroy"); + + if (!pfnFDICreate || !pfnFDICopy || !pfnFDIDestroy) + { + FreeLibrary(hCabinetDll); + return FALSE; + } + + // Create FDI context + ExtractHandler = pfnFDICreate(fnMemAlloc, + fnMemFree, + fnFileOpen, + fnFileRead, + fnFileWrite, + fnFileClose, + fnFileSeek, + cpuUNKNOWN, + &ExtractErrors); + + if (!ExtractHandler) + { + FreeLibrary(hCabinetDll); + return FALSE; + } + + // Create output dir + bResult = CreateDirectoryW(szOutputDir, NULL); + + if (bResult || GetLastError() == ERROR_ALREADY_EXISTS) + { + // Convert wide strings to UTF-8 + bResult = WideToMultiByte(szCabName, szCabNameUTF8, CP_UTF8); + bResult &= WideToMultiByte(szCabDir, szCabDirUTF8, CP_UTF8); + bResult &= WideToMultiByte(szOutputDir, szOutputDirUTF8, CP_UTF8); + } + + // Perform extraction + if (bResult) + { + // Add a slash to cab name as required by the api + szCabNameUTF8 = "\\" + szCabNameUTF8; + + bResult = pfnFDICopy(ExtractHandler, + (LPSTR) szCabNameUTF8.GetString(), + (LPSTR) szCabDirUTF8.GetString(), + 0, + fnNotify, + NULL, + (void FAR *) szOutputDirUTF8.GetString()); + } + + pfnFDIDestroy(ExtractHandler); + FreeLibrary(hCabinetDll); + return bResult; +} diff --git a/base/applications/rapps/include/available.h b/base/applications/rapps/include/available.h index 782f960be8..876465fd09 100644 --- a/base/applications/rapps/include/available.h +++ b/base/applications/rapps/include/available.h @@ -86,6 +86,8 @@ struct AvailableStrings ATL::CStringW szCabPath; ATL::CStringW szAppsPath; ATL::CStringW szSearchPath; + ATL::CStringW szCabName; + ATL::CStringW szCabDir; AvailableStrings(); }; diff --git a/base/applications/rapps/include/cabinet.h b/base/applications/rapps/include/cabinet.h deleted file mode 100644 index 02bbd5738d..0000000000 --- a/base/applications/rapps/include/cabinet.h +++ /dev/null @@ -1,32 +0,0 @@ -// Structs related to .cab extraction -// FIXME: they should belong to exports of cabinet.dll -#pragma once - -struct ERF -{ - INT erfOper; - INT erfType; - BOOL fError; -}; - -struct FILELIST -{ - LPSTR FileName; - FILELIST *next; - BOOL DoExtract; -}; - -struct SESSION -{ - INT FileSize; - ERF Error; - FILELIST *FileList; - INT FileCount; - INT Operation; - CHAR Destination[MAX_PATH]; - CHAR CurrentFile[MAX_PATH]; - CHAR Reserved[MAX_PATH]; - FILELIST *FilterList; -}; - -typedef HRESULT(WINAPI *fnExtract)(SESSION *dest, LPCSTR szCabName); diff --git a/base/applications/rapps/include/misc.h b/base/applications/rapps/include/misc.h index b3f3063001..738f6746cb 100644 --- a/base/applications/rapps/include/misc.h +++ b/base/applications/rapps/include/misc.h @@ -14,12 +14,16 @@ VOID ShowPopupMenu(HWND hwnd, UINT MenuID, UINT DefaultItem); BOOL StartProcess(ATL::CStringW &Path, BOOL Wait); BOOL StartProcess(LPWSTR lpPath, BOOL Wait); BOOL GetStorageDirectory(ATL::CStringW &lpDirectory); -BOOL ExtractFilesFromCab(LPCWSTR lpCabName, LPCWSTR lpOutputPath); + VOID InitLogs(); VOID FreeLogs(); BOOL WriteLogMessage(WORD wType, DWORD dwEventID, LPCWSTR lpMsg); BOOL GetInstalledVersion(ATL::CStringW *pszVersion, const ATL::CStringW &szRegName); +BOOL ExtractFilesFromCab(const ATL::CStringW& szCabName, + const ATL::CStringW& szCabDir, + const ATL::CStringW& szOutputDir); + class CConfigParser { // Locale names cache diff --git a/base/applications/rapps/misc.cpp b/base/applications/rapps/misc.cpp index 1a527317af..db0ae0bf1b 100644 --- a/base/applications/rapps/misc.cpp +++ b/base/applications/rapps/misc.cpp @@ -11,11 +11,6 @@ #include "gui.h" #include "misc.h" -#include "cabinet.h" - - /* SESSION Operation */ -#define EXTRACT_FILLFILELIST 0x00000001 -#define EXTRACT_EXTRACTFILES 0x00000002 static HANDLE hLog = NULL; @@ -203,55 +198,6 @@ BOOL GetStorageDirectory(ATL::CStringW& Directory) return (CreateDirectoryW(Directory.GetString(), NULL) || GetLastError() == ERROR_ALREADY_EXISTS); } -BOOL ExtractFilesFromCab(const ATL::CStringW &CabName, const ATL::CStringW &OutputPath) -{ - return ExtractFilesFromCab(CabName.GetString(), OutputPath.GetString()); -} - -BOOL ExtractFilesFromCab(LPCWSTR lpCabName, LPCWSTR lpOutputPath) -{ - HINSTANCE hCabinetDll; - CHAR szCabName[MAX_PATH]; - SESSION Dest; - HRESULT Result; - fnExtract pfnExtract; - - hCabinetDll = LoadLibraryW(L"cabinet.dll"); - if (hCabinetDll) - { - pfnExtract = (fnExtract) GetProcAddress(hCabinetDll, "Extract"); - if (pfnExtract) - { - ZeroMemory(&Dest, sizeof(Dest)); - - WideCharToMultiByte(CP_ACP, 0, lpOutputPath, -1, Dest.Destination, MAX_PATH, NULL, NULL); - WideCharToMultiByte(CP_ACP, 0, lpCabName, -1, szCabName, _countof(szCabName), NULL, NULL); - Dest.Operation = EXTRACT_FILLFILELIST; - - Result = pfnExtract(&Dest, szCabName); - if (Result == S_OK) - { - Dest.Operation = EXTRACT_EXTRACTFILES; - CreateDirectoryW(lpOutputPath, NULL); - - Result = pfnExtract(&Dest, szCabName); - if (Result == S_OK) - { - FreeLibrary(hCabinetDll); - return TRUE; - } - else - { - RemoveDirectoryW(lpOutputPath); - } - } - } - FreeLibrary(hCabinetDll); - } - - return FALSE; -} - VOID InitLogs() { if (!SettingsInfo.bLogEnabled)