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