1 /*
2 * PROJECT: ReactOS Local Spooler
3 * LICENSE: GPL-2.0+ (https://spdx.org/licenses/GPL-2.0+)
4 * PURPOSE: Functions related to Printer Configuration Data
5 * COPYRIGHT: Copyright 2017 Colin Finck (colin@reactos.org)
6 */
7
8 #include "precomp.h"
9
10 DWORD WINAPI
LocalGetPrinterData(HANDLE hPrinter,PWSTR pValueName,PDWORD pType,PBYTE pData,DWORD nSize,PDWORD pcbNeeded)11 LocalGetPrinterData(HANDLE hPrinter, PWSTR pValueName, PDWORD pType, PBYTE pData, DWORD nSize, PDWORD pcbNeeded)
12 {
13 TRACE("LocalGetPrinterData(%p, %S, %p, %p, %lu, %p)\n", hPrinter, pValueName, pType, pData, nSize, pcbNeeded);
14
15 // The ReactOS Printing Stack forwards all GetPrinterData calls to GetPrinterDataEx as soon as possible.
16 // This function may only be called if localspl.dll is used together with Windows Printing Stack components.
17 WARN("This function should never be called!\n");
18 return LocalGetPrinterDataEx(hPrinter, L"PrinterDriverData", pValueName, pType, pData, nSize, pcbNeeded);
19 }
20
21 static DWORD
_MakePrinterSubKey(PLOCAL_PRINTER_HANDLE pPrinterHandle,PCWSTR pKeyName,PWSTR * ppwszSubKey)22 _MakePrinterSubKey(PLOCAL_PRINTER_HANDLE pPrinterHandle, PCWSTR pKeyName, PWSTR* ppwszSubKey)
23 {
24 const WCHAR wszBackslash[] = L"\\";
25
26 size_t cbSubKey;
27 PWSTR p;
28
29 // Sanity check
30 if (!pKeyName || !*pKeyName)
31 return ERROR_INVALID_PARAMETER;
32
33 // Allocate a buffer for the subkey "PrinterName\KeyName".
34 cbSubKey = (wcslen(pPrinterHandle->pPrinter->pwszPrinterName) + 1 + wcslen(pKeyName) + 1) * sizeof(WCHAR);
35 *ppwszSubKey = DllAllocSplMem(cbSubKey);
36 if (!*ppwszSubKey)
37 return ERROR_NOT_ENOUGH_MEMORY;
38
39 // Concatenate the subkey.
40 p = *ppwszSubKey;
41 StringCbCopyExW(p, cbSubKey, pPrinterHandle->pPrinter->pwszPrinterName, &p, &cbSubKey, 0);
42 StringCbCopyExW(p, cbSubKey, wszBackslash, &p, &cbSubKey, 0);
43 StringCbCopyExW(p, cbSubKey, pKeyName, &p, &cbSubKey, 0);
44
45 return ERROR_SUCCESS;
46 }
47
48 static DWORD
_LocalGetPrinterHandleData(PLOCAL_PRINTER_HANDLE pPrinterHandle,PCWSTR pKeyName,PCWSTR pValueName,PDWORD pType,PBYTE pData,DWORD nSize,PDWORD pcbNeeded)49 _LocalGetPrinterHandleData(PLOCAL_PRINTER_HANDLE pPrinterHandle, PCWSTR pKeyName, PCWSTR pValueName, PDWORD pType, PBYTE pData, DWORD nSize, PDWORD pcbNeeded)
50 {
51 DWORD dwErrorCode;
52 HKEY hKey = NULL;
53 PWSTR pwszSubKey = NULL;
54
55 dwErrorCode = _MakePrinterSubKey(pPrinterHandle, pKeyName, &pwszSubKey);
56 if (dwErrorCode != ERROR_SUCCESS)
57 goto Cleanup;
58
59 // Open the subkey.
60 dwErrorCode = (DWORD)RegOpenKeyExW(hPrintersKey, pwszSubKey, 0, KEY_READ, &hKey);
61 if (dwErrorCode != ERROR_SUCCESS)
62 {
63 ERR("RegOpenKeyExW failed for \"%S\" with error %lu!\n", pwszSubKey, dwErrorCode);
64 goto Cleanup;
65 }
66
67 // Query the desired value.
68 *pcbNeeded = nSize;
69 dwErrorCode = (DWORD)RegQueryValueExW(hKey, pValueName, NULL, pType, pData, pcbNeeded);
70
71 Cleanup:
72 if (hKey)
73 RegCloseKey(hKey);
74
75 if (pwszSubKey)
76 DllFreeSplMem(pwszSubKey);
77
78 return dwErrorCode;
79 }
80
81 static DWORD
_LocalGetPrintServerHandleData(PCWSTR pValueName,PDWORD pType,PBYTE pData,DWORD nSize,PDWORD pcbNeeded)82 _LocalGetPrintServerHandleData(PCWSTR pValueName, PDWORD pType, PBYTE pData, DWORD nSize, PDWORD pcbNeeded)
83 {
84 DWORD dwErrorCode;
85
86 if (_wcsicmp(pValueName, SPLREG_DEFAULT_SPOOL_DIRECTORY) == 0 ||
87 _wcsicmp(pValueName, SPLREG_PORT_THREAD_PRIORITY) == 0 ||
88 _wcsicmp(pValueName, SPLREG_SCHEDULER_THREAD_PRIORITY) == 0 ||
89 _wcsicmp(pValueName, SPLREG_BEEP_ENABLED) == 0 ||
90 _wcsicmp(pValueName, SPLREG_ALLOW_USER_MANAGEFORMS) == 0)
91 {
92 *pcbNeeded = nSize;
93 return (DWORD)RegQueryValueExW(hPrintersKey, pValueName, NULL, pType, pData, pcbNeeded);
94 }
95 else if (_wcsicmp(pValueName, SPLREG_PORT_THREAD_PRIORITY_DEFAULT) == 0 ||
96 _wcsicmp(pValueName, SPLREG_SCHEDULER_THREAD_PRIORITY_DEFAULT) == 0)
97 {
98 // Store a DWORD value as REG_NONE.
99 *pType = REG_NONE;
100 *pcbNeeded = sizeof(DWORD);
101 if (nSize < *pcbNeeded)
102 return ERROR_MORE_DATA;
103
104 // Apparently, these values don't serve a purpose anymore.
105 *((PDWORD)pData) = 0;
106 return ERROR_SUCCESS;
107 }
108 else if (_wcsicmp(pValueName, SPLREG_NET_POPUP) == 0 ||
109 _wcsicmp(pValueName, SPLREG_RETRY_POPUP) == 0 ||
110 _wcsicmp(pValueName, SPLREG_NET_POPUP_TO_COMPUTER) == 0 ||
111 _wcsicmp(pValueName, SPLREG_EVENT_LOG) == 0 ||
112 _wcsicmp(pValueName, SPLREG_RESTART_JOB_ON_POOL_ERROR) == 0 ||
113 _wcsicmp(pValueName, SPLREG_RESTART_JOB_ON_POOL_ENABLED) == 0)
114 {
115 HKEY hKey;
116
117 dwErrorCode = (DWORD)RegOpenKeyExW(hPrintKey, L"Providers", 0, KEY_READ, &hKey);
118 if (dwErrorCode != ERROR_SUCCESS)
119 {
120 ERR("RegOpenKeyExW failed for \"Providers\" with error %lu!\n", dwErrorCode);
121 return dwErrorCode;
122 }
123
124 *pcbNeeded = nSize;
125 dwErrorCode = (DWORD)RegQueryValueExW(hKey, pValueName, NULL, pType, pData, pcbNeeded);
126 RegCloseKey(hKey);
127 return dwErrorCode;
128 }
129 else if (_wcsicmp(pValueName, SPLREG_MAJOR_VERSION) == 0)
130 {
131 // Store a DWORD value as REG_NONE.
132 *pType = REG_NONE;
133 *pcbNeeded = sizeof(DWORD);
134 if (nSize < *pcbNeeded)
135 return ERROR_MORE_DATA;
136
137 // Apparently, these values don't serve a purpose anymore.
138 *((PDWORD)pData) = dwSpoolerMajorVersion;
139 return ERROR_SUCCESS;
140 }
141 else if (_wcsicmp(pValueName, SPLREG_MINOR_VERSION) == 0)
142 {
143 // Store a DWORD value as REG_NONE.
144 *pType = REG_NONE;
145 *pcbNeeded = sizeof(DWORD);
146 if (nSize < *pcbNeeded)
147 return ERROR_MORE_DATA;
148
149 // Apparently, these values don't serve a purpose anymore.
150 *((PDWORD)pData) = dwSpoolerMinorVersion;
151 return ERROR_SUCCESS;
152 }
153 else if (_wcsicmp(pValueName, SPLREG_ARCHITECTURE) == 0)
154 {
155 // Store a string as REG_NONE with the length of the environment name string.
156 *pType = REG_NONE;
157 *pcbNeeded = cbCurrentEnvironment;
158 if (nSize < *pcbNeeded)
159 return ERROR_MORE_DATA;
160
161 // Copy the environment name as the output value for SPLREG_ARCHITECTURE.
162 CopyMemory(pData, wszCurrentEnvironment, cbCurrentEnvironment);
163 return ERROR_SUCCESS;
164 }
165 else if (_wcsicmp(pValueName, SPLREG_OS_VERSION) == 0)
166 {
167 POSVERSIONINFOW pInfo = (POSVERSIONINFOW)pData;
168
169 // Store the OSVERSIONINFOW structure as REG_NONE.
170 *pType = REG_NONE;
171 *pcbNeeded = sizeof(OSVERSIONINFOW);
172 if (nSize < *pcbNeeded)
173 return ERROR_MORE_DATA;
174
175 // Return OS version information.
176 pInfo->dwOSVersionInfoSize = sizeof(OSVERSIONINFOW);
177 GetVersionExW(pInfo);
178 return ERROR_SUCCESS;
179 }
180 else if (_wcsicmp(pValueName, SPLREG_OS_VERSIONEX) == 0)
181 {
182 POSVERSIONINFOEXW pInfo = (POSVERSIONINFOEXW)pData;
183
184 // Store the OSVERSIONINFOEXW structure as REG_NONE.
185 *pType = REG_NONE;
186 *pcbNeeded = sizeof(OSVERSIONINFOEXW);
187 if (nSize < *pcbNeeded)
188 return ERROR_MORE_DATA;
189
190 // Return extended OS version information.
191 pInfo->dwOSVersionInfoSize = sizeof(OSVERSIONINFOEXW);
192 GetVersionExW((POSVERSIONINFOW)pInfo);
193 return ERROR_SUCCESS;
194 }
195 else if (_wcsicmp(pValueName, SPLREG_DS_PRESENT) == 0)
196 {
197 PDSROLE_PRIMARY_DOMAIN_INFO_BASIC pInfo;
198
199 // We want to store a REG_DWORD value.
200 *pType = REG_DWORD;
201 *pcbNeeded = sizeof(DWORD);
202 if (nSize < *pcbNeeded)
203 return ERROR_MORE_DATA;
204
205 // Get information about the domain membership of this computer.
206 dwErrorCode = DsRoleGetPrimaryDomainInformation(NULL, DsRolePrimaryDomainInfoBasic, (PBYTE*)&pInfo);
207 if (dwErrorCode != ERROR_SUCCESS)
208 {
209 ERR("DsRoleGetPrimaryDomainInformation failed with error %lu!\n", dwErrorCode);
210 return dwErrorCode;
211 }
212
213 // Return whether this computer is a workstation or server inside a domain.
214 *((PDWORD)pData) = (pInfo->MachineRole == DsRole_RoleMemberWorkstation || pInfo->MachineRole == DsRole_RoleMemberServer);
215 DsRoleFreeMemory(pInfo);
216 return ERROR_SUCCESS;
217 }
218 else if (_wcsicmp(pValueName, SPLREG_DS_PRESENT_FOR_USER) == 0)
219 {
220 DWORD cch;
221 PWSTR p;
222 WCHAR wszComputerName[MAX_COMPUTERNAME_LENGTH + 1];
223 WCHAR wszUserSam[UNLEN + 1];
224
225 // We want to store a REG_DWORD value.
226 *pType = REG_DWORD;
227 *pcbNeeded = sizeof(DWORD);
228 if (nSize < *pcbNeeded)
229 return ERROR_MORE_DATA;
230
231 // Get the local Computer Name.
232 cch = MAX_COMPUTERNAME_LENGTH + 1;
233 if (!GetComputerNameW(wszComputerName, &cch))
234 {
235 dwErrorCode = GetLastError();
236 ERR("GetComputerNameW failed with error %lu!\n", dwErrorCode);
237 return dwErrorCode;
238 }
239
240 // Get the User Name in the SAM format.
241 // This could either be:
242 // COMPUTERNAME\User
243 // DOMAINNAME\User
244 cch = UNLEN + 1;
245 if (!GetUserNameExW(NameSamCompatible, wszUserSam, &cch))
246 {
247 dwErrorCode = GetLastError();
248 ERR("GetUserNameExW failed with error %lu!\n", dwErrorCode);
249 return dwErrorCode;
250 }
251
252 // Terminate the SAM-formatted User Name at the backslash.
253 p = wcschr(wszUserSam, L'\\');
254 *p = 0;
255
256 // Compare it with the Computer Name.
257 // If they differ, this User is part of a domain.
258 *((PDWORD)pData) = (wcscmp(wszUserSam, wszComputerName) != 0);
259 return ERROR_SUCCESS;
260 }
261 else if (_wcsicmp(pValueName, SPLREG_REMOTE_FAX) == 0)
262 {
263 // Store a DWORD value as REG_NONE.
264 *pType = REG_NONE;
265 *pcbNeeded = sizeof(DWORD);
266 if (nSize < *pcbNeeded)
267 return ERROR_MORE_DATA;
268
269 // TODO: We don't support any fax service yet, but let's return the same value as Windows Server 2003 here.
270 *((PDWORD)pData) = 1;
271 return ERROR_SUCCESS;
272 }
273 else if (_wcsicmp(pValueName, SPLREG_DNS_MACHINE_NAME) == 0)
274 {
275 DWORD cchDnsName = 0;
276
277 // Get the length of the fully-qualified computer DNS name.
278 GetComputerNameExW(ComputerNameDnsFullyQualified, NULL, &cchDnsName);
279 dwErrorCode = GetLastError();
280 if (dwErrorCode != ERROR_MORE_DATA)
281 {
282 ERR("GetComputerNameExW failed with error %lu!\n", dwErrorCode);
283 return dwErrorCode;
284 }
285
286 // Check if our supplied buffer is large enough.
287 *pType = REG_SZ;
288 *pcbNeeded = cchDnsName * sizeof(WCHAR);
289 if (nSize < *pcbNeeded)
290 return ERROR_MORE_DATA;
291
292 // Get the actual DNS name.
293 if (!GetComputerNameExW(ComputerNameDnsFullyQualified, (PWSTR)pData, &cchDnsName))
294 {
295 dwErrorCode = GetLastError();
296 ERR("GetComputerNameExW failed with error %lu!\n", dwErrorCode);
297 return dwErrorCode;
298 }
299
300 // Lowercase the output just like Windows does.
301 _wcslwr((PWSTR)pData);
302 return ERROR_SUCCESS;
303 }
304 else
305 {
306 // For all other, unknown settings, we just return ERROR_INVALID_PARAMETER.
307 // That also includes SPLREG_WEBSHAREMGMT, which is supported in Windows Server 2003 according to the documentation,
308 // but is actually not!
309 return ERROR_INVALID_PARAMETER;
310 }
311 }
312
313 DWORD WINAPI
LocalGetPrinterDataEx(HANDLE hPrinter,PCWSTR pKeyName,PCWSTR pValueName,PDWORD pType,PBYTE pData,DWORD nSize,PDWORD pcbNeeded)314 LocalGetPrinterDataEx(HANDLE hPrinter, PCWSTR pKeyName, PCWSTR pValueName, PDWORD pType, PBYTE pData, DWORD nSize, PDWORD pcbNeeded)
315 {
316 BYTE Temp;
317 DWORD dwErrorCode;
318 DWORD dwTemp;
319 PLOCAL_HANDLE pHandle = (PLOCAL_HANDLE)hPrinter;
320
321 TRACE("LocalGetPrinterDataEx(%p, %S, %S, %p, %p, %lu, %p)\n", hPrinter, pKeyName, pValueName, pType, pData, nSize, pcbNeeded);
322
323 // Even if GetPrinterDataExW in winspool ensures that the RPC function is never called without a valid pointer for pType,
324 // it's officially optional. Windows' fpGetPrinterDataEx also works with NULL for pType!
325 // Ensure here that it is always set to simplify the code later.
326 if (!pType)
327 pType = &dwTemp;
328
329 // pData is later fed to RegQueryValueExW in many cases. When calling it with zero buffer size, RegQueryValueExW returns a
330 // different error code based on whether pData is NULL or something else.
331 // Ensure here that ERROR_MORE_DATA is always returned.
332 if (!pData)
333 pData = &Temp;
334
335 if (!pHandle)
336 {
337 dwErrorCode = ERROR_INVALID_HANDLE;
338 }
339 else if (!pcbNeeded)
340 {
341 dwErrorCode = ERROR_INVALID_PARAMETER;
342 }
343 else if (pHandle->HandleType == HandleType_Printer)
344 {
345 dwErrorCode = _LocalGetPrinterHandleData(pHandle->pSpecificHandle, pKeyName, pValueName, pType, pData, nSize, pcbNeeded);
346 }
347 else if (pHandle->HandleType == HandleType_PrintServer)
348 {
349 dwErrorCode = _LocalGetPrintServerHandleData(pValueName, pType, pData, nSize, pcbNeeded);
350 }
351 else
352 {
353 dwErrorCode = ERROR_INVALID_HANDLE;
354 }
355
356 SetLastError(dwErrorCode);
357 return dwErrorCode;
358 }
359
360 DWORD WINAPI
LocalSetPrinterData(HANDLE hPrinter,PWSTR pValueName,DWORD Type,PBYTE pData,DWORD cbData)361 LocalSetPrinterData(HANDLE hPrinter, PWSTR pValueName, DWORD Type, PBYTE pData, DWORD cbData)
362 {
363 TRACE("LocalSetPrinterData(%p, %S, %lu, %p, %lu)\n", hPrinter, pValueName, Type, pData, cbData);
364
365 // The ReactOS Printing Stack forwards all SetPrinterData calls to SetPrinterDataEx as soon as possible.
366 // This function may only be called if localspl.dll is used together with Windows Printing Stack components.
367 WARN("This function should never be called!\n");
368 return LocalSetPrinterDataEx(hPrinter, L"PrinterDriverData", pValueName, Type, pData, cbData);
369 }
370
371 static DWORD
_LocalSetPrinterHandleData(PLOCAL_PRINTER_HANDLE pPrinterHandle,PCWSTR pKeyName,PCWSTR pValueName,DWORD Type,PBYTE pData,DWORD cbData)372 _LocalSetPrinterHandleData(PLOCAL_PRINTER_HANDLE pPrinterHandle, PCWSTR pKeyName, PCWSTR pValueName, DWORD Type, PBYTE pData, DWORD cbData)
373 {
374 DWORD dwErrorCode;
375 HKEY hKey = NULL;
376 PWSTR pwszSubKey = NULL;
377
378 dwErrorCode = _MakePrinterSubKey(pPrinterHandle, pKeyName, &pwszSubKey);
379 if (dwErrorCode != ERROR_SUCCESS)
380 goto Cleanup;
381
382 // Open the subkey.
383 dwErrorCode = (DWORD)RegOpenKeyExW(hPrintersKey, pwszSubKey, 0, KEY_SET_VALUE, &hKey);
384 if (dwErrorCode != ERROR_SUCCESS)
385 {
386 ERR("RegOpenKeyExW failed for \"%S\" with error %lu!\n", pwszSubKey, dwErrorCode);
387 goto Cleanup;
388 }
389
390 // Set the value.
391 dwErrorCode = (DWORD)RegSetValueExW(hKey, pValueName, 0, Type, pData, cbData);
392
393 Cleanup:
394 if (hKey)
395 RegCloseKey(hKey);
396
397 if (pwszSubKey)
398 DllFreeSplMem(pwszSubKey);
399
400 return dwErrorCode;
401 }
402
403 static DWORD
_LocalSetPrintServerHandleData(PCWSTR pValueName,DWORD Type,PBYTE pData,DWORD cbData)404 _LocalSetPrintServerHandleData(PCWSTR pValueName, DWORD Type, PBYTE pData, DWORD cbData)
405 {
406 DWORD dwErrorCode;
407
408 if (_wcsicmp(pValueName, SPLREG_DEFAULT_SPOOL_DIRECTORY) == 0 ||
409 _wcsicmp(pValueName, SPLREG_PORT_THREAD_PRIORITY) == 0 ||
410 _wcsicmp(pValueName, SPLREG_SCHEDULER_THREAD_PRIORITY) == 0 ||
411 _wcsicmp(pValueName, SPLREG_BEEP_ENABLED) == 0 ||
412 _wcsicmp(pValueName, SPLREG_ALLOW_USER_MANAGEFORMS) == 0)
413 {
414 return (DWORD)RegSetValueExW(hPrintersKey, pValueName, 0, Type, pData, cbData);
415 }
416 else if (_wcsicmp(pValueName, SPLREG_NET_POPUP) == 0 ||
417 _wcsicmp(pValueName, SPLREG_RETRY_POPUP) == 0 ||
418 _wcsicmp(pValueName, SPLREG_NET_POPUP_TO_COMPUTER) == 0 ||
419 _wcsicmp(pValueName, SPLREG_EVENT_LOG) == 0 ||
420 _wcsicmp(pValueName, SPLREG_RESTART_JOB_ON_POOL_ERROR) == 0 ||
421 _wcsicmp(pValueName, SPLREG_RESTART_JOB_ON_POOL_ENABLED) == 0 ||
422 _wcsicmp(pValueName, L"NoRemotePrinterDrivers") == 0)
423 {
424 HKEY hKey;
425
426 dwErrorCode = (DWORD)RegOpenKeyExW(hPrintKey, L"Providers", 0, KEY_SET_VALUE, &hKey);
427 if (dwErrorCode != ERROR_SUCCESS)
428 {
429 ERR("RegOpenKeyExW failed for \"Providers\" with error %lu!\n", dwErrorCode);
430 return dwErrorCode;
431 }
432
433 dwErrorCode = (DWORD)RegSetValueExW(hKey, pValueName, 0, Type, pData, cbData);
434 RegCloseKey(hKey);
435 return dwErrorCode;
436 }
437 else if (_wcsicmp(pValueName, SPLREG_WEBSHAREMGMT) == 0)
438 {
439 WARN("Attempting to set WebShareMgmt, which is based on IIS and therefore not supported. Returning fake success!\n");
440 return ERROR_SUCCESS;
441 }
442 else
443 {
444 // For all other, unknown settings, we just return ERROR_INVALID_PARAMETER.
445 return ERROR_INVALID_PARAMETER;
446 }
447 }
448
449 DWORD WINAPI
LocalSetPrinterDataEx(HANDLE hPrinter,PCWSTR pKeyName,PCWSTR pValueName,DWORD Type,PBYTE pData,DWORD cbData)450 LocalSetPrinterDataEx(HANDLE hPrinter, PCWSTR pKeyName, PCWSTR pValueName, DWORD Type, PBYTE pData, DWORD cbData)
451 {
452 DWORD dwErrorCode;
453 PLOCAL_HANDLE pHandle = (PLOCAL_HANDLE)hPrinter;
454
455 TRACE("LocalSetPrinterDataEx(%p, %S, %S, %lu, %p, %lu)\n", hPrinter, pKeyName, pValueName, Type, pData, cbData);
456
457 if (!pHandle)
458 {
459 dwErrorCode = ERROR_INVALID_HANDLE;
460 }
461 else if (pHandle->HandleType == HandleType_Printer)
462 {
463 dwErrorCode = _LocalSetPrinterHandleData(pHandle->pSpecificHandle, pKeyName, pValueName, Type, pData, cbData);
464 }
465 else if (pHandle->HandleType == HandleType_PrintServer)
466 {
467 dwErrorCode = _LocalSetPrintServerHandleData(pValueName, Type, pData, cbData);
468 }
469 else
470 {
471 dwErrorCode = ERROR_INVALID_HANDLE;
472 }
473
474 SetLastError(dwErrorCode);
475 return dwErrorCode;
476 }
477