[LOCALSPL]
authorColin Finck <colin@reactos.org>
Thu, 24 Nov 2016 19:24:27 +0000 (19:24 +0000)
committerColin Finck <colin@reactos.org>
Thu, 24 Nov 2016 19:24:27 +0000 (19:24 +0000)
Tests show that the Print Provider functions are less forgiving and expect winspool.drv and spoolss.dll to perform the necessary sanity checks.
Also they don't default to the current environment if none was supplied as pEnvironment. Change our functions accordingly to pass the tests.

[LOCALSPL_APITEST]
Add a test to prove that spoolss.dll isn't just a forwarder, but also performs some sanity checks even though winspool.drv and localspl.dll also do.
This rather belongs to spoolss_apitest. But it needs to be tested inside the Spooler Server Process and it's not worth it to copy all the DLL injection code to spoolss_apitest just for a single test.

[SPOOLSS]
Check for an invalid buffer in GetPrintProcessorDirectoryW.

[WINSPOOL]
* Check the level before the RPC call in GetPrintProcessorDirectoryW.
* Supply our current environment if the caller didn't give any in EnumPrintProcessorsW and GetPrintProcessorDirectoryW.
* Implement GetPrintProcessorDirectoryA.

[WINSPOOL_APITEST]
Prove that pcbNeeded isn't touched when calling GetPrintProcessorDirectoryW with an invalid level.

ReactOS now passes all API tests for GetPrintProcessorDirectoryA and GetPrintProcessorDirectoryW. These tests also cover all known behaviors of both functions.
CORE-12399

svn path=/trunk/; revision=73373

reactos/win32ss/printing/base/spoolss/printprocessors.c
reactos/win32ss/printing/base/winspool/printprocessors.c
reactos/win32ss/printing/base/winspool/winspool.spec
reactos/win32ss/printing/include/prtprocenv.h [new file with mode: 0644]
reactos/win32ss/printing/providers/localspl/main.c
reactos/win32ss/printing/providers/localspl/printprocessors.c
rostests/apitests/localspl/dll/fpGetPrintProcessorDirectory.c
rostests/apitests/localspl/dll/main.c
rostests/apitests/winspool/GetPrintProcessorDirectory.c

