Something went wrong. Try again.
Reactos
Something went wrong. Try again.
8.7 kB · 326 lines
C++
at master
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327/* * 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 codepageinline BOOLWideToMultiByte(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 codepageinline BOOLMultiByteToWide(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(Codepage, 0, szSource, -1, szDest.GetBuffer(sz), sz);
szDest.ReleaseBuffer(); return sz != 0;}
struct NotifyData{ EXTRACTCALLBACK Callback; void *CallerCookie; LPCSTR OutputDir;};
/* 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; NotifyData *pND = (NotifyData *)pfdin->pv;
switch (fdint) { case fdintCOPY_FILE: { CStringW szExtractDir, szCabFileName;
// Append the destination directory to the file name. MultiByteToWide(pND->OutputDir, szExtractDir, CP_UTF8); UINT codepage = (pfdin->attribs & _A_NAME_IS_UTF) ? CP_UTF8 : CP_ACP; MultiByteToWide(pfdin->psz1, szCabFileName, codepage);
if (!NotifyFileExtractCallback(szCabFileName, pfdin->cb, pfdin->attribs, pND->Callback, pND->CallerCookie)) { break; // Skip file }
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 */BOOLExtractFilesFromCab(const CStringW &szCabName, const CStringW &szCabDir, const CStringW &szOutputDir, EXTRACTCALLBACK Callback, void *Cookie){ HINSTANCE hCabinetDll; HFDI ExtractHandler; ERF ExtractErrors; ATL::CStringA szCabNameUTF8, szCabDirUTF8, szOutputDirUTF8; fnFDICreate pfnFDICreate; fnFDICopy pfnFDICopy; fnFDIDestroy pfnFDIDestroy; BOOL bResult; NotifyData nd = { Callback, Cookie };
// 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;
nd.OutputDir = szOutputDirUTF8.GetString(); bResult = pfnFDICopy( ExtractHandler, (LPSTR)szCabNameUTF8.GetString(), (LPSTR)szCabDirUTF8.GetString(), 0, fnNotify, NULL, (void FAR *)&nd); }
pfnFDIDestroy(ExtractHandler); FreeLibrary(hCabinetDll); return bResult;}
BOOLExtractFilesFromCab(LPCWSTR FullCabPath, const CStringW &szOutputDir, EXTRACTCALLBACK Callback, void *Cookie){ CStringW dir, file = SplitFileAndDirectory(FullCabPath, &dir); return ExtractFilesFromCab(file, dir, szOutputDir, Callback, Cookie);}