reactos/win32ss/printing/providers/localspl/printerdata.c

477 lines
17 KiB
C

/*
* PROJECT: ReactOS Local Spooler
* LICENSE: GPL-2.0+ (https://spdx.org/licenses/GPL-2.0+)
* PURPOSE: Functions related to Printer Configuration Data
* COPYRIGHT: Copyright 2017 Colin Finck (colin@reactos.org)
*/
#include "precomp.h"
DWORD WINAPI
LocalGetPrinterData(HANDLE hPrinter, PWSTR pValueName, PDWORD pType, PBYTE pData, DWORD nSize, PDWORD pcbNeeded)
{
TRACE("LocalGetPrinterData(%p, %S, %p, %p, %lu, %p)\n", hPrinter, pValueName, pType, pData, nSize, pcbNeeded);
// The ReactOS Printing Stack forwards all GetPrinterData calls to GetPrinterDataEx as soon as possible.
// This function may only be called if localspl.dll is used together with Windows Printing Stack components.
WARN("This function should never be called!\n");
return LocalGetPrinterDataEx(hPrinter, L"PrinterDriverData", pValueName, pType, pData, nSize, pcbNeeded);
}
static DWORD
_MakePrinterSubKey(PLOCAL_PRINTER_HANDLE pPrinterHandle, PCWSTR pKeyName, PWSTR* ppwszSubKey)
{
const WCHAR wszBackslash[] = L"\\";
size_t cbSubKey;
PWSTR p;
// Sanity check
if (!pKeyName || !*pKeyName)
return ERROR_INVALID_PARAMETER;
// Allocate a buffer for the subkey "PrinterName\KeyName".
cbSubKey = (wcslen(pPrinterHandle->pPrinter->pwszPrinterName) + 1 + wcslen(pKeyName) + 1) * sizeof(WCHAR);
*ppwszSubKey = DllAllocSplMem(cbSubKey);
if (!*ppwszSubKey)
return ERROR_NOT_ENOUGH_MEMORY;
// Concatenate the subkey.
p = *ppwszSubKey;
StringCbCopyExW(p, cbSubKey, pPrinterHandle->pPrinter->pwszPrinterName, &p, &cbSubKey, 0);
StringCbCopyExW(p, cbSubKey, wszBackslash, &p, &cbSubKey, 0);
StringCbCopyExW(p, cbSubKey, pKeyName, &p, &cbSubKey, 0);
return ERROR_SUCCESS;
}
static DWORD
_LocalGetPrinterHandleData(PLOCAL_PRINTER_HANDLE pPrinterHandle, PCWSTR pKeyName, PCWSTR pValueName, PDWORD pType, PBYTE pData, DWORD nSize, PDWORD pcbNeeded)
{
DWORD dwErrorCode;
HKEY hKey = NULL;
PWSTR pwszSubKey = NULL;
dwErrorCode = _MakePrinterSubKey(pPrinterHandle, pKeyName, &pwszSubKey);
if (dwErrorCode != ERROR_SUCCESS)
goto Cleanup;
// Open the subkey.
dwErrorCode = (DWORD)RegOpenKeyExW(hPrintersKey, pwszSubKey, 0, KEY_READ, &hKey);
if (dwErrorCode != ERROR_SUCCESS)
{
ERR("RegOpenKeyExW failed for \"%S\" with error %lu!\n", pwszSubKey, dwErrorCode);
goto Cleanup;
}
// Query the desired value.
*pcbNeeded = nSize;
dwErrorCode = (DWORD)RegQueryValueExW(hKey, pValueName, NULL, pType, pData, pcbNeeded);
Cleanup:
if (hKey)
RegCloseKey(hKey);
if (pwszSubKey)
DllFreeSplMem(pwszSubKey);
return dwErrorCode;
}
static DWORD
_LocalGetPrintServerHandleData(PCWSTR pValueName, PDWORD pType, PBYTE pData, DWORD nSize, PDWORD pcbNeeded)
{
DWORD dwErrorCode;
if (wcsicmp(pValueName, SPLREG_DEFAULT_SPOOL_DIRECTORY) == 0 ||
wcsicmp(pValueName, SPLREG_PORT_THREAD_PRIORITY) == 0 ||
wcsicmp(pValueName, SPLREG_SCHEDULER_THREAD_PRIORITY) == 0 ||
wcsicmp(pValueName, SPLREG_BEEP_ENABLED) == 0 ||
wcsicmp(pValueName, SPLREG_ALLOW_USER_MANAGEFORMS) == 0)
{
*pcbNeeded = nSize;
return (DWORD)RegQueryValueExW(hPrintersKey, pValueName, NULL, pType, pData, pcbNeeded);
}
else if (wcsicmp(pValueName, SPLREG_PORT_THREAD_PRIORITY_DEFAULT) == 0 ||
wcsicmp(pValueName, SPLREG_SCHEDULER_THREAD_PRIORITY_DEFAULT) == 0)
{
// Store a DWORD value as REG_NONE.
*pType = REG_NONE;
*pcbNeeded = sizeof(DWORD);
if (nSize < *pcbNeeded)
return ERROR_MORE_DATA;
// Apparently, these values don't serve a purpose anymore.
*((PDWORD)pData) = 0;
return ERROR_SUCCESS;
}
else if (wcsicmp(pValueName, SPLREG_NET_POPUP) == 0 ||
wcsicmp(pValueName, SPLREG_RETRY_POPUP) == 0 ||
wcsicmp(pValueName, SPLREG_NET_POPUP_TO_COMPUTER) == 0 ||
wcsicmp(pValueName, SPLREG_EVENT_LOG) == 0 ||
wcsicmp(pValueName, SPLREG_RESTART_JOB_ON_POOL_ERROR) == 0 ||
wcsicmp(pValueName, SPLREG_RESTART_JOB_ON_POOL_ENABLED) == 0)
{
HKEY hKey;
dwErrorCode = (DWORD)RegOpenKeyExW(hPrintKey, L"Providers", 0, KEY_READ, &hKey);
if (dwErrorCode != ERROR_SUCCESS)
{
ERR("RegOpenKeyExW failed for \"Providers\" with error %lu!\n", dwErrorCode);
return dwErrorCode;
}
*pcbNeeded = nSize;
dwErrorCode = (DWORD)RegQueryValueExW(hKey, pValueName, NULL, pType, pData, pcbNeeded);
RegCloseKey(hKey);
return dwErrorCode;
}
else if (wcsicmp(pValueName, SPLREG_MAJOR_VERSION) == 0)
{
// Store a DWORD value as REG_NONE.
*pType = REG_NONE;
*pcbNeeded = sizeof(DWORD);
if (nSize < *pcbNeeded)
return ERROR_MORE_DATA;
// Apparently, these values don't serve a purpose anymore.
*((PDWORD)pData) = dwSpoolerMajorVersion;
return ERROR_SUCCESS;
}
else if (wcsicmp(pValueName, SPLREG_MINOR_VERSION) == 0)
{
// Store a DWORD value as REG_NONE.
*pType = REG_NONE;
*pcbNeeded = sizeof(DWORD);
if (nSize < *pcbNeeded)
return ERROR_MORE_DATA;
// Apparently, these values don't serve a purpose anymore.
*((PDWORD)pData) = dwSpoolerMinorVersion;
return ERROR_SUCCESS;
}
else if (wcsicmp(pValueName, SPLREG_ARCHITECTURE) == 0)
{
// Store a string as REG_NONE with the length of the environment name string.
*pType = REG_NONE;
*pcbNeeded = cbCurrentEnvironment;
if (nSize < *pcbNeeded)
return ERROR_MORE_DATA;
// Copy the environment name as the output value for SPLREG_ARCHITECTURE.
CopyMemory(pData, wszCurrentEnvironment, cbCurrentEnvironment);
return ERROR_SUCCESS;
}
else if (wcsicmp(pValueName, SPLREG_OS_VERSION) == 0)
{
POSVERSIONINFOW pInfo = (POSVERSIONINFOW)pData;
// Store the OSVERSIONINFOW structure as REG_NONE.
*pType = REG_NONE;
*pcbNeeded = sizeof(OSVERSIONINFOW);
if (nSize < *pcbNeeded)
return ERROR_MORE_DATA;
// Return OS version information.
pInfo->dwOSVersionInfoSize = sizeof(OSVERSIONINFOW);
GetVersionExW(pInfo);
return ERROR_SUCCESS;
}
else if (wcsicmp(pValueName, SPLREG_OS_VERSIONEX) == 0)
{
POSVERSIONINFOEXW pInfo = (POSVERSIONINFOEXW)pData;
// Store the OSVERSIONINFOEXW structure as REG_NONE.
*pType = REG_NONE;
*pcbNeeded = sizeof(OSVERSIONINFOEXW);
if (nSize < *pcbNeeded)
return ERROR_MORE_DATA;
// Return extended OS version information.
pInfo->dwOSVersionInfoSize = sizeof(OSVERSIONINFOEXW);
GetVersionExW((POSVERSIONINFOW)pInfo);
return ERROR_SUCCESS;
}
else if (wcsicmp(pValueName, SPLREG_DS_PRESENT) == 0)
{
PDSROLE_PRIMARY_DOMAIN_INFO_BASIC pInfo;
// We want to store a REG_DWORD value.
*pType = REG_DWORD;
*pcbNeeded = sizeof(DWORD);
if (nSize < *pcbNeeded)
return ERROR_MORE_DATA;
// Get information about the domain membership of this computer.
dwErrorCode = DsRoleGetPrimaryDomainInformation(NULL, DsRolePrimaryDomainInfoBasic, (PBYTE*)&pInfo);
if (dwErrorCode != ERROR_SUCCESS)
{
ERR("DsRoleGetPrimaryDomainInformation failed with error %lu!\n", dwErrorCode);
return dwErrorCode;
}
// Return whether this computer is a workstation or server inside a domain.
*((PDWORD)pData) = (pInfo->MachineRole == DsRole_RoleMemberWorkstation || pInfo->MachineRole == DsRole_RoleMemberServer);
DsRoleFreeMemory(pInfo);
return ERROR_SUCCESS;
}
else if (wcsicmp(pValueName, SPLREG_DS_PRESENT_FOR_USER) == 0)
{
DWORD cch;
PWSTR p;
WCHAR wszComputerName[MAX_COMPUTERNAME_LENGTH + 1];
WCHAR wszUserSam[UNLEN + 1];
// We want to store a REG_DWORD value.
*pType = REG_DWORD;
*pcbNeeded = sizeof(DWORD);
if (nSize < *pcbNeeded)
return ERROR_MORE_DATA;
// Get the local Computer Name.
cch = MAX_COMPUTERNAME_LENGTH + 1;
if (!GetComputerNameW(wszComputerName, &cch))
{
dwErrorCode = GetLastError();
ERR("GetComputerNameW failed with error %lu!\n", dwErrorCode);
return dwErrorCode;
}
// Get the User Name in the SAM format.
// This could either be:
// COMPUTERNAME\User
// DOMAINNAME\User
cch = UNLEN + 1;
if (!GetUserNameExW(NameSamCompatible, wszUserSam, &cch))
{
dwErrorCode = GetLastError();
ERR("GetUserNameExW failed with error %lu!\n", dwErrorCode);
return dwErrorCode;
}
// Terminate the SAM-formatted User Name at the backslash.
p = wcschr(wszUserSam, L'\\');
*p = 0;
// Compare it with the Computer Name.
// If they differ, this User is part of a domain.
*((PDWORD)pData) = (wcscmp(wszUserSam, wszComputerName) != 0);
return ERROR_SUCCESS;
}
else if (wcsicmp(pValueName, SPLREG_REMOTE_FAX) == 0)
{
// Store a DWORD value as REG_NONE.
*pType = REG_NONE;
*pcbNeeded = sizeof(DWORD);
if (nSize < *pcbNeeded)
return ERROR_MORE_DATA;
// TODO: We don't support any fax service yet, but let's return the same value as Windows Server 2003 here.
*((PDWORD)pData) = 1;
return ERROR_SUCCESS;
}
else if (wcsicmp(pValueName, SPLREG_DNS_MACHINE_NAME) == 0)
{
DWORD cchDnsName = 0;
// Get the length of the fully-qualified computer DNS name.
GetComputerNameExW(ComputerNameDnsFullyQualified, NULL, &cchDnsName);
dwErrorCode = GetLastError();
if (dwErrorCode != ERROR_MORE_DATA)
{
ERR("GetComputerNameExW failed with error %lu!\n", dwErrorCode);
return dwErrorCode;
}
// Check if our supplied buffer is large enough.
*pType = REG_SZ;
*pcbNeeded = cchDnsName * sizeof(WCHAR);
if (nSize < *pcbNeeded)
return ERROR_MORE_DATA;
// Get the actual DNS name.
if (!GetComputerNameExW(ComputerNameDnsFullyQualified, (PWSTR)pData, &cchDnsName))
{
dwErrorCode = GetLastError();
ERR("GetComputerNameExW failed with error %lu!\n", dwErrorCode);
return dwErrorCode;
}
// Lowercase the output just like Windows does.
_wcslwr((PWSTR)pData);
return ERROR_SUCCESS;
}
else
{
// For all other, unknown settings, we just return ERROR_INVALID_PARAMETER.
// That also includes SPLREG_WEBSHAREMGMT, which is supported in Windows Server 2003 according to the documentation,
// but is actually not!
return ERROR_INVALID_PARAMETER;
}
}
DWORD WINAPI
LocalGetPrinterDataEx(HANDLE hPrinter, PCWSTR pKeyName, PCWSTR pValueName, PDWORD pType, PBYTE pData, DWORD nSize, PDWORD pcbNeeded)
{
BYTE Temp;
DWORD dwErrorCode;
DWORD dwTemp;
PLOCAL_HANDLE pHandle = (PLOCAL_HANDLE)hPrinter;
TRACE("LocalGetPrinterDataEx(%p, %S, %S, %p, %p, %lu, %p)\n", hPrinter, pKeyName, pValueName, pType, pData, nSize, pcbNeeded);
// Even if GetPrinterDataExW in winspool ensures that the RPC function is never called without a valid pointer for pType,
// it's officially optional. Windows' fpGetPrinterDataEx also works with NULL for pType!
// Ensure here that it is always set to simplify the code later.
if (!pType)
pType = &dwTemp;
// pData is later fed to RegQueryValueExW in many cases. When calling it with zero buffer size, RegQueryValueExW returns a
// different error code based on whether pData is NULL or something else.
// Ensure here that ERROR_MORE_DATA is always returned.
if (!pData)
pData = &Temp;
if (!pHandle)
{
dwErrorCode = ERROR_INVALID_HANDLE;
}
else if (!pcbNeeded)
{
dwErrorCode = ERROR_INVALID_PARAMETER;
}
else if (pHandle->HandleType == HandleType_Printer)
{
dwErrorCode = _LocalGetPrinterHandleData(pHandle->pSpecificHandle, pKeyName, pValueName, pType, pData, nSize, pcbNeeded);
}
else if (pHandle->HandleType == HandleType_PrintServer)
{
dwErrorCode = _LocalGetPrintServerHandleData(pValueName, pType, pData, nSize, pcbNeeded);
}
else
{
dwErrorCode = ERROR_INVALID_HANDLE;
}
SetLastError(dwErrorCode);
return dwErrorCode;
}
DWORD WINAPI
LocalSetPrinterData(HANDLE hPrinter, PWSTR pValueName, DWORD Type, PBYTE pData, DWORD cbData)
{
TRACE("LocalSetPrinterData(%p, %S, %lu, %p, %lu)\n", hPrinter, pValueName, Type, pData, cbData);
// The ReactOS Printing Stack forwards all SetPrinterData calls to SetPrinterDataEx as soon as possible.
// This function may only be called if localspl.dll is used together with Windows Printing Stack components.
WARN("This function should never be called!\n");
return LocalSetPrinterDataEx(hPrinter, L"PrinterDriverData", pValueName, Type, pData, cbData);
}
static DWORD
_LocalSetPrinterHandleData(PLOCAL_PRINTER_HANDLE pPrinterHandle, PCWSTR pKeyName, PCWSTR pValueName, DWORD Type, PBYTE pData, DWORD cbData)
{
DWORD dwErrorCode;
HKEY hKey = NULL;
PWSTR pwszSubKey = NULL;
dwErrorCode = _MakePrinterSubKey(pPrinterHandle, pKeyName, &pwszSubKey);
if (dwErrorCode != ERROR_SUCCESS)
goto Cleanup;
// Open the subkey.
dwErrorCode = (DWORD)RegOpenKeyExW(hPrintersKey, pwszSubKey, 0, KEY_SET_VALUE, &hKey);
if (dwErrorCode != ERROR_SUCCESS)
{
ERR("RegOpenKeyExW failed for \"%S\" with error %lu!\n", pwszSubKey, dwErrorCode);
goto Cleanup;
}
// Set the value.
dwErrorCode = (DWORD)RegSetValueExW(hKey, pValueName, 0, Type, pData, cbData);
Cleanup:
if (hKey)
RegCloseKey(hKey);
if (pwszSubKey)
DllFreeSplMem(pwszSubKey);
return dwErrorCode;
}
static DWORD
_LocalSetPrintServerHandleData(PCWSTR pValueName, DWORD Type, PBYTE pData, DWORD cbData)
{
DWORD dwErrorCode;
if (wcsicmp(pValueName, SPLREG_DEFAULT_SPOOL_DIRECTORY) == 0 ||
wcsicmp(pValueName, SPLREG_PORT_THREAD_PRIORITY) == 0 ||
wcsicmp(pValueName, SPLREG_SCHEDULER_THREAD_PRIORITY) == 0 ||
wcsicmp(pValueName, SPLREG_BEEP_ENABLED) == 0 ||
wcsicmp(pValueName, SPLREG_ALLOW_USER_MANAGEFORMS) == 0)
{
return (DWORD)RegSetValueExW(hPrintersKey, pValueName, 0, Type, pData, cbData);
}
else if (wcsicmp(pValueName, SPLREG_NET_POPUP) == 0 ||
wcsicmp(pValueName, SPLREG_RETRY_POPUP) == 0 ||
wcsicmp(pValueName, SPLREG_NET_POPUP_TO_COMPUTER) == 0 ||
wcsicmp(pValueName, SPLREG_EVENT_LOG) == 0 ||
wcsicmp(pValueName, SPLREG_RESTART_JOB_ON_POOL_ERROR) == 0 ||
wcsicmp(pValueName, SPLREG_RESTART_JOB_ON_POOL_ENABLED) == 0 ||
wcsicmp(pValueName, L"NoRemotePrinterDrivers") == 0)
{
HKEY hKey;
dwErrorCode = (DWORD)RegOpenKeyExW(hPrintKey, L"Providers", 0, KEY_SET_VALUE, &hKey);
if (dwErrorCode != ERROR_SUCCESS)
{
ERR("RegOpenKeyExW failed for \"Providers\" with error %lu!\n", dwErrorCode);
return dwErrorCode;
}
dwErrorCode = (DWORD)RegSetValueExW(hKey, pValueName, 0, Type, pData, cbData);
RegCloseKey(hKey);
return dwErrorCode;
}
else if (wcsicmp(pValueName, SPLREG_WEBSHAREMGMT) == 0)
{
WARN("Attempting to set WebShareMgmt, which is based on IIS and therefore not supported. Returning fake success!\n");
return ERROR_SUCCESS;
}
else
{
// For all other, unknown settings, we just return ERROR_INVALID_PARAMETER.
return ERROR_INVALID_PARAMETER;
}
}
DWORD WINAPI
LocalSetPrinterDataEx(HANDLE hPrinter, PCWSTR pKeyName, PCWSTR pValueName, DWORD Type, PBYTE pData, DWORD cbData)
{
DWORD dwErrorCode;
PLOCAL_HANDLE pHandle = (PLOCAL_HANDLE)hPrinter;
TRACE("LocalSetPrinterDataEx(%p, %S, %S, %lu, %p, %lu)\n", hPrinter, pKeyName, pValueName, Type, pData, cbData);
if (!pHandle)
{
dwErrorCode = ERROR_INVALID_HANDLE;
}
else if (pHandle->HandleType == HandleType_Printer)
{
dwErrorCode = _LocalSetPrinterHandleData(pHandle->pSpecificHandle, pKeyName, pValueName, Type, pData, cbData);
}
else if (pHandle->HandleType == HandleType_PrintServer)
{
dwErrorCode = _LocalSetPrintServerHandleData(pValueName, Type, pData, cbData);
}
else
{
dwErrorCode = ERROR_INVALID_HANDLE;
}
SetLastError(dwErrorCode);
return dwErrorCode;
}