Git conversion: Make reactos the root directory, move rosapps, rostests, wallpapers...
[reactos.git] / win32ss / printing / monitors / localmon / xcv.c
diff --git a/win32ss/printing/monitors/localmon/xcv.c b/win32ss/printing/monitors/localmon/xcv.c
new file mode 100644 (file)
index 0000000..a726f3b
--- /dev/null
@@ -0,0 +1,451 @@
+/*
+ * 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)
+{
+    return ERROR_CALL_NOT_IMPLEMENTED;
+}
+
+/**
+ * @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)
+{
+    return ERROR_CALL_NOT_IMPLEMENTED;
+}
+
+/**
+ * @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 (!pOutputData || !pcbOutputNeeded)
+        return ERROR_INVALID_PARAMETER;
+
+    *pcbOutputNeeded = sizeof(wszMonitorUI);
+
+    // Check if the supplied buffer is large enough.
+    if (cbOutputData < *pcbOutputNeeded)
+        return ERROR_INSUFFICIENT_BUFFER;
+
+    // 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)
+{
+    return ERROR_CALL_NOT_IMPLEMENTED;
+}
+
+/**
+ * @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.
+    RemoveEntryList(&pXcv->Entry);
+    DllFreeSplMem(pXcv);
+
+    SetLastError(ERROR_SUCCESS);
+    return TRUE;
+}
+
+DWORD WINAPI
+LocalmonXcvDataPort(HANDLE hXcv, PCWSTR pszDataName, PBYTE pInputData, DWORD cbInputData, PBYTE pOutputData, DWORD cbOutputData, PDWORD pcbOutputNeeded)
+{
+    TRACE("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;
+
+    TRACE("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);
+}