index 31fe0e3..0fc23aa 100644 (file)
@@ -26,6 +26,13 @@ EnumPrintProcessorsW(PWSTR pName, PWSTR pEnvironment, DWORD Level, PBYTE pPrintP
 BOOL WINAPI
 GetPrintProcessorDirectoryW(PWSTR pName, PWSTR pEnvironment, DWORD Level, PBYTE pPrintProcessorInfo, DWORD cbBuf, PDWORD pcbNeeded)
 {
+    // Sanity checks
+    if (cbBuf && !pPrintProcessorInfo)
+    {
+        SetLastError(ERROR_INVALID_USER_BUFFER);
+        return FALSE;
+    }
+
     // Always call this function on the Local Spooler.
     PSPOOLSS_PRINT_PROVIDER pPrintProvider = CONTAINING_RECORD(PrintProviderList.Flink, SPOOLSS_PRINT_PROVIDER, Entry);
     return pPrintProvider->PrintProvider.fpGetPrintProcessorDirectory(pName, pEnvironment, Level, pPrintProcessorInfo, cbBuf, pcbNeeded);
index 97a0759..ba71970 100644 (file)
@@ -2,10 +2,11 @@
  * PROJECT:     ReactOS Spooler API
  * LICENSE:     GNU LGPL v2.1 or any later version as published by the Free Software Foundation
  * PURPOSE:     Functions related to Print Processors
- * COPYRIGHT:   Copyright 2015 Colin Finck <colin@reactos.org>
+ * COPYRIGHT:   Copyright 2015-2016 Colin Finck <colin@reactos.org>
  */
 
 #include "precomp.h"
+#include <prtprocenv.h>
 
 static void
 _MarshallUpDatatypesInfo(PDATATYPES_INFO_1W pDatatypesInfo1)
@@ -82,6 +83,10 @@ EnumPrintProcessorsW(PWSTR pName, PWSTR pEnvironment, DWORD Level, PBYTE pPrintP
     DWORD i;
     PBYTE p = pPrintProcessorInfo;
 
+    // Choose our current environment if the caller didn't give any.
+    if (!pEnvironment)
+        pEnvironment = (PWSTR)wszCurrentEnvironment;
+
     // Do the RPC call
     RpcTryExcept
     {
@@ -90,7 +95,6 @@ EnumPrintProcessorsW(PWSTR pName, PWSTR pEnvironment, DWORD Level, PBYTE pPrintP
     RpcExcept(EXCEPTION_EXECUTE_HANDLER)
     {
         dwErrorCode = RpcExceptionCode();
-        ERR("_RpcEnumPrintProcessors failed with exception code %lu!\n", dwErrorCode);
     }
     RpcEndExcept;
 
@@ -109,23 +113,106 @@ EnumPrintProcessorsW(PWSTR pName, PWSTR pEnvironment, DWORD Level, PBYTE pPrintP
 }
 
 BOOL WINAPI
-GetPrintProcessorDirectoryW(PWSTR pName, PWSTR pEnvironment, DWORD Level, PBYTE pPrintProcessorInfo, DWORD cbBuf, PDWORD pcbNeeded)
+GetPrintProcessorDirectoryA(PSTR pName, PSTR pEnvironment, DWORD Level, PBYTE pPrintProcessorInfo, DWORD cbBuf, PDWORD pcbNeeded)
 {
     BOOL bReturnValue = FALSE;
+    DWORD cch;
+    PWSTR pwszName = NULL;
+    PWSTR pwszEnvironment = NULL;
+    PWSTR pwszPrintProcessorInfo = NULL;
+
+    if (pName)
+    {
+        // Convert pName to a Unicode string pwszName.
+        cch = strlen(pName);
+
+        pwszName = HeapAlloc(hProcessHeap, 0, (cch + 1) * sizeof(WCHAR));
+        if (!pwszName)
+        {
+            ERR("HeapAlloc failed for pwszName with last error %lu!\n", GetLastError());
+            goto Cleanup;
+        }
+
+        MultiByteToWideChar(CP_ACP, 0, pName, -1, pwszName, cch + 1);
+    }
+
+    if (pEnvironment)
+    {
+        // Convert pEnvironment to a Unicode string pwszEnvironment.
+        cch = strlen(pEnvironment);
+
+        pwszEnvironment = HeapAlloc(hProcessHeap, 0, (cch + 1) * sizeof(WCHAR));
+        if (!pwszEnvironment)
+        {
+            ERR("HeapAlloc failed for pwszEnvironment with last error %lu!\n", GetLastError());
+            goto Cleanup;
+        }
+
+        MultiByteToWideChar(CP_ACP, 0, pEnvironment, -1, pwszEnvironment, cch + 1);
+    }
+
+    if (cbBuf && pPrintProcessorInfo)
+    {
+        // Allocate a temporary buffer for the Unicode result.
+        // We can just go with cbBuf here. The user should have set it based on pcbNeeded returned in a previous call and our
+        // pcbNeeded is the same for the A and W functions.
+        pwszPrintProcessorInfo = HeapAlloc(hProcessHeap, 0, cbBuf);
+        if (!pwszPrintProcessorInfo)
+        {
+            ERR("HeapAlloc failed for pwszPrintProcessorInfo with last error %lu!\n", GetLastError());
+            goto Cleanup;
+        }
+    }
+
+    bReturnValue = GetPrintProcessorDirectoryW(pwszName, pwszEnvironment, Level, (PBYTE)pwszPrintProcessorInfo, cbBuf, pcbNeeded);
+
+    if (bReturnValue)
+    {
+        // Convert pwszPrintProcessorInfo to an ANSI string pPrintProcessorInfo.
+        WideCharToMultiByte(CP_ACP, 0, pwszPrintProcessorInfo, -1, (PSTR)pPrintProcessorInfo, cbBuf, NULL, NULL);
+    }
+
+Cleanup:
+    if (pwszName)
+        HeapFree(hProcessHeap, 0, pwszName);
+
+    if (pwszEnvironment)
+        HeapFree(hProcessHeap, 0, pwszEnvironment);
+
+    if (pwszPrintProcessorInfo)
+        HeapFree(hProcessHeap, 0, pwszPrintProcessorInfo);
+
+    return bReturnValue;
+}
+
+BOOL WINAPI
+GetPrintProcessorDirectoryW(PWSTR pName, PWSTR pEnvironment, DWORD Level, PBYTE pPrintProcessorInfo, DWORD cbBuf, PDWORD pcbNeeded)
+{
     DWORD dwErrorCode;
 
+    // Sanity checks
+    if (Level != 1)
+    {
+        dwErrorCode = ERROR_INVALID_LEVEL;
+        goto Cleanup;
+    }
+
+    // Choose our current environment if the caller didn't give any.
+    if (!pEnvironment)
+        pEnvironment = (PWSTR)wszCurrentEnvironment;
+
     // Do the RPC call
     RpcTryExcept
     {
         dwErrorCode = _RpcGetPrintProcessorDirectory(pName, pEnvironment, Level, pPrintProcessorInfo, cbBuf, pcbNeeded);
-        SetLastError(dwErrorCode);
-        bReturnValue = (dwErrorCode == ERROR_SUCCESS);
     }
     RpcExcept(EXCEPTION_EXECUTE_HANDLER)
     {
-        ERR("_RpcGetPrintProcessorDirectory failed with exception code %lu!\n", RpcExceptionCode());
+        dwErrorCode = RpcExceptionCode();
     }
     RpcEndExcept;
 
-    return bReturnValue;
+Cleanup:
+    SetLastError(dwErrorCode);
+    return (dwErrorCode == ERROR_SUCCESS);
 }
index 76e5376..9dcd522 100644 (file)
 243 stub GetFormW
 244 stdcall GetJobA(long long long ptr long ptr)
 245 stdcall GetJobW(long long long ptr long ptr)
-246 stdcall -stub GetPrintProcessorDirectoryA(str str long ptr long ptr)
+246 stdcall GetPrintProcessorDirectoryA(str str long ptr long ptr)
 247 stdcall GetPrintProcessorDirectoryW(wstr wstr long ptr long ptr)
 248 stdcall GetPrinterA(long long ptr long ptr)
 249 stub GetPrinterDataA
diff --git a/reactos/win32ss/printing/include/prtprocenv.h b/reactos/win32ss/printing/include/prtprocenv.h
new file mode 100644 (file)
index 0000000..b5ae866
--- /dev/null
@@ -0,0 +1,24 @@
+/*
+ * PROJECT:     ReactOS Printing Include files
+ * LICENSE:     GNU LGPL v2.1 or any later version as published by the Free Software Foundation
+ * PURPOSE:     Provide a constant for the current Printing Processor Environment based on the architecture
+ * COPYRIGHT:   Copyright 2016 Colin Finck <colin@reactos.org>
+ */
+
+#ifndef _REACTOS_PRTPROCENV_H
+#define _REACTOS_PRTPROCENV_H
+
+const WCHAR wszCurrentEnvironment[] =
+#if defined(_X86_)
+    L"Windows NT x86";
+#elif defined(_AMD64_)
+    L"Windows x64";
+#elif defined(_ARM_)
+    L"Windows ARM";
+#else
+    #error Unsupported architecture
+#endif
+
+const DWORD cbCurrentEnvironment = sizeof(wszCurrentEnvironment);
+
+#endif
index 98b4d17..aada4a9 100644 (file)
@@ -2,7 +2,7 @@
  * PROJECT:     ReactOS Local Spooler
  * LICENSE:     GNU LGPL v2.1 or any later version as published by the Free Software Foundation
  * PURPOSE:     Main functions
- * COPYRIGHT:   Copyright 2015 Colin Finck <colin@reactos.org>
+ * COPYRIGHT:   Copyright 2015-2016 Colin Finck <colin@reactos.org>
  */
 
 #include "precomp.h"
@@ -12,18 +12,7 @@ WCHAR wszSpoolDirectory[MAX_PATH];
 DWORD cchSpoolDirectory;
 
 // Global Constants
-const WCHAR wszCurrentEnvironment[] =
-#if defined(_X86_)
-    L"Windows NT x86";
-#elif defined(_AMD64_)
-    L"Windows x64";
-#elif defined(_ARM_)
-    L"Windows ARM";
-#else
-    #error Unsupported architecture
-#endif
-
-const DWORD cbCurrentEnvironment = sizeof(wszCurrentEnvironment);
+#include <prtprocenv.h>
 
 const WCHAR wszDefaultDocumentName[] = L"Local Downlevel Document";
 
index bda9e53..66214d2 100644 (file)
@@ -2,7 +2,7 @@
  * PROJECT:     ReactOS Local Spooler
  * LICENSE:     GNU LGPL v2.1 or any later version as published by the Free Software Foundation
  * PURPOSE:     Functions related to Print Processors
- * COPYRIGHT:   Copyright 2015 Colin Finck <colin@reactos.org>
+ * COPYRIGHT:   Copyright 2015-2016 Colin Finck <colin@reactos.org>
  */
 
 #include "precomp.h"
@@ -17,7 +17,7 @@ static LIST_ENTRY _PrintProcessorList;
  * Checks a supplied pEnvironment variable for validity and opens its registry key.
  *
  * @param pEnvironment
- * The pEnvironment variable to check. Can be NULL to use the current environment.
+ * The pEnvironment variable to check.
  *
  * @param hKey
  * On success, this variable will contain a HKEY to the opened registry key of the environment.
@@ -36,9 +36,12 @@ _OpenEnvironment(PCWSTR pEnvironment, PHKEY hKey)
     DWORD dwErrorCode;
     PWSTR pwszEnvironmentKey = NULL;
 
-    // Use the current environment if none was supplied.
+    // Sanity checks
     if (!pEnvironment)
-        pEnvironment = wszCurrentEnvironment;
+    {
+        dwErrorCode = ERROR_INVALID_ENVIRONMENT;
+        goto Cleanup;
+    }
 
     // Construct the registry key of the demanded environment.
     cchEnvironment = wcslen(pEnvironment);
@@ -140,7 +143,7 @@ InitializePrintProcessorList()
     InitializeListHead(&_PrintProcessorList);
     
     // Prepare the path to the Print Processor directory.
-    if (!LocalGetPrintProcessorDirectory(NULL, NULL, 1, (PBYTE)wszPrintProcessorPath, sizeof(wszPrintProcessorPath), &cchPrintProcessorPath))
+    if (!LocalGetPrintProcessorDirectory(NULL, (PWSTR)wszCurrentEnvironment, 1, (PBYTE)wszPrintProcessorPath, sizeof(wszPrintProcessorPath), &cchPrintProcessorPath))
     {
         dwErrorCode = GetLastError();
         goto Cleanup;
@@ -155,7 +158,7 @@ InitializePrintProcessorList()
     ++cchPrintProcessorPath;
 
     // Open the environment registry key.
-    dwErrorCode = _OpenEnvironment(NULL, &hKey);
+    dwErrorCode = _OpenEnvironment(wszCurrentEnvironment, &hKey);
     if (dwErrorCode != ERROR_SUCCESS)
     {
         ERR("_OpenEnvironment failed with error %lu!\n", dwErrorCode);
@@ -602,7 +605,6 @@ Cleanup:
  *
  * @param pEnvironment
  * One of the predefined operating system and architecture "environment" strings (like "Windows NT x86").
- * Alternatively, NULL to output the Print Processor directory of the current environment.
  *
  * @param Level
  * The level of the (non-existing) structure supplied through pPrintProcessorInfo. This must be 1.
@@ -633,20 +635,6 @@ LocalGetPrintProcessorDirectory(PWSTR pName, PWSTR pEnvironment, DWORD Level, PB
     HKEY hKey = NULL;
     PWSTR pwszDirectory = (PWSTR)pPrintProcessorInfo;
 
-    // Sanity checks
-    if (Level != 1)
-    {
-        dwErrorCode = ERROR_INVALID_LEVEL;
-        goto Cleanup;
-    }
-
-    if (!pcbNeeded)
-    {
-        // This error is also caught by RPC and returned as RPC_X_NULL_REF_POINTER.
-        dwErrorCode = ERROR_INVALID_PARAMETER;
-        goto Cleanup;
-    }
-
     // Verify pEnvironment and open its registry key.
     dwErrorCode = _OpenEnvironment(pEnvironment, &hKey);
     if (dwErrorCode != ERROR_SUCCESS)
index 5050d60..0684b3b 100644 (file)
 #include "../localspl_apitest.h"
 #include <spoolss.h>
 
+typedef BOOL (WINAPI *PGetPrintProcessorDirectoryW)(LPWSTR, LPWSTR, DWORD, LPBYTE, DWORD, LPDWORD);
 extern BOOL GetLocalsplFuncs(LPPRINTPROVIDOR pp);
+extern PVOID GetSpoolssFunc(const char* FunctionName);
 
 START_TEST(fpGetPrintProcessorDirectory)
 {
     DWORD cbNeeded;
     DWORD cbTemp;
     DWORD dwReturned;
+    PGetPrintProcessorDirectoryW pGetPrintProcessorDirectoryW;
     PRINTPROVIDOR pp;
     PWSTR pwszBuffer;
 
     if (!GetLocalsplFuncs(&pp))
         return;
 
+    pGetPrintProcessorDirectoryW = GetSpoolssFunc("GetPrintProcessorDirectoryW");
+    if (!pGetPrintProcessorDirectoryW)
+        return;
+
     // In contrast to GetPrintProcessorDirectoryW, fpGetPrintProcessorDirectory needs an environment and doesn't just accept NULL.
     SetLastError(0xDEADBEEF);
     ok(!pp.fpGetPrintProcessorDirectory(NULL, NULL, 0, NULL, 0, NULL), "fpGetPrintProcessorDirectory returns TRUE!\n");
@@ -80,6 +87,14 @@ START_TEST(fpGetPrintProcessorDirectory)
 
     ok(dwReturned == EXCEPTION_ACCESS_VIOLATION, "dwReturned is %lu!\n", dwReturned);
 
+    // Prove that this check is implemented in spoolss' GetPrintProcessorDirectoryW instead.
+    // In contrast to winspool's GetPrintProcessorDirectoryW, cbTemp is left untouched though. This comes from the fact that RPC isn't involved here.
+    SetLastError(0xDEADBEEF);
+    cbTemp = 0xDEADBEEF;
+    ok(!pGetPrintProcessorDirectoryW(NULL, L"wIndows nt x86", 1, NULL, cbNeeded, &cbTemp), "pGetPrintProcessorDirectoryW returns TRUE!\n");
+    ok(GetLastError() == ERROR_INVALID_USER_BUFFER, "pGetPrintProcessorDirectoryW returns error %lu!\n", GetLastError());
+    ok(cbTemp == 0xDEADBEEF, "cbTemp is %lu!\n", cbTemp);
+
     // Finally use the function as intended and aim for success!
     // We only check success by the boolean return value though. GetLastError doesn't return anything meaningful here.
     pwszBuffer = DllAllocSplMem(cbNeeded);
index fb2885b..6fe5a97 100644 (file)
@@ -67,6 +67,22 @@ GetLocalsplFuncs(LPPRINTPROVIDOR pp)
     return TRUE;
 }
 
+PVOID
+GetSpoolssFunc(const char* FunctionName)
+{
+    HMODULE hSpoolss;
+
+    // Get us a handle to the loaded spoolss.dll.
+    hSpoolss = GetModuleHandleW(L"spoolss");
+    if (!hSpoolss)
+    {
+        skip("GetModuleHandleW failed with error %u!\n", GetLastError());
+        return FALSE;
+    }
+
+    return GetProcAddress(hSpoolss, FunctionName);
+}
+
 // Running the tests from the injected DLL and redirecting their output to the pipe.
 BOOL WINAPI
 DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
index 569ed8e..7412b5d 100644 (file)
@@ -21,8 +21,10 @@ START_TEST(GetPrintProcessorDirectoryA)
 
     // Try with an invalid level, this needs to be caught first.
     SetLastError(0xDEADBEEF);
-    ok(!GetPrintProcessorDirectoryA(NULL, NULL, 0, NULL, 0, NULL), "GetPrintProcessorDirectoryA returns TRUE!\n");
+    cbNeeded = 0xDEADBEEF;
+    ok(!GetPrintProcessorDirectoryA(NULL, NULL, 0, NULL, 0, &cbNeeded), "GetPrintProcessorDirectoryA returns TRUE!\n");
     ok(GetLastError() == ERROR_INVALID_LEVEL, "GetPrintProcessorDirectoryA returns error %lu!\n", GetLastError());
+    ok(cbNeeded == 0xDEADBEEF, "cbNeeded is %lu!\n", cbNeeded);
 
     // Now try with valid level, but no pcbNeeded.
     SetLastError(0xDEADBEEF);
@@ -77,8 +79,10 @@ START_TEST(GetPrintProcessorDirectoryW)
 
     // Try with an invalid level, this needs to be caught first.
     SetLastError(0xDEADBEEF);
-    ok(!GetPrintProcessorDirectoryW(NULL, NULL, 0, NULL, 0, NULL), "GetPrintProcessorDirectoryW returns TRUE!\n");
+    cbNeeded = 0xDEADBEEF;
+    ok(!GetPrintProcessorDirectoryW(NULL, NULL, 0, NULL, 0, &cbNeeded), "GetPrintProcessorDirectoryW returns TRUE!\n");
     ok(GetLastError() == ERROR_INVALID_LEVEL, "GetPrintProcessorDirectoryW returns error %lu!\n", GetLastError());
+    ok(cbNeeded == 0xDEADBEEF, "cbNeeded is %lu!\n", cbNeeded);
 
     // Now try with valid level, but no pcbNeeded.
     SetLastError(0xDEADBEEF);