1 /* 2 * PROJECT: ReactOS Applications Manager 3 * LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later) 4 * PURPOSE: Cabinet extraction using FDI API 5 * COPYRIGHT: Copyright 2018 Alexander Shaposhnikov (sanchaez@reactos.org) 6 */ 7 #include "rapps.h" 8 9 #include <fdi.h> 10 #include <fcntl.h> 11 12 /* 13 * HACK: treat any input strings as Unicode (UTF-8) 14 * cabinet.dll lacks any sort of a Unicode API, but FCI/FDI 15 * provide an ability to use user-defined callbacks for any file or memory 16 * operations. This flexibility and the magic power of C/C++ casting allows 17 * us to treat input as we please. 18 * This is by far the best way to extract .cab using Unicode paths. 19 */ 20 21 /* String conversion helper functions */ 22 23 // converts CStringW to CStringA using a given codepage 24 inline BOOL WideToMultiByte(const CStringW& szSource, 25 CStringA& szDest, 26 UINT Codepage) 27 { 28 // determine the needed size 29 INT sz = WideCharToMultiByte(Codepage, 30 0, 31 szSource, 32 -1, 33 NULL, 34 NULL, 35 NULL, 36 NULL); 37 if (!sz) 38 return FALSE; 39 40 // do the actual conversion 41 sz = WideCharToMultiByte(Codepage, 42 0, 43 szSource, 44 -1, 45 szDest.GetBuffer(sz), 46 sz, 47 NULL, 48 NULL); 49 50 szDest.ReleaseBuffer(); 51 return sz != 0; 52 } 53 54 // converts CStringA to CStringW using a given codepage 55 inline BOOL MultiByteToWide(const CStringA& szSource, 56 CStringW& szDest, 57 UINT Codepage) 58 { 59 // determine the needed size 60 INT sz = MultiByteToWideChar(Codepage, 61 0, 62 szSource, 63 -1, 64 NULL, 65 NULL); 66 if (!sz) 67 return FALSE; 68 69 // do the actual conversion 70 sz = MultiByteToWideChar(CP_UTF8, 71 0, 72 szSource, 73 -1, 74 szDest.GetBuffer(sz), 75 sz); 76 77 szDest.ReleaseBuffer(); 78 return sz != 0; 79 } 80 81 /* FDICreate callbacks */ 82 83 FNALLOC(fnMemAlloc) 84 { 85 return HeapAlloc(GetProcessHeap(), NULL, cb); 86 } 87 88 FNFREE(fnMemFree) 89 { 90 HeapFree(GetProcessHeap(), NULL, pv); 91 } 92 93 FNOPEN(fnFileOpen) 94 { 95 HANDLE hFile = NULL; 96 DWORD dwDesiredAccess = 0; 97 DWORD dwCreationDisposition = 0; 98 ATL::CStringW szFileName; 99 100 UNREFERENCED_PARAMETER(pmode); 101 102 if (oflag & _O_RDWR) 103 { 104 dwDesiredAccess = GENERIC_READ | GENERIC_WRITE; 105 } 106 else if (oflag & _O_WRONLY) 107 { 108 dwDesiredAccess = GENERIC_WRITE; 109 } 110 else 111 { 112 dwDesiredAccess = GENERIC_READ; 113 } 114 115 if (oflag & _O_CREAT) 116 { 117 dwCreationDisposition = CREATE_ALWAYS; 118 } 119 else 120 { 121 dwCreationDisposition = OPEN_EXISTING; 122 } 123 124 MultiByteToWide(pszFile, szFileName, CP_UTF8); 125 126 hFile = CreateFileW(szFileName, 127 dwDesiredAccess, 128 FILE_SHARE_READ, 129 NULL, 130 dwCreationDisposition, 131 FILE_ATTRIBUTE_NORMAL, 132 NULL); 133 134 return (INT_PTR) hFile; 135 } 136 137 FNREAD(fnFileRead) 138 { 139 DWORD dwBytesRead = 0; 140 141 if (ReadFile((HANDLE) hf, pv, cb, &dwBytesRead, NULL) == FALSE) 142 { 143 dwBytesRead = (DWORD) -1L; 144 } 145 146 return dwBytesRead; 147 } 148 149 FNWRITE(fnFileWrite) 150 { 151 DWORD dwBytesWritten = 0; 152 153 if (WriteFile((HANDLE) hf, pv, cb, &dwBytesWritten, NULL) == FALSE) 154 { 155 dwBytesWritten = (DWORD) -1; 156 } 157 158 return dwBytesWritten; 159 } 160 161 FNCLOSE(fnFileClose) 162 { 163 return (CloseHandle((HANDLE) hf) != FALSE) ? 0 : -1; 164 } 165 166 FNSEEK(fnFileSeek) 167 { 168 return SetFilePointer((HANDLE) hf, dist, NULL, seektype); 169 } 170 171 /* FDICopy callbacks */ 172 173 FNFDINOTIFY(fnNotify) 174 { 175 INT_PTR iResult = 0; 176 177 switch (fdint) 178 { 179 case fdintCOPY_FILE: 180 { 181 ATL::CStringW szNewFileName, szExtractDir, szCabFileName; 182 ATL::CStringA szFilePathUTF8; 183 184 // Append the destination directory to the file name. 185 MultiByteToWide((LPCSTR) pfdin->pv, szExtractDir, CP_UTF8); 186 MultiByteToWide(pfdin->psz1, szCabFileName, CP_ACP); 187 188 szNewFileName = szExtractDir + L"\\" + szCabFileName; 189 190 WideToMultiByte(szNewFileName, szFilePathUTF8, CP_UTF8); 191 192 // Copy file 193 iResult = fnFileOpen((LPSTR) szFilePathUTF8.GetString(), 194 _O_WRONLY | _O_CREAT, 195 0); 196 } 197 break; 198 199 case fdintCLOSE_FILE_INFO: 200 iResult = !fnFileClose(pfdin->hf); 201 break; 202 203 case fdintNEXT_CABINET: 204 if (pfdin->fdie != FDIERROR_NONE) 205 { 206 iResult = -1; 207 } 208 break; 209 210 case fdintPARTIAL_FILE: 211 iResult = 0; 212 break; 213 214 case fdintCABINET_INFO: 215 iResult = 0; 216 break; 217 218 case fdintENUMERATE: 219 iResult = 0; 220 break; 221 222 default: 223 iResult = -1; 224 break; 225 } 226 227 return iResult; 228 } 229 230 /* cabinet.dll FDI function pointers */ 231 232 typedef HFDI(*fnFDICreate)(PFNALLOC, 233 PFNFREE, 234 PFNOPEN, 235 PFNREAD, 236 PFNWRITE, 237 PFNCLOSE, 238 PFNSEEK, 239 int, 240 PERF); 241 242 typedef BOOL(*fnFDICopy)(HFDI, 243 LPSTR, 244 LPSTR, 245 INT, 246 PFNFDINOTIFY, 247 PFNFDIDECRYPT, 248 void FAR *pvUser); 249 250 typedef BOOL(*fnFDIDestroy)(HFDI); 251 252 /* 253 * Extraction function 254 * TODO: require only a full path to the cab as an argument 255 */ 256 BOOL ExtractFilesFromCab(const ATL::CStringW& szCabName, 257 const ATL::CStringW& szCabDir, 258 const ATL::CStringW& szOutputDir) 259 { 260 HINSTANCE hCabinetDll; 261 HFDI ExtractHandler; 262 ERF ExtractErrors; 263 ATL::CStringA szCabNameUTF8, szCabDirUTF8, szOutputDirUTF8; 264 fnFDICreate pfnFDICreate; 265 fnFDICopy pfnFDICopy; 266 fnFDIDestroy pfnFDIDestroy; 267 BOOL bResult; 268 269 // Load cabinet.dll and extract needed functions 270 hCabinetDll = LoadLibraryW(L"cabinet.dll"); 271 272 if (!hCabinetDll) 273 { 274 return FALSE; 275 } 276 277 pfnFDICreate = (fnFDICreate) GetProcAddress(hCabinetDll, "FDICreate"); 278 pfnFDICopy = (fnFDICopy) GetProcAddress(hCabinetDll, "FDICopy"); 279 pfnFDIDestroy = (fnFDIDestroy) GetProcAddress(hCabinetDll, "FDIDestroy"); 280 281 if (!pfnFDICreate || !pfnFDICopy || !pfnFDIDestroy) 282 { 283 FreeLibrary(hCabinetDll); 284 return FALSE; 285 } 286 287 // Create FDI context 288 ExtractHandler = pfnFDICreate(fnMemAlloc, 289 fnMemFree, 290 fnFileOpen, 291 fnFileRead, 292 fnFileWrite, 293 fnFileClose, 294 fnFileSeek, 295 cpuUNKNOWN, 296 &ExtractErrors); 297 298 if (!ExtractHandler) 299 { 300 FreeLibrary(hCabinetDll); 301 return FALSE; 302 } 303 304 // Create output dir 305 bResult = CreateDirectoryW(szOutputDir, NULL); 306 307 if (bResult || GetLastError() == ERROR_ALREADY_EXISTS) 308 { 309 // Convert wide strings to UTF-8 310 bResult = WideToMultiByte(szCabName, szCabNameUTF8, CP_UTF8); 311 bResult &= WideToMultiByte(szCabDir, szCabDirUTF8, CP_UTF8); 312 bResult &= WideToMultiByte(szOutputDir, szOutputDirUTF8, CP_UTF8); 313 } 314 315 // Perform extraction 316 if (bResult) 317 { 318 // Add a slash to cab name as required by the api 319 szCabNameUTF8 = "\\" + szCabNameUTF8; 320 321 bResult = pfnFDICopy(ExtractHandler, 322 (LPSTR) szCabNameUTF8.GetString(), 323 (LPSTR) szCabDirUTF8.GetString(), 324 0, 325 fnNotify, 326 NULL, 327 (void FAR *) szOutputDirUTF8.GetString()); 328 } 329 330 pfnFDIDestroy(ExtractHandler); 331 FreeLibrary(hCabinetDll); 332 return bResult; 333 } 334