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 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 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 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 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 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 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 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 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 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