1 /*
2  * PROJECT:     ReactOS Spooler API
3  * LICENSE:     GPL-2.0+ (https://spdx.org/licenses/GPL-2.0+)
4  * PURPOSE:     Functions related to Printer Configuration Data
5  * COPYRIGHT:   Copyright 2015-2017 Colin Finck (colin@reactos.org)
6  */
7 
8 #include "precomp.h"
9 
10 LONG WINAPI
11 AdvancedDocumentPropertiesW(HWND hWnd, HANDLE hPrinter, PWSTR pDeviceName, PDEVMODEW pDevModeOutput, PDEVMODEW pDevModeInput)
12 {
13     UNIMPLEMENTED;
14     return FALSE;
15 }
16 
17 DWORD WINAPI
18 GetPrinterDataA(HANDLE hPrinter, LPSTR pValueName, LPDWORD pType, LPBYTE pData, DWORD nSize, LPDWORD pcbNeeded)
19 {
20     return GetPrinterDataExA(hPrinter, "PrinterDriverData", pValueName, pType, pData, nSize, pcbNeeded);
21 }
22 
23 DWORD WINAPI
24 GetPrinterDataExA(HANDLE hPrinter, LPCSTR pKeyName, LPCSTR pValueName, LPDWORD pType, LPBYTE pData, DWORD nSize, LPDWORD pcbNeeded)
25 {
26     DWORD cbUnicodeData;
27     DWORD cch;
28     DWORD dwReturnValue;
29     DWORD dwType;
30     POSVERSIONINFOEXA pInfoA;
31     POSVERSIONINFOEXW pInfoW;
32     PVOID pUnicodeData = NULL;
33     PWSTR pwszKeyName = NULL;
34     PWSTR pwszValueName = NULL;
35 
36     if (pKeyName)
37     {
38         // Convert pKeyName to a Unicode string pwszKeyName
39         cch = strlen(pKeyName);
40 
41         pwszKeyName = HeapAlloc(hProcessHeap, 0, (cch + 1) * sizeof(WCHAR));
42         if (!pwszKeyName)
43         {
44             dwReturnValue = ERROR_NOT_ENOUGH_MEMORY;
45             ERR("HeapAlloc failed!\n");
46             goto Cleanup;
47         }
48 
49         MultiByteToWideChar(CP_ACP, 0, pKeyName, -1, pwszKeyName, cch + 1);
50     }
51 
52     if (pValueName)
53     {
54         // Convert pValueName to a Unicode string pwszValueName
55         cch = strlen(pValueName);
56 
57         pwszValueName = HeapAlloc(hProcessHeap, 0, (cch + 1) * sizeof(WCHAR));
58         if (!pwszValueName)
59         {
60             dwReturnValue = ERROR_NOT_ENOUGH_MEMORY;
61             ERR("HeapAlloc failed!\n");
62             goto Cleanup;
63         }
64 
65         MultiByteToWideChar(CP_ACP, 0, pValueName, -1, pwszValueName, cch + 1);
66     }
67 
68     // We need the data type information, even if no pData was passed.
69     if (!pType)
70         pType = &dwType;
71 
72     // Call GetPrinterDataExW for the first time.
73     // If we're lucky, the supplied buffer is already large enough and we don't need to do the expensive RPC call a second time.
74     dwReturnValue = GetPrinterDataExW(hPrinter, pwszKeyName, pwszValueName, pType, pData, nSize, pcbNeeded);
75 
76     // If a critical error occurred, just return it. We cannot do anything else in this case.
77     if (dwReturnValue != ERROR_SUCCESS && dwReturnValue != ERROR_MORE_DATA)
78         goto Cleanup;
79 
80     // Save the needed buffer size for the Unicode data. We may alter *pcbNeeded for an ANSI buffer size.
81     cbUnicodeData = *pcbNeeded;
82 
83     if (*pType == REG_SZ || *pType == REG_MULTI_SZ || *pType == REG_EXPAND_SZ)
84     {
85         // This is a string that needs to be converted from Unicode to ANSI.
86         // Output the required buffer size for the ANSI string.
87         *pcbNeeded /= sizeof(WCHAR);
88     }
89     else if (*pType == REG_NONE)
90     {
91         if (cbUnicodeData == sizeof(OSVERSIONINFOW) && wcsicmp(pwszValueName, SPLREG_OS_VERSION) == 0)
92         {
93             // This is a Unicode OSVERSIONINFOW structure that needs to be converted to an ANSI OSVERSIONINFOA.
94             *pcbNeeded = sizeof(OSVERSIONINFOA);
95         }
96         else if (cbUnicodeData == sizeof(OSVERSIONINFOEXW) && wcsicmp(pwszValueName, SPLREG_OS_VERSIONEX) == 0)
97         {
98             // This is a Unicode OSVERSIONINFOEXW structure that needs to be converted to an ANSI OSVERSIONINFOEXA.
99             *pcbNeeded = sizeof(OSVERSIONINFOEXA);
100         }
101         else
102         {
103             // Other REG_NONE value, nothing to do.
104             goto Cleanup;
105         }
106     }
107 
108     // Check if the supplied buffer is large enough for the ANSI data.
109     if (nSize < *pcbNeeded)
110     {
111         dwReturnValue = ERROR_MORE_DATA;
112         goto Cleanup;
113     }
114 
115     // Allocate a temporary buffer for the Unicode data.
116     pUnicodeData = HeapAlloc(hProcessHeap, 0, cbUnicodeData);
117     if (!pUnicodeData)
118     {
119         dwReturnValue = ERROR_NOT_ENOUGH_MEMORY;
120         ERR("HeapAlloc failed!\n");
121         goto Cleanup;
122     }
123 
124     if (dwReturnValue == ERROR_SUCCESS)
125     {
126         // ERROR_SUCCESS: The buffer is large enough for the ANSI and the Unicode string,
127         // so the Unicode string has been copied into pData. Copy it to pUnicodeData.
128         CopyMemory(pUnicodeData, pData, cbUnicodeData);
129     }
130     else
131     {
132         // ERROR_MORE_DATA: The buffer is large enough for the ANSI string, but not for the Unicode string.
133         // We have to call GetPrinterDataExW again with the temporary buffer.
134         dwReturnValue = GetPrinterDataExW(hPrinter, pwszKeyName, pwszValueName, NULL, (PBYTE)pUnicodeData, cbUnicodeData, &cbUnicodeData);
135         if (dwReturnValue != ERROR_SUCCESS)
136             goto Cleanup;
137     }
138 
139     if (*pType == REG_SZ || *pType == REG_MULTI_SZ || *pType == REG_EXPAND_SZ)
140     {
141         // Convert the Unicode string to ANSI.
142         WideCharToMultiByte(CP_ACP, 0, (PWSTR)pUnicodeData, -1, (PSTR)pData, *pcbNeeded, NULL, NULL);
143     }
144     else
145     {
146         // This is a REG_NONE with either OSVERSIONINFOW or OSVERSIONINFOEXW.
147         // Copy the fields and convert the Unicode CSD Version string to ANSI.
148         pInfoW = (POSVERSIONINFOEXW)pUnicodeData;
149         pInfoA = (POSVERSIONINFOEXA)pData;
150         pInfoA->dwMajorVersion = pInfoW->dwMajorVersion;
151         pInfoA->dwMinorVersion = pInfoW->dwMinorVersion;
152         pInfoA->dwBuildNumber = pInfoW->dwBuildNumber;
153         pInfoA->dwPlatformId = pInfoW->dwPlatformId;
154         WideCharToMultiByte(CP_ACP, 0, pInfoW->szCSDVersion, -1, pInfoA->szCSDVersion, sizeof(pInfoA->szCSDVersion), NULL, NULL);
155 
156         if (cbUnicodeData == sizeof(OSVERSIONINFOW))
157         {
158             pInfoA->dwOSVersionInfoSize = sizeof(OSVERSIONINFOA);
159         }
160         else
161         {
162             pInfoA->dwOSVersionInfoSize = sizeof(OSVERSIONINFOEXA);
163             pInfoA->wServicePackMajor = pInfoW->wServicePackMajor;
164             pInfoA->wServicePackMinor = pInfoW->wServicePackMinor;
165             pInfoA->wSuiteMask = pInfoW->wSuiteMask;
166             pInfoA->wProductType = pInfoW->wProductType;
167             pInfoA->wReserved = pInfoW->wReserved;
168         }
169     }
170 
171 Cleanup:
172     if (pwszKeyName)
173         HeapFree(hProcessHeap, 0, pwszKeyName);
174 
175     if (pwszValueName)
176         HeapFree(hProcessHeap, 0, pwszValueName);
177 
178     if (pUnicodeData)
179         HeapFree(hProcessHeap, 0, pUnicodeData);
180 
181     return dwReturnValue;
182 }
183 
184 DWORD WINAPI
185 GetPrinterDataExW(HANDLE hPrinter, LPCWSTR pKeyName, LPCWSTR pValueName, LPDWORD pType, LPBYTE pData, DWORD nSize, LPDWORD pcbNeeded)
186 {
187     const WCHAR wszEmptyString[] = L"";
188 
189     BYTE DummyData;
190     DWORD dwErrorCode;
191     DWORD dwType = REG_NONE;
192     PSPOOLER_HANDLE pHandle = (PSPOOLER_HANDLE)hPrinter;
193 
194     // Sanity checks
195     if (!pHandle)
196         return ERROR_INVALID_HANDLE;
197 
198     // Yes, instead of declaring these pointers unique in the IDL file (and perfectly accepting NULL pointers this way),
199     // Windows does it differently for GetPrinterDataExW and points them to empty variables.
200     if (!pKeyName)
201         pKeyName = wszEmptyString;
202 
203     if (!pType)
204         pType = &dwType;
205 
206     if (!pData && !nSize)
207         pData = &DummyData;
208 
209     // Do the RPC call
210     RpcTryExcept
211     {
212         dwErrorCode = _RpcGetPrinterDataEx(pHandle->hPrinter, pKeyName, pValueName, pType, pData, nSize, pcbNeeded);
213     }
214     RpcExcept(EXCEPTION_EXECUTE_HANDLER)
215     {
216         dwErrorCode = RpcExceptionCode();
217     }
218     RpcEndExcept;
219 
220     return dwErrorCode;
221 }
222 
223 DWORD WINAPI
224 GetPrinterDataW(HANDLE hPrinter, LPWSTR pValueName, LPDWORD pType, LPBYTE pData, DWORD nSize, LPDWORD pcbNeeded)
225 {
226     return GetPrinterDataExW(hPrinter, L"PrinterDriverData", pValueName, pType, pData, nSize, pcbNeeded);
227 }
228 
229 DWORD WINAPI
230 SetPrinterDataA(HANDLE hPrinter, PSTR pValueName, DWORD Type, PBYTE pData, DWORD cbData)
231 {
232     return SetPrinterDataExA(hPrinter, "PrinterDriverData", pValueName, Type, pData, cbData);
233 }
234 
235 DWORD WINAPI
236 SetPrinterDataExA(HANDLE hPrinter, LPCSTR pKeyName, LPCSTR pValueName, DWORD Type, LPBYTE pData, DWORD cbData)
237 {
238     DWORD cch;
239     DWORD dwReturnValue;
240     PWSTR pwszKeyName = NULL;
241     PWSTR pwszValueName = NULL;
242     PWSTR pUnicodeData = NULL;
243 
244     if (pKeyName)
245     {
246         // Convert pKeyName to a Unicode string pwszKeyName
247         cch = strlen(pKeyName);
248 
249         pwszKeyName = HeapAlloc(hProcessHeap, 0, (cch + 1) * sizeof(WCHAR));
250         if (!pwszKeyName)
251         {
252             dwReturnValue = ERROR_NOT_ENOUGH_MEMORY;
253             ERR("HeapAlloc failed!\n");
254             goto Cleanup;
255         }
256 
257         MultiByteToWideChar(CP_ACP, 0, pKeyName, -1, pwszKeyName, cch + 1);
258     }
259 
260     if (pValueName)
261     {
262         // Convert pValueName to a Unicode string pwszValueName
263         cch = strlen(pValueName);
264 
265         pwszValueName = HeapAlloc(hProcessHeap, 0, (cch + 1) * sizeof(WCHAR));
266         if (!pwszValueName)
267         {
268             dwReturnValue = ERROR_NOT_ENOUGH_MEMORY;
269             ERR("HeapAlloc failed!\n");
270             goto Cleanup;
271         }
272 
273         MultiByteToWideChar(CP_ACP, 0, pValueName, -1, pwszValueName, cch + 1);
274     }
275 
276     if (Type == REG_SZ || Type == REG_MULTI_SZ || Type == REG_EXPAND_SZ)
277     {
278         // Convert pData to a Unicode string pUnicodeData.
279         pUnicodeData = HeapAlloc(hProcessHeap, 0, cbData * sizeof(WCHAR));
280         if (!pUnicodeData)
281         {
282             dwReturnValue = ERROR_NOT_ENOUGH_MEMORY;
283             ERR("HeapAlloc failed!\n");
284             goto Cleanup;
285         }
286 
287         MultiByteToWideChar(CP_ACP, 0, (PCSTR)pData, -1, pUnicodeData, cbData);
288 
289         pData = (PBYTE)pUnicodeData;
290         cbData *= sizeof(WCHAR);
291     }
292 
293     dwReturnValue = SetPrinterDataExW(hPrinter, pwszKeyName, pwszValueName, Type, pData, cbData);
294 
295 Cleanup:
296     if (pwszKeyName)
297         HeapFree(hProcessHeap, 0, pwszKeyName);
298 
299     if (pwszValueName)
300         HeapFree(hProcessHeap, 0, pwszValueName);
301 
302     if (pUnicodeData)
303         HeapFree(hProcessHeap, 0, pUnicodeData);
304 
305     return dwReturnValue;
306 }
307 
308 DWORD WINAPI
309 SetPrinterDataExW(HANDLE hPrinter, LPCWSTR pKeyName, LPCWSTR pValueName, DWORD Type, LPBYTE pData, DWORD cbData)
310 {
311     const WCHAR wszEmptyString[] = L"";
312 
313     DWORD dwErrorCode;
314     PSPOOLER_HANDLE pHandle = (PSPOOLER_HANDLE)hPrinter;
315 
316     // Sanity checks
317     if (!pHandle)
318         return ERROR_INVALID_HANDLE;
319 
320     if (!pKeyName)
321         pKeyName = wszEmptyString;
322 
323     // Do the RPC call
324     RpcTryExcept
325     {
326         dwErrorCode = _RpcSetPrinterDataEx(pHandle->hPrinter, pKeyName, pValueName, Type, pData, cbData);
327     }
328     RpcExcept(EXCEPTION_EXECUTE_HANDLER)
329     {
330         dwErrorCode = RpcExceptionCode();
331     }
332     RpcEndExcept;
333 
334     return dwErrorCode;
335 }
336 
337 DWORD WINAPI
338 SetPrinterDataW(HANDLE hPrinter, PWSTR pValueName, DWORD Type, PBYTE pData, DWORD cbData)
339 {
340     return SetPrinterDataExW(hPrinter, L"PrinterDriverData", pValueName, Type, pData, cbData);
341 }
342