2 * PROJECT: ReactOS Local Port Monitor
3 * LICENSE: GPL-2.0+ (https://spdx.org/licenses/GPL-2.0+)
4 * PURPOSE: Implementation of Xcv* and support functions
5 * COPYRIGHT: Copyright 2015 Colin Finck (colin@reactos.org)
11 _HandleAddPort(PLOCALMON_XCV pXcv
, PBYTE pInputData
, PDWORD pcbOutputNeeded
)
13 DWORD res
, cbPortName
;
17 PLOCALMON_HANDLE pLocalmon
= pXcv
->pLocalmon
;
18 PWSTR PortName
= (PWSTR
)pInputData
;
20 FIXME("LcmXcvAddPort : %s\n", debugstr_w( (LPWSTR
) PortName
) );
24 res
= ERROR_INVALID_PARAMETER
;
28 // This action can only happen at SERVER_ACCESS_ADMINISTER access level.
29 if (!(pXcv
->GrantedAccess
& SERVER_ACCESS_ADMINISTER
))
31 res
= ERROR_ACCESS_DENIED
;
35 // Switch to the SYSTEM context for modifying the registry.
36 hToken
= RevertToPrinterSelf();
40 ERR("RevertToPrinterSelf failed with error %lu!\n", res
);
44 res
= RegOpenKeyW(HKEY_LOCAL_MACHINE
, L
"Software\\Microsoft\\Windows NT\\CurrentVersion\\Ports", &hroot
);
45 if (res
== ERROR_SUCCESS
)
47 if ( DoesPortExist( PortName
) )
50 FIXME("=> %u\n", ERROR_ALREADY_EXISTS
);
51 res
= ERROR_ALREADY_EXISTS
;
55 cbPortName
= (wcslen( PortName
) + 1) * sizeof(WCHAR
);
57 // Create a new LOCALMON_PORT structure for it.
58 pPort
= DllAllocSplMem(sizeof(LOCALMON_PORT
) + cbPortName
);
61 res
= ERROR_NOT_ENOUGH_MEMORY
;
65 memset( pPort
, 0, sizeof(LOCALMON_PORT
) + cbPortName
);
67 pPort
->hFile
= INVALID_HANDLE_VALUE
;
68 pPort
->pLocalmon
= pLocalmon
;
69 pPort
->pwszPortName
= wcscpy( (PWSTR
)(pPort
+1), PortName
);
71 // Insert it into the Registry list.
72 InsertTailList(&pLocalmon
->RegistryPorts
, &pPort
->Entry
);
74 res
= RegSetValueExW(hroot
, PortName
, 0, REG_SZ
, (const BYTE
*) L
"", sizeof(L
""));
78 FIXME("=> %u\n", res
);
81 if (hToken
) ImpersonatePrinterClient(hToken
);
87 * @name _HandleConfigureLPTPortCommandOK
89 * Writes the value for "TransmissionRetryTimeout" to the registry. Checks for granted SERVER_ACCESS_ADMINISTER access.
90 * Actually the opposite of _HandleGetTransmissionRetryTimeout, but name kept for compatibility.
93 * Pointer to the LOCALMON_XCV structure of the currently opened Xcv port.
96 * Pointer to a Unicode string containing the value to be written to the registry.
98 * @param pcbOutputNeeded
99 * Pointer to a DWORD that will be zeroed on return.
102 * An error code indicating success or failure.
105 _HandleConfigureLPTPortCommandOK(PLOCALMON_XCV pXcv
, PBYTE pInputData
, PDWORD pcbOutputNeeded
)
113 if (!pXcv
|| !pInputData
|| !pcbOutputNeeded
)
115 dwErrorCode
= ERROR_INVALID_PARAMETER
;
119 *pcbOutputNeeded
= 0;
121 // This action can only happen at SERVER_ACCESS_ADMINISTER access level.
122 if (!(pXcv
->GrantedAccess
& SERVER_ACCESS_ADMINISTER
))
124 dwErrorCode
= ERROR_ACCESS_DENIED
;
128 // Switch to the SYSTEM context for modifying the registry.
129 hToken
= RevertToPrinterSelf();
132 dwErrorCode
= GetLastError();
133 ERR("RevertToPrinterSelf failed with error %lu!\n", dwErrorCode
);
137 // Open the key where our value is stored.
138 dwErrorCode
= (DWORD
)RegOpenKeyExW(HKEY_LOCAL_MACHINE
, L
"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Windows", 0, KEY_SET_VALUE
, &hKey
);
139 if (dwErrorCode
!= ERROR_SUCCESS
)
141 ERR("RegOpenKeyExW failed with status %lu!\n", dwErrorCode
);
145 // We don't use cbInputData here, because the buffer pInputData could be bigger than the data it contains.
146 cbBuffer
= (wcslen((PWSTR
)pInputData
) + 1) * sizeof(WCHAR
);
148 // Write the value to the registry.
149 dwErrorCode
= (DWORD
)RegSetValueExW(hKey
, L
"TransmissionRetryTimeout", 0, REG_SZ
, pInputData
, cbBuffer
);
150 if (dwErrorCode
!= ERROR_SUCCESS
)
152 ERR("RegSetValueExW failed with status %lu!\n", dwErrorCode
);
161 ImpersonatePrinterClient(hToken
);
167 _HandleDeletePort(PLOCALMON_XCV pXcv
, PBYTE pInputData
, PDWORD pcbOutputNeeded
)
172 PLOCALMON_HANDLE pLocalmon
= pXcv
->pLocalmon
;
173 PLOCALMON_PORT pPort
= NULL
;
175 PWSTR pPortName
= (PWSTR
)pInputData
;
177 FIXME("LcmXcvDeletePort : %s\n", debugstr_w( pPortName
) );
181 res
= ERROR_INVALID_PARAMETER
;
185 // This action can only happen at SERVER_ACCESS_ADMINISTER access level.
186 if (!(pXcv
->GrantedAccess
& SERVER_ACCESS_ADMINISTER
))
188 res
= ERROR_ACCESS_DENIED
;
192 // Switch to the SYSTEM context for modifying the registry.
193 hToken
= RevertToPrinterSelf();
196 res
= GetLastError();
197 ERR("RevertToPrinterSelf failed with error %lu!\n", res
);
201 res
= RegOpenKeyW(HKEY_LOCAL_MACHINE
, L
"Software\\Microsoft\\Windows NT\\CurrentVersion\\Ports", &hroot
);
202 if ( res
== ERROR_SUCCESS
)
204 res
= RegDeleteValueW(hroot
, pPortName
);
208 if ( res
== ERROR_SUCCESS
)
210 EnterCriticalSection(&pLocalmon
->Section
);
212 if (!IsListEmpty(&pLocalmon
->RegistryPorts
) )
214 for (pEntry
= pLocalmon
->RegistryPorts
.Flink
; pEntry
!= &pLocalmon
->RegistryPorts
; pEntry
= pEntry
->Flink
)
216 pPort
= CONTAINING_RECORD(pEntry
, LOCALMON_PORT
, Entry
);
218 if (wcscmp(pPort
->pwszPortName
, pPortName
) == 0)
223 LeaveCriticalSection(&pLocalmon
->Section
);
227 FIXME("LcmXcvDeletePort removed Port Entry\n");
228 EnterCriticalSection(&pPort
->pLocalmon
->Section
);
229 RemoveEntryList(&pPort
->Entry
);
230 LeaveCriticalSection(&pPort
->pLocalmon
->Section
);
232 DllFreeSplMem(pPort
);
235 FIXME("LcmXcvDeletePort => %u with %u\n", res
, GetLastError() );
239 if (hToken
) ImpersonatePrinterClient(hToken
);
245 * @name _HandleGetDefaultCommConfig
247 * Gets the default configuration of a legacy port.
248 * The opposite function is _HandleSetDefaultCommConfig.
251 * The port name (without colon!) whose default configuration you want to get.
254 * Pointer to a COMMCONFIG structure that will receive the configuration information.
256 * @param cbOutputData
257 * Size of the variable pointed to by pOutputData.
259 * @param pcbOutputNeeded
260 * Pointer to a DWORD that contains the required size for pOutputData on return.
263 * An error code indicating success or failure.
266 _HandleGetDefaultCommConfig(PBYTE pInputData
, PBYTE pOutputData
, DWORD cbOutputData
, PDWORD pcbOutputNeeded
)
269 if (!pInputData
|| !pcbOutputNeeded
)
270 return ERROR_INVALID_PARAMETER
;
272 *pcbOutputNeeded
= sizeof(COMMCONFIG
);
274 // Check if the supplied buffer is large enough.
275 if (cbOutputData
< *pcbOutputNeeded
)
276 return ERROR_INSUFFICIENT_BUFFER
;
278 // Finally get the port configuration.
279 if (!GetDefaultCommConfigW((PCWSTR
)pInputData
, (LPCOMMCONFIG
)pOutputData
, pcbOutputNeeded
))
280 return GetLastError();
282 return ERROR_SUCCESS
;
286 * @name _HandleGetTransmissionRetryTimeout
288 * Reads the value for "TransmissionRetryTimeout" from the registry and converts it to a DWORD.
289 * The opposite function is _HandleConfigureLPTPortCommandOK.
292 * Pointer to a DWORD that will receive the timeout value.
294 * @param cbOutputData
295 * Size of the variable pointed to by pOutputData.
297 * @param pcbOutputNeeded
298 * Pointer to a DWORD that contains the required size for pOutputData on return.
301 * An error code indicating success or failure.
304 _HandleGetTransmissionRetryTimeout(PBYTE pOutputData
, DWORD cbOutputData
, PDWORD pcbOutputNeeded
)
309 if (!pOutputData
|| !pcbOutputNeeded
)
310 return ERROR_INVALID_PARAMETER
;
312 *pcbOutputNeeded
= sizeof(DWORD
);
314 // Check if the supplied buffer is large enough.
315 if (cbOutputData
< *pcbOutputNeeded
)
316 return ERROR_INSUFFICIENT_BUFFER
;
318 // Retrieve and copy the number.
319 dwTimeout
= GetLPTTransmissionRetryTimeout();
320 CopyMemory(pOutputData
, &dwTimeout
, sizeof(DWORD
));
321 return ERROR_SUCCESS
;
325 * @name _HandleMonitorUI
327 * Returns the filename of the associated UI DLL for this Port Monitor.
330 * Pointer to a Unicode string that will receive the DLL filename.
332 * @param cbOutputData
333 * Size of the variable pointed to by pOutputData.
335 * @param pcbOutputNeeded
336 * Pointer to a DWORD that contains the required size for pOutputData on return.
339 * An error code indicating success or failure.
342 _HandleMonitorUI(PBYTE pOutputData
, DWORD cbOutputData
, PDWORD pcbOutputNeeded
)
344 const WCHAR wszMonitorUI
[] = L
"LocalUI.dll";
347 if (!pcbOutputNeeded
)
348 return ERROR_INVALID_PARAMETER
;
350 *pcbOutputNeeded
= sizeof(wszMonitorUI
);
352 // Check if the supplied buffer is large enough.
353 if (cbOutputData
< *pcbOutputNeeded
)
354 return ERROR_INSUFFICIENT_BUFFER
;
357 return ERROR_INVALID_PARAMETER
;
360 CopyMemory(pOutputData
, wszMonitorUI
, sizeof(wszMonitorUI
));
361 return ERROR_SUCCESS
;
365 * @name _HandlePortExists
367 * Checks all Port Monitors installed on the local system to find out if a given port already exists.
370 * Pointer to a Unicode string specifying the port name to check.
373 * Pointer to a BOOL that receives the result of the check.
375 * @param cbOutputData
376 * Size of the variable pointed to by pOutputData.
378 * @param pcbOutputNeeded
379 * Pointer to a DWORD that contains the required size for pOutputData on return.
382 * An error code indicating success or failure.
385 _HandlePortExists(PBYTE pInputData
, PBYTE pOutputData
, DWORD cbOutputData
, PDWORD pcbOutputNeeded
)
388 if (!pInputData
|| !pOutputData
|| !pcbOutputNeeded
)
389 return ERROR_INVALID_PARAMETER
;
391 *pcbOutputNeeded
= sizeof(BOOL
);
393 // Check if the supplied buffer is large enough.
394 if (cbOutputData
< *pcbOutputNeeded
)
395 return ERROR_INSUFFICIENT_BUFFER
;
397 // Return the check result and error code.
398 *(PBOOL
)pOutputData
= DoesPortExist((PCWSTR
)pInputData
);
399 return GetLastError();
403 _HandlePortIsValid(PBYTE pInputData
, PBYTE pOutputData
, DWORD cbOutputData
, PDWORD pcbOutputNeeded
)
407 TRACE("HandlePortIsValid : pInputData %s\n", debugstr_w( (LPWSTR
) pInputData
));
409 res
= GetTypeFromName((LPCWSTR
) pInputData
);
411 TRACE("HandlePortIsValid : detected as %u\n", res
);
413 /* names, that we have recognized, are valid */
414 if (res
) return ERROR_SUCCESS
;
416 TRACE("=> %u\n", GetLastError());
418 /* ERROR_ACCESS_DENIED, ERROR_PATH_NOT_FOUND or something else */
419 return GetLastError();
423 * @name _HandleSetDefaultCommConfig
425 * Sets the default configuration of a legacy port. Checks for granted SERVER_ACCESS_ADMINISTER access.
426 * You have to supply the port name (with colon!) in XcvOpenPort.
427 * The opposite function is _HandleGetDefaultCommConfig.
430 * Pointer to the LOCALMON_XCV structure of the currently opened Xcv port.
433 * Pointer to the COMMCONFIG structure that shall be passed to SetDefaultCommConfigW.
435 * @param pcbOutputNeeded
436 * Pointer to a DWORD that will be zeroed on return.
439 * An error code indicating success or failure.
442 _HandleSetDefaultCommConfig(PLOCALMON_XCV pXcv
, PBYTE pInputData
, PDWORD pcbOutputNeeded
)
445 HANDLE hToken
= NULL
;
446 LPCOMMCONFIG pCommConfig
;
447 PWSTR pwszPortNameWithoutColon
= NULL
;
450 // pwszObject needs to be at least 2 characters long to be a port name with a trailing colon.
451 if (!pXcv
|| !pXcv
->pwszObject
|| !pXcv
->pwszObject
[0] || !pXcv
->pwszObject
[1] || !pInputData
|| !pcbOutputNeeded
)
453 dwErrorCode
= ERROR_INVALID_PARAMETER
;
457 *pcbOutputNeeded
= 0;
459 // This action can only happen at SERVER_ACCESS_ADMINISTER access level.
460 if (!(pXcv
->GrantedAccess
& SERVER_ACCESS_ADMINISTER
))
462 dwErrorCode
= ERROR_ACCESS_DENIED
;
466 // SetDefaultCommConfigW needs the port name without colon.
467 dwErrorCode
= GetPortNameWithoutColon(pXcv
->pwszObject
, &pwszPortNameWithoutColon
);
468 if (dwErrorCode
!= ERROR_SUCCESS
)
471 // Switch to the SYSTEM context for setting the port configuration.
472 hToken
= RevertToPrinterSelf();
475 dwErrorCode
= GetLastError();
476 ERR("RevertToPrinterSelf failed with error %lu!\n", dwErrorCode
);
480 // Finally pass the parameters to SetDefaultCommConfigW.
481 pCommConfig
= (LPCOMMCONFIG
)pInputData
;
482 if (!SetDefaultCommConfigW(pwszPortNameWithoutColon
, pCommConfig
, pCommConfig
->dwSize
))
484 dwErrorCode
= GetLastError();
485 ERR("SetDefaultCommConfigW failed with error %lu!\n", dwErrorCode
);
489 dwErrorCode
= ERROR_SUCCESS
;
493 ImpersonatePrinterClient(hToken
);
495 if (pwszPortNameWithoutColon
)
496 DllFreeSplMem(pwszPortNameWithoutColon
);
502 LocalmonXcvClosePort(HANDLE hXcv
)
504 PLOCALMON_XCV pXcv
= (PLOCALMON_XCV
)hXcv
;
506 TRACE("LocalmonXcvClosePort(%p)\n", hXcv
);
511 SetLastError(ERROR_INVALID_PARAMETER
);
515 // Remove it from the list and free the memory.
516 EnterCriticalSection(&pXcv
->pLocalmon
->Section
);
517 RemoveEntryList(&pXcv
->Entry
);
518 LeaveCriticalSection(&pXcv
->pLocalmon
->Section
);
521 SetLastError(ERROR_SUCCESS
);
526 LocalmonXcvDataPort(HANDLE hXcv
, PCWSTR pszDataName
, PBYTE pInputData
, DWORD cbInputData
, PBYTE pOutputData
, DWORD cbOutputData
, PDWORD pcbOutputNeeded
)
528 FIXME("LocalmonXcvDataPort(%p, %S, %p, %lu, %p, %lu, %p)\n", hXcv
, pszDataName
, pInputData
, cbInputData
, pOutputData
, cbOutputData
, pcbOutputNeeded
);
532 return ERROR_INVALID_PARAMETER
;
534 // Call the appropriate handler for the requested data name.
535 if (wcscmp(pszDataName
, L
"AddPort") == 0)
536 return _HandleAddPort((PLOCALMON_XCV
)hXcv
, pInputData
, pcbOutputNeeded
);
538 if (wcscmp(pszDataName
, L
"ConfigureLPTPortCommandOK") == 0)
539 return _HandleConfigureLPTPortCommandOK((PLOCALMON_XCV
)hXcv
, pInputData
, pcbOutputNeeded
);
541 if (wcscmp(pszDataName
, L
"DeletePort") == 0)
542 return _HandleDeletePort((PLOCALMON_XCV
)hXcv
, pInputData
, pcbOutputNeeded
);
544 if (wcscmp(pszDataName
, L
"GetDefaultCommConfig") == 0)
545 return _HandleGetDefaultCommConfig(pInputData
, pOutputData
, cbOutputData
, pcbOutputNeeded
);
547 if (wcscmp(pszDataName
, L
"GetTransmissionRetryTimeout") == 0)
548 return _HandleGetTransmissionRetryTimeout(pOutputData
, cbOutputData
, pcbOutputNeeded
);
550 if (wcscmp(pszDataName
, L
"MonitorUI") == 0)
551 return _HandleMonitorUI(pOutputData
, cbOutputData
, pcbOutputNeeded
);
553 if (wcscmp(pszDataName
, L
"PortExists") == 0)
554 return _HandlePortExists(pInputData
, pOutputData
, cbOutputData
, pcbOutputNeeded
);
556 if (wcscmp(pszDataName
, L
"PortIsValid") == 0)
557 return _HandlePortIsValid(pInputData
, pOutputData
, cbOutputData
, pcbOutputNeeded
);
559 if (wcscmp(pszDataName
, L
"SetDefaultCommConfig") == 0)
560 return _HandleSetDefaultCommConfig((PLOCALMON_XCV
)hXcv
, pInputData
, pcbOutputNeeded
);
562 return ERROR_INVALID_PARAMETER
;
566 LocalmonXcvOpenPort(HANDLE hMonitor
, PCWSTR pwszObject
, ACCESS_MASK GrantedAccess
, PHANDLE phXcv
)
570 PLOCALMON_HANDLE pLocalmon
= (PLOCALMON_HANDLE
)hMonitor
;
573 FIXME("LocalmonXcvOpenPort(%p, %S, %lu, %p)\n", hMonitor
, pwszObject
, GrantedAccess
, phXcv
);
576 if (!pLocalmon
|| !phXcv
)
578 dwErrorCode
= ERROR_INVALID_PARAMETER
;
583 cbObject
= (wcslen(pwszObject
) + 1) * sizeof(WCHAR
);
585 // Create a new LOCALMON_XCV structure and fill the relevant fields.
586 pXcv
= DllAllocSplMem(sizeof(LOCALMON_XCV
) + cbObject
);
589 dwErrorCode
= ERROR_NOT_ENOUGH_MEMORY
;
590 ERR("DllAllocSplMem failed with error %lu!\n", GetLastError());
594 pXcv
->pLocalmon
= pLocalmon
;
595 pXcv
->GrantedAccess
= GrantedAccess
;
599 pXcv
->pwszObject
= (PWSTR
)((PBYTE
)pXcv
+ sizeof(LOCALMON_XCV
));
600 CopyMemory(pXcv
->pwszObject
, pwszObject
, cbObject
);
603 InsertTailList(&pLocalmon
->XcvHandles
, &pXcv
->Entry
);
605 // Return it as the Xcv handle.
606 *phXcv
= (HANDLE
)pXcv
;
607 dwErrorCode
= ERROR_SUCCESS
;
610 SetLastError(dwErrorCode
);
611 return (dwErrorCode
== ERROR_SUCCESS
);