[PRINTING]
[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)
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", GetLastError());
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 pwszUserSam;
220 PWSTR p;
221 WCHAR wszComputerName[MAX_COMPUTERNAME_LENGTH + 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 ERR("GetComputerNameW failed with error %lu!\n", GetLastError());
234 return GetLastError();
235 }
236
237 // Get the User Name in the SAM format.
238 // This could either be:
239 // COMPUTERNAME\User
240 // DOMAINNAME\User
241 cch = 0;
242 GetUserNameExW(NameSamCompatible, NULL, &cch);
243 dwErrorCode = GetLastError();
244 if (dwErrorCode != ERROR_MORE_DATA)
245 {
246 ERR("GetUserNameExW failed with error %lu!\n", dwErrorCode);
247 return dwErrorCode;
248 }
249
250 pwszUserSam = DllAllocSplMem(cch * sizeof(WCHAR));
251 if (!pwszUserSam)
252 {
253 dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
254 ERR("DllAllocSplMem failed!\n");
255 return dwErrorCode;
256 }
257
258 if (!GetUserNameExW(NameSamCompatible, pwszUserSam, &cch))
259 {
260 dwErrorCode = GetLastError();
261 ERR("GetUserNameExW failed with error %lu!\n", dwErrorCode);
262 DllFreeSplMem(pwszUserSam);
263 return dwErrorCode;
264 }
265
266 // Terminate the SAM-formatted User Name at the backslash.
267 p = wcschr(pwszUserSam, L'\\');
268 *p = 0;
269
270 // Compare it with the Computer Name.
271 // If they differ, this User is part of a domain.
272 *((PDWORD)pData) = (wcscmp(pwszUserSam, wszComputerName) != 0);
273 DllFreeSplMem(pwszUserSam);
274 return ERROR_SUCCESS;
275 }
276 else if (wcsicmp(pValueName, SPLREG_REMOTE_FAX) == 0)
277 {
278 // Store a DWORD value as REG_NONE.
279 *pType = REG_NONE;
280 *pcbNeeded = sizeof(DWORD);
281 if (nSize < *pcbNeeded)
282 return ERROR_MORE_DATA;
283
284 // TODO: We don't support any fax service yet, but let's return the same value as Windows Server 2003 here.
285 *((PDWORD)pData) = 1;
286 return ERROR_SUCCESS;
287 }
288 else if (wcsicmp(pValueName, SPLREG_DNS_MACHINE_NAME) == 0)
289 {
290 DWORD cchDnsName = 0;
291
292 // Get the length of the fully-qualified computer DNS name.
293 GetComputerNameExW(ComputerNameDnsFullyQualified, NULL, &cchDnsName);
294 dwErrorCode = GetLastError();
295 if (dwErrorCode != ERROR_MORE_DATA)
296 {
297 ERR("GetComputerNameExW failed with error %lu!\n", dwErrorCode);
298 return dwErrorCode;
299 }
300
301 // Check if our supplied buffer is large enough.
302 *pType = REG_SZ;
303 *pcbNeeded = cchDnsName * sizeof(WCHAR);
304 if (nSize < *pcbNeeded)
305 return ERROR_MORE_DATA;
306
307 // Get the actual DNS name.
308 if (!GetComputerNameExW(ComputerNameDnsFullyQualified, (PWSTR)pData, &cchDnsName))
309 {
310 dwErrorCode = GetLastError();
311 ERR("GetComputerNameExW failed with error %lu!\n", dwErrorCode);
312 return dwErrorCode;
313 }
314
315 // Lowercase the output just like Windows does.
316 _wcslwr((PWSTR)pData);
317 return ERROR_SUCCESS;
318 }
319 else
320 {
321 // For all other, unknown settings, we just return ERROR_INVALID_PARAMETER.
322 // That also includes SPLREG_WEBSHAREMGMT, which is supported in Windows Server 2003 according to the documentation,
323 // but is actually not!
324 return ERROR_INVALID_PARAMETER;
325 }
326 }
327
328 DWORD WINAPI
329 LocalGetPrinterDataEx(HANDLE hPrinter, PCWSTR pKeyName, PCWSTR pValueName, PDWORD pType, PBYTE pData, DWORD nSize, PDWORD pcbNeeded)
330 {
331 DWORD dwErrorCode;
332 DWORD dwTemp;
333 PLOCAL_HANDLE pHandle = (PLOCAL_HANDLE)hPrinter;
334
335 // Even if GetPrinterDataExW in winspool ensures that the RPC function is never called without a valid pointer for pType,
336 // it's officially optional. Windows' fpGetPrinterDataEx also works with NULL for pType!
337 // Ensure here that it is always set to simplify the code later.
338 if (!pType)
339 pType = &dwTemp;
340
341 if (!pHandle)
342 {
343 dwErrorCode = ERROR_INVALID_HANDLE;
344 }
345 else if (!pcbNeeded)
346 {
347 dwErrorCode = ERROR_INVALID_PARAMETER;
348 }
349 else if (pHandle->HandleType == HandleType_Printer)
350 {
351 dwErrorCode = _LocalGetPrinterHandleData(pHandle->pSpecificHandle, pKeyName, pValueName, pType, pData, nSize, pcbNeeded);
352 }
353 else if (pHandle->HandleType == HandleType_PrintServer)
354 {
355 dwErrorCode = _LocalGetPrintServerHandleData(pValueName, pType, pData, nSize, pcbNeeded);
356 }
357 else
358 {
359 dwErrorCode = ERROR_INVALID_HANDLE;
360 }
361
362 SetLastError(dwErrorCode);
363 return dwErrorCode;
364 }
365
366 DWORD WINAPI
367 LocalSetPrinterData(HANDLE hPrinter, PWSTR pValueName, DWORD Type, PBYTE pData, DWORD cbData)
368 {
369 // The ReactOS Printing Stack forwards all SetPrinterData calls to SetPrinterDataEx as soon as possible.
370 // This function may only be called if localspl.dll is used together with Windows Printing Stack components.
371 WARN("This function should never be called!\n");
372 return LocalSetPrinterDataEx(hPrinter, L"PrinterDriverData", pValueName, Type, pData, cbData);
373 }
374
375 static DWORD
376 _LocalSetPrinterHandleData(PLOCAL_PRINTER_HANDLE pPrinterHandle, PCWSTR pKeyName, PCWSTR pValueName, DWORD Type, PBYTE pData, DWORD cbData)
377 {
378 DWORD dwErrorCode;
379 HKEY hKey = NULL;
380 PWSTR pwszSubKey = NULL;
381
382 dwErrorCode = _MakePrinterSubKey(pPrinterHandle, pKeyName, &pwszSubKey);
383 if (dwErrorCode != ERROR_SUCCESS)
384 goto Cleanup;
385
386 // Open the subkey.
387 dwErrorCode = (DWORD)RegOpenKeyExW(hPrintersKey, pwszSubKey, 0, KEY_SET_VALUE, &hKey);
388 if (dwErrorCode != ERROR_SUCCESS)
389 {
390 ERR("RegOpenKeyExW failed for \"%S\" with error %lu!\n", pwszSubKey, dwErrorCode);
391 goto Cleanup;
392 }
393
394 // Set the value.
395 dwErrorCode = (DWORD)RegSetValueExW(hKey, pValueName, 0, Type, pData, cbData);
396
397 Cleanup:
398 if (hKey)
399 RegCloseKey(hKey);
400
401 if (pwszSubKey)
402 DllFreeSplMem(pwszSubKey);
403
404 return dwErrorCode;
405 }
406
407 static DWORD
408 _LocalSetPrintServerHandleData(PCWSTR pValueName, DWORD Type, PBYTE pData, DWORD cbData)
409 {
410 DWORD dwErrorCode;
411
412 if (wcsicmp(pValueName, SPLREG_DEFAULT_SPOOL_DIRECTORY) == 0 ||
413 wcsicmp(pValueName, SPLREG_PORT_THREAD_PRIORITY) == 0 ||
414 wcsicmp(pValueName, SPLREG_SCHEDULER_THREAD_PRIORITY) == 0 ||
415 wcsicmp(pValueName, SPLREG_BEEP_ENABLED) == 0 ||
416 wcsicmp(pValueName, SPLREG_ALLOW_USER_MANAGEFORMS) == 0)
417 {
418 return (DWORD)RegSetValueExW(hPrintersKey, pValueName, 0, Type, pData, cbData);
419 }
420 else if (wcsicmp(pValueName, SPLREG_NET_POPUP) == 0 ||
421 wcsicmp(pValueName, SPLREG_RETRY_POPUP) == 0 ||
422 wcsicmp(pValueName, SPLREG_NET_POPUP_TO_COMPUTER) == 0 ||
423 wcsicmp(pValueName, SPLREG_EVENT_LOG) == 0 ||
424 wcsicmp(pValueName, SPLREG_RESTART_JOB_ON_POOL_ERROR) == 0 ||
425 wcsicmp(pValueName, SPLREG_RESTART_JOB_ON_POOL_ENABLED) == 0 ||
426 wcsicmp(pValueName, L"NoRemotePrinterDrivers") == 0)
427 {
428 HKEY hKey;
429
430 dwErrorCode = (DWORD)RegOpenKeyExW(hPrintKey, L"Providers", 0, KEY_SET_VALUE, &hKey);
431 if (dwErrorCode != ERROR_SUCCESS)
432 {
433 ERR("RegOpenKeyExW failed for \"Providers\" with error %lu!\n", dwErrorCode);
434 return dwErrorCode;
435 }
436
437 dwErrorCode = (DWORD)RegSetValueExW(hKey, pValueName, 0, Type, pData, cbData);
438 RegCloseKey(hKey);
439 return dwErrorCode;
440 }
441 else if (wcsicmp(pValueName, SPLREG_WEBSHAREMGMT) == 0)
442 {
443 WARN("Attempting to set WebShareMgmt, which is based on IIS and therefore not supported. Returning fake success!\n");
444 return ERROR_SUCCESS;
445 }
446 else
447 {
448 // For all other, unknown settings, we just return ERROR_INVALID_PARAMETER.
449 return ERROR_INVALID_PARAMETER;
450 }
451 }
452
453 DWORD WINAPI
454 LocalSetPrinterDataEx(HANDLE hPrinter, PCWSTR pKeyName, PCWSTR pValueName, DWORD Type, PBYTE pData, DWORD cbData)
455 {
456 DWORD dwErrorCode;
457 PLOCAL_HANDLE pHandle = (PLOCAL_HANDLE)hPrinter;
458
459 if (!pHandle)
460 {
461 dwErrorCode = ERROR_INVALID_HANDLE;
462 }
463 else if (pHandle->HandleType == HandleType_Printer)
464 {
465 dwErrorCode = _LocalSetPrinterHandleData(pHandle->pSpecificHandle, pKeyName, pValueName, Type, pData, cbData);
466 }
467 else if (pHandle->HandleType == HandleType_PrintServer)
468 {
469 dwErrorCode = _LocalSetPrintServerHandleData(pValueName, Type, pData, cbData);
470 }
471 else
472 {
473 dwErrorCode = ERROR_INVALID_HANDLE;
474 }
475
476 SetLastError(dwErrorCode);
477 return dwErrorCode;
478 }