[RAPPS] Replace Extract with FDI for handling .cab 426/head
authorAlexander Shaposhnikov <sanchaez@reactos.org>
Sun, 25 Feb 2018 13:20:00 +0000 (15:20 +0200)
committerAlexander Shaposhnikov <sanchaez@reactos.org>
Sun, 11 Mar 2018 19:56:32 +0000 (21:56 +0200)
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
base/applications/rapps/available.cpp
base/applications/rapps/cabinet.cpp [new file with mode: 0644]
base/applications/rapps/include/available.h
base/applications/rapps/include/cabinet.h [deleted file]
base/applications/rapps/include/misc.h
base/applications/rapps/misc.cpp

index cdbb6f9..ce8ba3a 100644 (file)
@@ -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
index 04e5005..5ed7119 100644 (file)
@@ -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 (file)
index 0000000..11b301e
--- /dev/null
@@ -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     (sanchaez@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;
+}
index 782f960..876465f 100644 (file)
@@ -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 (file)
index 02bbd57..0000000
+++ /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);
index b3f3063..738f674 100644 (file)
@@ -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
index 1a52731..db0ae0b 100644 (file)
 
 #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)