reactos/win32ss/printing/monitors/localmon/xcv.c
James Tabor 3ca217621d [Printing] Fix Typos and Test Regressions
Fix WinSpool API regressions.
Fix typos and debug prints.
2020-09-08 13:15:16 -05:00

613 lines
18 KiB
C

/*
* PROJECT: ReactOS Local Port Monitor
* LICENSE: GPL-2.0+ (https://spdx.org/licenses/GPL-2.0+)
* PURPOSE: Implementation of Xcv* and support functions
* COPYRIGHT: Copyright 2015 Colin Finck (colin@reactos.org)
*/
#include "precomp.h"
static DWORD
_HandleAddPort(PLOCALMON_XCV pXcv, PBYTE pInputData, PDWORD pcbOutputNeeded)
{
DWORD res, cbPortName;
HKEY hroot;
HKEY hToken = NULL;
PLOCALMON_PORT pPort;
PLOCALMON_HANDLE pLocalmon = pXcv->pLocalmon;
PWSTR PortName = (PWSTR)pInputData;
FIXME("LcmXcvAddPort : %s\n", debugstr_w( (LPWSTR) PortName ) );
if (!pLocalmon )
{
res = ERROR_INVALID_PARAMETER;
goto Cleanup;
}
// This action can only happen at SERVER_ACCESS_ADMINISTER access level.
if (!(pXcv->GrantedAccess & SERVER_ACCESS_ADMINISTER))
{
res = ERROR_ACCESS_DENIED;
goto Cleanup;
}
// Switch to the SYSTEM context for modifying the registry.
hToken = RevertToPrinterSelf();
if (!hToken)
{
res = GetLastError();
ERR("RevertToPrinterSelf failed with error %lu!\n", res);
goto Cleanup;
}
res = RegOpenKeyW(HKEY_LOCAL_MACHINE, L"Software\\Microsoft\\Windows NT\\CurrentVersion\\Ports", &hroot);
if (res == ERROR_SUCCESS)
{
if ( DoesPortExist( PortName ) )
{
RegCloseKey(hroot);
FIXME("=> %u\n", ERROR_ALREADY_EXISTS);
res = ERROR_ALREADY_EXISTS;
goto Cleanup;
}
cbPortName = (wcslen( PortName ) + 1) * sizeof(WCHAR);
// Create a new LOCALMON_PORT structure for it.
pPort = DllAllocSplMem(sizeof(LOCALMON_PORT) + cbPortName);
if (!pPort)
{
res = ERROR_NOT_ENOUGH_MEMORY;
RegCloseKey( hroot );
goto Cleanup;
}
memset( pPort, 0, sizeof(LOCALMON_PORT) + cbPortName );
pPort->hFile = INVALID_HANDLE_VALUE;
pPort->pLocalmon = pLocalmon;
pPort->pwszPortName = wcscpy( (PWSTR)(pPort+1), PortName );
// Insert it into the Registry list.
InsertTailList(&pLocalmon->RegistryPorts, &pPort->Entry);
res = RegSetValueExW(hroot, PortName, 0, REG_SZ, (const BYTE *) L"", sizeof(L""));
RegCloseKey(hroot);
}
FIXME("=> %u\n", res);
Cleanup:
if (hToken) ImpersonatePrinterClient(hToken);
SetLastError(res);
return res;
}
/**
* @name _HandleConfigureLPTPortCommandOK
*
* Writes the value for "TransmissionRetryTimeout" to the registry. Checks for granted SERVER_ACCESS_ADMINISTER access.
* Actually the opposite of _HandleGetTransmissionRetryTimeout, but name kept for compatibility.
*
* @param pXcv
* Pointer to the LOCALMON_XCV structure of the currently opened Xcv port.
*
* @param pInputData
* Pointer to a Unicode string containing the value to be written to the registry.
*
* @param pcbOutputNeeded
* Pointer to a DWORD that will be zeroed on return.
*
* @return
* An error code indicating success or failure.
*/
static DWORD
_HandleConfigureLPTPortCommandOK(PLOCALMON_XCV pXcv, PBYTE pInputData, PDWORD pcbOutputNeeded)
{
DWORD cbBuffer;
DWORD dwErrorCode;
HKEY hKey = NULL;
HKEY hToken = NULL;
// Sanity checks
if (!pXcv || !pInputData || !pcbOutputNeeded)
{
dwErrorCode = ERROR_INVALID_PARAMETER;
goto Cleanup;
}
*pcbOutputNeeded = 0;
// This action can only happen at SERVER_ACCESS_ADMINISTER access level.
if (!(pXcv->GrantedAccess & SERVER_ACCESS_ADMINISTER))
{
dwErrorCode = ERROR_ACCESS_DENIED;
goto Cleanup;
}
// Switch to the SYSTEM context for modifying the registry.
hToken = RevertToPrinterSelf();
if (!hToken)
{
dwErrorCode = GetLastError();
ERR("RevertToPrinterSelf failed with error %lu!\n", dwErrorCode);
goto Cleanup;
}
// Open the key where our value is stored.
dwErrorCode = (DWORD)RegOpenKeyExW(HKEY_LOCAL_MACHINE, L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Windows", 0, KEY_SET_VALUE, &hKey);
if (dwErrorCode != ERROR_SUCCESS)
{
ERR("RegOpenKeyExW failed with status %lu!\n", dwErrorCode);
goto Cleanup;
}
// We don't use cbInputData here, because the buffer pInputData could be bigger than the data it contains.
cbBuffer = (wcslen((PWSTR)pInputData) + 1) * sizeof(WCHAR);
// Write the value to the registry.
dwErrorCode = (DWORD)RegSetValueExW(hKey, L"TransmissionRetryTimeout", 0, REG_SZ, pInputData, cbBuffer);
if (dwErrorCode != ERROR_SUCCESS)
{
ERR("RegSetValueExW failed with status %lu!\n", dwErrorCode);
goto Cleanup;
}
Cleanup:
if (hKey)
RegCloseKey(hKey);
if (hToken)
ImpersonatePrinterClient(hToken);
return dwErrorCode;
}
static DWORD
_HandleDeletePort(PLOCALMON_XCV pXcv, PBYTE pInputData, PDWORD pcbOutputNeeded)
{
DWORD res;
HKEY hroot;
HKEY hToken = NULL;
PLOCALMON_HANDLE pLocalmon = pXcv->pLocalmon;
PLOCALMON_PORT pPort = NULL;
PLIST_ENTRY pEntry;
PWSTR pPortName = (PWSTR)pInputData;
FIXME("LcmXcvDeletePort : %s\n", debugstr_w( pPortName ) );
if (!pLocalmon )
{
res = ERROR_INVALID_PARAMETER;
goto Cleanup;
}
// This action can only happen at SERVER_ACCESS_ADMINISTER access level.
if (!(pXcv->GrantedAccess & SERVER_ACCESS_ADMINISTER))
{
res = ERROR_ACCESS_DENIED;
goto Cleanup;
}
// Switch to the SYSTEM context for modifying the registry.
hToken = RevertToPrinterSelf();
if (!hToken)
{
res = GetLastError();
ERR("RevertToPrinterSelf failed with error %lu!\n", res);
goto Cleanup;
}
res = RegOpenKeyW(HKEY_LOCAL_MACHINE, L"Software\\Microsoft\\Windows NT\\CurrentVersion\\Ports", &hroot);
if ( res == ERROR_SUCCESS )
{
res = RegDeleteValueW(hroot, pPortName );
RegCloseKey(hroot);
if ( res == ERROR_SUCCESS )
{
EnterCriticalSection(&pLocalmon->Section);
if (!IsListEmpty(&pLocalmon->RegistryPorts) )
{
for (pEntry = pLocalmon->RegistryPorts.Flink; pEntry != &pLocalmon->RegistryPorts; pEntry = pEntry->Flink)
{
pPort = CONTAINING_RECORD(pEntry, LOCALMON_PORT, Entry);
if (wcscmp(pPort->pwszPortName, pPortName) == 0)
break;
}
}
LeaveCriticalSection(&pLocalmon->Section);
if ( pPort )
{
FIXME("LcmXcvDeletePort removed Port Entry\n");
EnterCriticalSection(&pPort->pLocalmon->Section);
RemoveEntryList(&pPort->Entry);
LeaveCriticalSection(&pPort->pLocalmon->Section);
DllFreeSplMem(pPort);
}
}
FIXME("LcmXcvDeletePort => %u with %u\n", res, GetLastError() );
}
Cleanup:
if (hToken) ImpersonatePrinterClient(hToken);
SetLastError(res);
return res;
}
/**
* @name _HandleGetDefaultCommConfig
*
* Gets the default configuration of a legacy port.
* The opposite function is _HandleSetDefaultCommConfig.
*
* @param pInputData
* The port name (without colon!) whose default configuration you want to get.
*
* @param pOutputData
* Pointer to a COMMCONFIG structure that will receive the configuration information.
*
* @param cbOutputData
* Size of the variable pointed to by pOutputData.
*
* @param pcbOutputNeeded
* Pointer to a DWORD that contains the required size for pOutputData on return.
*
* @return
* An error code indicating success or failure.
*/
static DWORD
_HandleGetDefaultCommConfig(PBYTE pInputData, PBYTE pOutputData, DWORD cbOutputData, PDWORD pcbOutputNeeded)
{
// Sanity checks
if (!pInputData || !pcbOutputNeeded)
return ERROR_INVALID_PARAMETER;
*pcbOutputNeeded = sizeof(COMMCONFIG);
// Check if the supplied buffer is large enough.
if (cbOutputData < *pcbOutputNeeded)
return ERROR_INSUFFICIENT_BUFFER;
// Finally get the port configuration.
if (!GetDefaultCommConfigW((PCWSTR)pInputData, (LPCOMMCONFIG)pOutputData, pcbOutputNeeded))
return GetLastError();
return ERROR_SUCCESS;
}
/**
* @name _HandleGetTransmissionRetryTimeout
*
* Reads the value for "TransmissionRetryTimeout" from the registry and converts it to a DWORD.
* The opposite function is _HandleConfigureLPTPortCommandOK.
*
* @param pOutputData
* Pointer to a DWORD that will receive the timeout value.
*
* @param cbOutputData
* Size of the variable pointed to by pOutputData.
*
* @param pcbOutputNeeded
* Pointer to a DWORD that contains the required size for pOutputData on return.
*
* @return
* An error code indicating success or failure.
*/
static DWORD
_HandleGetTransmissionRetryTimeout(PBYTE pOutputData, DWORD cbOutputData, PDWORD pcbOutputNeeded)
{
DWORD dwTimeout;
// Sanity checks
if (!pOutputData || !pcbOutputNeeded)
return ERROR_INVALID_PARAMETER;
*pcbOutputNeeded = sizeof(DWORD);
// Check if the supplied buffer is large enough.
if (cbOutputData < *pcbOutputNeeded)
return ERROR_INSUFFICIENT_BUFFER;
// Retrieve and copy the number.
dwTimeout = GetLPTTransmissionRetryTimeout();
CopyMemory(pOutputData, &dwTimeout, sizeof(DWORD));
return ERROR_SUCCESS;
}
/**
* @name _HandleMonitorUI
*
* Returns the filename of the associated UI DLL for this Port Monitor.
*
* @param pOutputData
* Pointer to a Unicode string that will receive the DLL filename.
*
* @param cbOutputData
* Size of the variable pointed to by pOutputData.
*
* @param pcbOutputNeeded
* Pointer to a DWORD that contains the required size for pOutputData on return.
*
* @return
* An error code indicating success or failure.
*/
static DWORD
_HandleMonitorUI(PBYTE pOutputData, DWORD cbOutputData, PDWORD pcbOutputNeeded)
{
const WCHAR wszMonitorUI[] = L"LocalUI.dll";
// Sanity checks
if (!pcbOutputNeeded)
return ERROR_INVALID_PARAMETER;
*pcbOutputNeeded = sizeof(wszMonitorUI);
// Check if the supplied buffer is large enough.
if (cbOutputData < *pcbOutputNeeded)
return ERROR_INSUFFICIENT_BUFFER;
if (!pOutputData)
return ERROR_INVALID_PARAMETER;
// Copy the string.
CopyMemory(pOutputData, wszMonitorUI, sizeof(wszMonitorUI));
return ERROR_SUCCESS;
}
/**
* @name _HandlePortExists
*
* Checks all Port Monitors installed on the local system to find out if a given port already exists.
*
* @param pInputData
* Pointer to a Unicode string specifying the port name to check.
*
* @param pOutputData
* Pointer to a BOOL that receives the result of the check.
*
* @param cbOutputData
* Size of the variable pointed to by pOutputData.
*
* @param pcbOutputNeeded
* Pointer to a DWORD that contains the required size for pOutputData on return.
*
* @return
* An error code indicating success or failure.
*/
static DWORD
_HandlePortExists(PBYTE pInputData, PBYTE pOutputData, DWORD cbOutputData, PDWORD pcbOutputNeeded)
{
// Sanity checks
if (!pInputData || !pOutputData || !pcbOutputNeeded)
return ERROR_INVALID_PARAMETER;
*pcbOutputNeeded = sizeof(BOOL);
// Check if the supplied buffer is large enough.
if (cbOutputData < *pcbOutputNeeded)
return ERROR_INSUFFICIENT_BUFFER;
// Return the check result and error code.
*(PBOOL)pOutputData = DoesPortExist((PCWSTR)pInputData);
return GetLastError();
}
static DWORD
_HandlePortIsValid(PBYTE pInputData, PBYTE pOutputData, DWORD cbOutputData, PDWORD pcbOutputNeeded)
{
DWORD res;
TRACE("HandlePortIsValid : pInputData %s\n", debugstr_w( (LPWSTR) pInputData));
res = GetTypeFromName((LPCWSTR) pInputData);
TRACE("HandlePortIsValid : detected as %u\n", res);
/* names, that we have recognized, are valid */
if (res) return ERROR_SUCCESS;
TRACE("=> %u\n", GetLastError());
/* ERROR_ACCESS_DENIED, ERROR_PATH_NOT_FOUND or something else */
return GetLastError();
}
/**
* @name _HandleSetDefaultCommConfig
*
* Sets the default configuration of a legacy port. Checks for granted SERVER_ACCESS_ADMINISTER access.
* You have to supply the port name (with colon!) in XcvOpenPort.
* The opposite function is _HandleGetDefaultCommConfig.
*
* @param pXcv
* Pointer to the LOCALMON_XCV structure of the currently opened Xcv port.
*
* @param pInputData
* Pointer to the COMMCONFIG structure that shall be passed to SetDefaultCommConfigW.
*
* @param pcbOutputNeeded
* Pointer to a DWORD that will be zeroed on return.
*
* @return
* An error code indicating success or failure.
*/
static DWORD
_HandleSetDefaultCommConfig(PLOCALMON_XCV pXcv, PBYTE pInputData, PDWORD pcbOutputNeeded)
{
DWORD dwErrorCode;
HANDLE hToken = NULL;
LPCOMMCONFIG pCommConfig;
PWSTR pwszPortNameWithoutColon = NULL;
// Sanity checks
// pwszObject needs to be at least 2 characters long to be a port name with a trailing colon.
if (!pXcv || !pXcv->pwszObject || !pXcv->pwszObject[0] || !pXcv->pwszObject[1] || !pInputData || !pcbOutputNeeded)
{
dwErrorCode = ERROR_INVALID_PARAMETER;
goto Cleanup;
}
*pcbOutputNeeded = 0;
// This action can only happen at SERVER_ACCESS_ADMINISTER access level.
if (!(pXcv->GrantedAccess & SERVER_ACCESS_ADMINISTER))
{
dwErrorCode = ERROR_ACCESS_DENIED;
goto Cleanup;
}
// SetDefaultCommConfigW needs the port name without colon.
dwErrorCode = GetPortNameWithoutColon(pXcv->pwszObject, &pwszPortNameWithoutColon);
if (dwErrorCode != ERROR_SUCCESS)
goto Cleanup;
// Switch to the SYSTEM context for setting the port configuration.
hToken = RevertToPrinterSelf();
if (!hToken)
{
dwErrorCode = GetLastError();
ERR("RevertToPrinterSelf failed with error %lu!\n", dwErrorCode);
goto Cleanup;
}
// Finally pass the parameters to SetDefaultCommConfigW.
pCommConfig = (LPCOMMCONFIG)pInputData;
if (!SetDefaultCommConfigW(pwszPortNameWithoutColon, pCommConfig, pCommConfig->dwSize))
{
dwErrorCode = GetLastError();
ERR("SetDefaultCommConfigW failed with error %lu!\n", dwErrorCode);
goto Cleanup;
}
dwErrorCode = ERROR_SUCCESS;
Cleanup:
if (hToken)
ImpersonatePrinterClient(hToken);
if (pwszPortNameWithoutColon)
DllFreeSplMem(pwszPortNameWithoutColon);
return dwErrorCode;
}
BOOL WINAPI
LocalmonXcvClosePort(HANDLE hXcv)
{
PLOCALMON_XCV pXcv = (PLOCALMON_XCV)hXcv;
TRACE("LocalmonXcvClosePort(%p)\n", hXcv);
// Sanity checks
if (!pXcv)
{
SetLastError(ERROR_INVALID_PARAMETER);
return FALSE;
}
// Remove it from the list and free the memory.
EnterCriticalSection(&pXcv->pLocalmon->Section);
RemoveEntryList(&pXcv->Entry);
LeaveCriticalSection(&pXcv->pLocalmon->Section);
DllFreeSplMem(pXcv);
SetLastError(ERROR_SUCCESS);
return TRUE;
}
DWORD WINAPI
LocalmonXcvDataPort(HANDLE hXcv, PCWSTR pszDataName, PBYTE pInputData, DWORD cbInputData, PBYTE pOutputData, DWORD cbOutputData, PDWORD pcbOutputNeeded)
{
FIXME("LocalmonXcvDataPort(%p, %S, %p, %lu, %p, %lu, %p)\n", hXcv, pszDataName, pInputData, cbInputData, pOutputData, cbOutputData, pcbOutputNeeded);
// Sanity checks
if (!pszDataName)
return ERROR_INVALID_PARAMETER;
// Call the appropriate handler for the requested data name.
if (wcscmp(pszDataName, L"AddPort") == 0)
return _HandleAddPort((PLOCALMON_XCV)hXcv, pInputData, pcbOutputNeeded);
if (wcscmp(pszDataName, L"ConfigureLPTPortCommandOK") == 0)
return _HandleConfigureLPTPortCommandOK((PLOCALMON_XCV)hXcv, pInputData, pcbOutputNeeded);
if (wcscmp(pszDataName, L"DeletePort") == 0)
return _HandleDeletePort((PLOCALMON_XCV)hXcv, pInputData, pcbOutputNeeded);
if (wcscmp(pszDataName, L"GetDefaultCommConfig") == 0)
return _HandleGetDefaultCommConfig(pInputData, pOutputData, cbOutputData, pcbOutputNeeded);
if (wcscmp(pszDataName, L"GetTransmissionRetryTimeout") == 0)
return _HandleGetTransmissionRetryTimeout(pOutputData, cbOutputData, pcbOutputNeeded);
if (wcscmp(pszDataName, L"MonitorUI") == 0)
return _HandleMonitorUI(pOutputData, cbOutputData, pcbOutputNeeded);
if (wcscmp(pszDataName, L"PortExists") == 0)
return _HandlePortExists(pInputData, pOutputData, cbOutputData, pcbOutputNeeded);
if (wcscmp(pszDataName, L"PortIsValid") == 0)
return _HandlePortIsValid(pInputData, pOutputData, cbOutputData, pcbOutputNeeded);
if (wcscmp(pszDataName, L"SetDefaultCommConfig") == 0)
return _HandleSetDefaultCommConfig((PLOCALMON_XCV)hXcv, pInputData, pcbOutputNeeded);
return ERROR_INVALID_PARAMETER;
}
BOOL WINAPI
LocalmonXcvOpenPort(HANDLE hMonitor, PCWSTR pwszObject, ACCESS_MASK GrantedAccess, PHANDLE phXcv)
{
DWORD cbObject = 0;
DWORD dwErrorCode;
PLOCALMON_HANDLE pLocalmon = (PLOCALMON_HANDLE)hMonitor;
PLOCALMON_XCV pXcv;
FIXME("LocalmonXcvOpenPort(%p, %S, %lu, %p)\n", hMonitor, pwszObject, GrantedAccess, phXcv);
// Sanity checks
if (!pLocalmon || !phXcv)
{
dwErrorCode = ERROR_INVALID_PARAMETER;
goto Cleanup;
}
if (pwszObject)
cbObject = (wcslen(pwszObject) + 1) * sizeof(WCHAR);
// Create a new LOCALMON_XCV structure and fill the relevant fields.
pXcv = DllAllocSplMem(sizeof(LOCALMON_XCV) + cbObject);
if (!pXcv)
{
dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
ERR("DllAllocSplMem failed with error %lu!\n", GetLastError());
goto Cleanup;
}
pXcv->pLocalmon = pLocalmon;
pXcv->GrantedAccess = GrantedAccess;
if (cbObject)
{
pXcv->pwszObject = (PWSTR)((PBYTE)pXcv + sizeof(LOCALMON_XCV));
CopyMemory(pXcv->pwszObject, pwszObject, cbObject);
}
InsertTailList(&pLocalmon->XcvHandles, &pXcv->Entry);
// Return it as the Xcv handle.
*phXcv = (HANDLE)pXcv;
dwErrorCode = ERROR_SUCCESS;
Cleanup:
SetLastError(dwErrorCode);
return (dwErrorCode == ERROR_SUCCESS);
}