mirror of
https://github.com/reactos/reactos.git
synced 2024-07-08 13:45:06 +00:00
![Mark Jansen](/assets/img/avatar_default.png)
Previously, there would be function duplication between installed and available applications. Now this is handled with polymorphism, which allows to re-use a lot of code. Additionally, toolbar buttons are properly disabled now. The mutex used to guard rapps' single instance is renamed, so that the 'new' and old rapps can be run at the same time for testing. CORE-18459
302 lines
7.7 KiB
C++
302 lines
7.7 KiB
C++
/*
|
|
* PROJECT: ReactOS Applications Manager
|
|
* LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later)
|
|
* PURPOSE: Cabinet extraction using FDI API
|
|
* COPYRIGHT: Copyright 2018 Alexander Shaposhnikov (sanchaez@reactos.org)
|
|
*/
|
|
#include "rapps.h"
|
|
#include <debug.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;
|
|
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:
|
|
{
|
|
CStringW szExtractDir, szCabFileName;
|
|
|
|
// Append the destination directory to the file name.
|
|
MultiByteToWide((LPCSTR)pfdin->pv, szExtractDir, CP_UTF8);
|
|
MultiByteToWide(pfdin->psz1, szCabFileName, CP_ACP);
|
|
|
|
if (szCabFileName.Find('\\') >= 0)
|
|
{
|
|
CStringW szNewDirName = szExtractDir;
|
|
int nTokenPos = 0;
|
|
// We do not want to interpret the filename as directory,
|
|
// so bail out before the last token!
|
|
while (szCabFileName.Find('\\', nTokenPos) >= 0)
|
|
{
|
|
CStringW token = szCabFileName.Tokenize(L"\\", nTokenPos);
|
|
if (token.IsEmpty())
|
|
break;
|
|
|
|
szNewDirName += L"\\" + token;
|
|
if (!CreateDirectoryW(szNewDirName, NULL))
|
|
{
|
|
DWORD dwErr = GetLastError();
|
|
if (dwErr != ERROR_ALREADY_EXISTS)
|
|
{
|
|
DPRINT1(
|
|
"ERROR: Unable to create directory %S (err %lu)\n", szNewDirName.GetString(), dwErr);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
CStringW szNewFileName = szExtractDir + L"\\" + szCabFileName;
|
|
|
|
CStringA szFilePathUTF8;
|
|
WideToMultiByte(szNewFileName, szFilePathUTF8, CP_UTF8);
|
|
|
|
// Open the 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 CStringW &szCabName, const CStringW &szCabDir, const 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;
|
|
}
|