[Printing] Fix Typos and Test Regressions
[reactos.git] / win32ss / printing / monitors / localmon / xcv.c
1 /*
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)
6 */
7
8 #include "precomp.h"
9
10 static DWORD
11 _HandleAddPort(PLOCALMON_XCV pXcv, PBYTE pInputData, PDWORD pcbOutputNeeded)
12 {
13 DWORD res, cbPortName;
14 HKEY hroot;
15 HKEY hToken = NULL;
16 PLOCALMON_PORT pPort;
17 PLOCALMON_HANDLE pLocalmon = pXcv->pLocalmon;
18 PWSTR PortName = (PWSTR)pInputData;
19
20 FIXME("LcmXcvAddPort : %s\n", debugstr_w( (LPWSTR) PortName ) );
21
22 if (!pLocalmon )
23 {
24 res = ERROR_INVALID_PARAMETER;
25 goto Cleanup;
26 }
27
28 // This action can only happen at SERVER_ACCESS_ADMINISTER access level.
29 if (!(pXcv->GrantedAccess & SERVER_ACCESS_ADMINISTER))
30 {
31 res = ERROR_ACCESS_DENIED;
32 goto Cleanup;
33 }
34
35 // Switch to the SYSTEM context for modifying the registry.
36 hToken = RevertToPrinterSelf();
37 if (!hToken)
38 {
39 res = GetLastError();
40 ERR("RevertToPrinterSelf failed with error %lu!\n", res);
41 goto Cleanup;
42 }
43
44 res = RegOpenKeyW(HKEY_LOCAL_MACHINE, L"Software\\Microsoft\\Windows NT\\CurrentVersion\\Ports", &hroot);
45 if (res == ERROR_SUCCESS)
46 {
47 if ( DoesPortExist( PortName ) )
48 {
49 RegCloseKey(hroot);
50 FIXME("=> %u\n", ERROR_ALREADY_EXISTS);
51 res = ERROR_ALREADY_EXISTS;
52 goto Cleanup;
53 }
54
55 cbPortName = (wcslen( PortName ) + 1) * sizeof(WCHAR);
56
57 // Create a new LOCALMON_PORT structure for it.
58 pPort = DllAllocSplMem(sizeof(LOCALMON_PORT) + cbPortName);
59 if (!pPort)
60 {
61 res = ERROR_NOT_ENOUGH_MEMORY;
62 RegCloseKey( hroot );
63 goto Cleanup;
64 }
65 memset( pPort, 0, sizeof(LOCALMON_PORT) + cbPortName );
66
67 pPort->hFile = INVALID_HANDLE_VALUE;
68 pPort->pLocalmon = pLocalmon;
69 pPort->pwszPortName = wcscpy( (PWSTR)(pPort+1), PortName );
70
71 // Insert it into the Registry list.
72 InsertTailList(&pLocalmon->RegistryPorts, &pPort->Entry);
73
74 res = RegSetValueExW(hroot, PortName, 0, REG_SZ, (const BYTE *) L"", sizeof(L""));
75 RegCloseKey(hroot);
76 }
77
78 FIXME("=> %u\n", res);
79
80 Cleanup:
81 if (hToken) ImpersonatePrinterClient(hToken);
82 SetLastError(res);
83 return res;
84 }
85
86 /**
87 * @name _HandleConfigureLPTPortCommandOK
88 *
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.
91 *
92 * @param pXcv
93 * Pointer to the LOCALMON_XCV structure of the currently opened Xcv port.
94 *
95 * @param pInputData
96 * Pointer to a Unicode string containing the value to be written to the registry.
97 *
98 * @param pcbOutputNeeded
99 * Pointer to a DWORD that will be zeroed on return.
100 *
101 * @return
102 * An error code indicating success or failure.
103 */
104 static DWORD
105 _HandleConfigureLPTPortCommandOK(PLOCALMON_XCV pXcv, PBYTE pInputData, PDWORD pcbOutputNeeded)
106 {
107 DWORD cbBuffer;
108 DWORD dwErrorCode;
109 HKEY hKey = NULL;
110 HKEY hToken = NULL;
111
112 // Sanity checks
113 if (!pXcv || !pInputData || !pcbOutputNeeded)
114 {
115 dwErrorCode = ERROR_INVALID_PARAMETER;
116 goto Cleanup;
117 }
118
119 *pcbOutputNeeded = 0;
120
121 // This action can only happen at SERVER_ACCESS_ADMINISTER access level.
122 if (!(pXcv->GrantedAccess & SERVER_ACCESS_ADMINISTER))
123 {
124 dwErrorCode = ERROR_ACCESS_DENIED;
125 goto Cleanup;
126 }
127
128 // Switch to the SYSTEM context for modifying the registry.
129 hToken = RevertToPrinterSelf();
130 if (!hToken)
131 {
132 dwErrorCode = GetLastError();
133 ERR("RevertToPrinterSelf failed with error %lu!\n", dwErrorCode);
134 goto Cleanup;
135 }
136
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)
140 {
141 ERR("RegOpenKeyExW failed with status %lu!\n", dwErrorCode);
142 goto Cleanup;
143 }
144
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);
147
148 // Write the value to the registry.
149 dwErrorCode = (DWORD)RegSetValueExW(hKey, L"TransmissionRetryTimeout", 0, REG_SZ, pInputData, cbBuffer);
150 if (dwErrorCode != ERROR_SUCCESS)
151 {
152 ERR("RegSetValueExW failed with status %lu!\n", dwErrorCode);
153 goto Cleanup;
154 }
155
156 Cleanup:
157 if (hKey)
158 RegCloseKey(hKey);
159
160 if (hToken)
161 ImpersonatePrinterClient(hToken);
162
163 return dwErrorCode;
164 }
165
166 static DWORD
167 _HandleDeletePort(PLOCALMON_XCV pXcv, PBYTE pInputData, PDWORD pcbOutputNeeded)
168 {
169 DWORD res;
170 HKEY hroot;
171 HKEY hToken = NULL;
172 PLOCALMON_HANDLE pLocalmon = pXcv->pLocalmon;
173 PLOCALMON_PORT pPort = NULL;
174 PLIST_ENTRY pEntry;
175 PWSTR pPortName = (PWSTR)pInputData;
176
177 FIXME("LcmXcvDeletePort : %s\n", debugstr_w( pPortName ) );
178
179 if (!pLocalmon )
180 {
181 res = ERROR_INVALID_PARAMETER;
182 goto Cleanup;
183 }
184
185 // This action can only happen at SERVER_ACCESS_ADMINISTER access level.
186 if (!(pXcv->GrantedAccess & SERVER_ACCESS_ADMINISTER))
187 {
188 res = ERROR_ACCESS_DENIED;
189 goto Cleanup;
190 }
191
192 // Switch to the SYSTEM context for modifying the registry.
193 hToken = RevertToPrinterSelf();
194 if (!hToken)
195 {
196 res = GetLastError();
197 ERR("RevertToPrinterSelf failed with error %lu!\n", res);
198 goto Cleanup;
199 }
200
201 res = RegOpenKeyW(HKEY_LOCAL_MACHINE, L"Software\\Microsoft\\Windows NT\\CurrentVersion\\Ports", &hroot);
202 if ( res == ERROR_SUCCESS )
203 {
204 res = RegDeleteValueW(hroot, pPortName );
205
206 RegCloseKey(hroot);
207
208 if ( res == ERROR_SUCCESS )
209 {
210 EnterCriticalSection(&pLocalmon->Section);
211
212 if (!IsListEmpty(&pLocalmon->RegistryPorts) )
213 {
214 for (pEntry = pLocalmon->RegistryPorts.Flink; pEntry != &pLocalmon->RegistryPorts; pEntry = pEntry->Flink)
215 {
216 pPort = CONTAINING_RECORD(pEntry, LOCALMON_PORT, Entry);
217
218 if (wcscmp(pPort->pwszPortName, pPortName) == 0)
219 break;
220 }
221 }
222
223 LeaveCriticalSection(&pLocalmon->Section);
224
225 if ( pPort )
226 {
227 FIXME("LcmXcvDeletePort removed Port Entry\n");
228 EnterCriticalSection(&pPort->pLocalmon->Section);
229 RemoveEntryList(&pPort->Entry);
230 LeaveCriticalSection(&pPort->pLocalmon->Section);
231
232 DllFreeSplMem(pPort);
233 }
234 }
235 FIXME("LcmXcvDeletePort => %u with %u\n", res, GetLastError() );
236 }
237
238 Cleanup:
239 if (hToken) ImpersonatePrinterClient(hToken);
240 SetLastError(res);
241 return res;
242 }
243
244 /**
245 * @name _HandleGetDefaultCommConfig
246 *
247 * Gets the default configuration of a legacy port.
248 * The opposite function is _HandleSetDefaultCommConfig.
249 *
250 * @param pInputData
251 * The port name (without colon!) whose default configuration you want to get.
252 *
253 * @param pOutputData
254 * Pointer to a COMMCONFIG structure that will receive the configuration information.
255 *
256 * @param cbOutputData
257 * Size of the variable pointed to by pOutputData.
258 *
259 * @param pcbOutputNeeded
260 * Pointer to a DWORD that contains the required size for pOutputData on return.
261 *
262 * @return
263 * An error code indicating success or failure.
264 */
265 static DWORD
266 _HandleGetDefaultCommConfig(PBYTE pInputData, PBYTE pOutputData, DWORD cbOutputData, PDWORD pcbOutputNeeded)
267 {
268 // Sanity checks
269 if (!pInputData || !pcbOutputNeeded)
270 return ERROR_INVALID_PARAMETER;
271
272 *pcbOutputNeeded = sizeof(COMMCONFIG);
273
274 // Check if the supplied buffer is large enough.
275 if (cbOutputData < *pcbOutputNeeded)
276 return ERROR_INSUFFICIENT_BUFFER;
277
278 // Finally get the port configuration.
279 if (!GetDefaultCommConfigW((PCWSTR)pInputData, (LPCOMMCONFIG)pOutputData, pcbOutputNeeded))
280 return GetLastError();
281
282 return ERROR_SUCCESS;
283 }
284
285 /**
286 * @name _HandleGetTransmissionRetryTimeout
287 *
288 * Reads the value for "TransmissionRetryTimeout" from the registry and converts it to a DWORD.
289 * The opposite function is _HandleConfigureLPTPortCommandOK.
290 *
291 * @param pOutputData
292 * Pointer to a DWORD that will receive the timeout value.
293 *
294 * @param cbOutputData
295 * Size of the variable pointed to by pOutputData.
296 *
297 * @param pcbOutputNeeded
298 * Pointer to a DWORD that contains the required size for pOutputData on return.
299 *
300 * @return
301 * An error code indicating success or failure.
302 */
303 static DWORD
304 _HandleGetTransmissionRetryTimeout(PBYTE pOutputData, DWORD cbOutputData, PDWORD pcbOutputNeeded)
305 {
306 DWORD dwTimeout;
307
308 // Sanity checks
309 if (!pOutputData || !pcbOutputNeeded)
310 return ERROR_INVALID_PARAMETER;
311
312 *pcbOutputNeeded = sizeof(DWORD);
313
314 // Check if the supplied buffer is large enough.
315 if (cbOutputData < *pcbOutputNeeded)
316 return ERROR_INSUFFICIENT_BUFFER;
317
318 // Retrieve and copy the number.
319 dwTimeout = GetLPTTransmissionRetryTimeout();
320 CopyMemory(pOutputData, &dwTimeout, sizeof(DWORD));
321 return ERROR_SUCCESS;
322 }
323
324 /**
325 * @name _HandleMonitorUI
326 *
327 * Returns the filename of the associated UI DLL for this Port Monitor.
328 *
329 * @param pOutputData
330 * Pointer to a Unicode string that will receive the DLL filename.
331 *
332 * @param cbOutputData
333 * Size of the variable pointed to by pOutputData.
334 *
335 * @param pcbOutputNeeded
336 * Pointer to a DWORD that contains the required size for pOutputData on return.
337 *
338 * @return
339 * An error code indicating success or failure.
340 */
341 static DWORD
342 _HandleMonitorUI(PBYTE pOutputData, DWORD cbOutputData, PDWORD pcbOutputNeeded)
343 {
344 const WCHAR wszMonitorUI[] = L"LocalUI.dll";
345
346 // Sanity checks
347 if (!pcbOutputNeeded)
348 return ERROR_INVALID_PARAMETER;
349
350 *pcbOutputNeeded = sizeof(wszMonitorUI);
351
352 // Check if the supplied buffer is large enough.
353 if (cbOutputData < *pcbOutputNeeded)
354 return ERROR_INSUFFICIENT_BUFFER;
355
356 if (!pOutputData)
357 return ERROR_INVALID_PARAMETER;
358
359 // Copy the string.
360 CopyMemory(pOutputData, wszMonitorUI, sizeof(wszMonitorUI));
361 return ERROR_SUCCESS;
362 }
363
364 /**
365 * @name _HandlePortExists
366 *
367 * Checks all Port Monitors installed on the local system to find out if a given port already exists.
368 *
369 * @param pInputData
370 * Pointer to a Unicode string specifying the port name to check.
371 *
372 * @param pOutputData
373 * Pointer to a BOOL that receives the result of the check.
374 *
375 * @param cbOutputData
376 * Size of the variable pointed to by pOutputData.
377 *
378 * @param pcbOutputNeeded
379 * Pointer to a DWORD that contains the required size for pOutputData on return.
380 *
381 * @return
382 * An error code indicating success or failure.
383 */
384 static DWORD
385 _HandlePortExists(PBYTE pInputData, PBYTE pOutputData, DWORD cbOutputData, PDWORD pcbOutputNeeded)
386 {
387 // Sanity checks
388 if (!pInputData || !pOutputData || !pcbOutputNeeded)
389 return ERROR_INVALID_PARAMETER;
390
391 *pcbOutputNeeded = sizeof(BOOL);
392
393 // Check if the supplied buffer is large enough.
394 if (cbOutputData < *pcbOutputNeeded)
395 return ERROR_INSUFFICIENT_BUFFER;
396
397 // Return the check result and error code.
398 *(PBOOL)pOutputData = DoesPortExist((PCWSTR)pInputData);
399 return GetLastError();
400 }
401
402 static DWORD
403 _HandlePortIsValid(PBYTE pInputData, PBYTE pOutputData, DWORD cbOutputData, PDWORD pcbOutputNeeded)
404 {
405 DWORD res;
406
407 TRACE("HandlePortIsValid : pInputData %s\n", debugstr_w( (LPWSTR) pInputData));
408
409 res = GetTypeFromName((LPCWSTR) pInputData);
410
411 TRACE("HandlePortIsValid : detected as %u\n", res);
412
413 /* names, that we have recognized, are valid */
414 if (res) return ERROR_SUCCESS;
415
416 TRACE("=> %u\n", GetLastError());
417
418 /* ERROR_ACCESS_DENIED, ERROR_PATH_NOT_FOUND or something else */
419 return GetLastError();
420 }
421
422 /**
423 * @name _HandleSetDefaultCommConfig
424 *
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.
428 *
429 * @param pXcv
430 * Pointer to the LOCALMON_XCV structure of the currently opened Xcv port.
431 *
432 * @param pInputData
433 * Pointer to the COMMCONFIG structure that shall be passed to SetDefaultCommConfigW.
434 *
435 * @param pcbOutputNeeded
436 * Pointer to a DWORD that will be zeroed on return.
437 *
438 * @return
439 * An error code indicating success or failure.
440 */
441 static DWORD
442 _HandleSetDefaultCommConfig(PLOCALMON_XCV pXcv, PBYTE pInputData, PDWORD pcbOutputNeeded)
443 {
444 DWORD dwErrorCode;
445 HANDLE hToken = NULL;
446 LPCOMMCONFIG pCommConfig;
447 PWSTR pwszPortNameWithoutColon = NULL;
448
449 // Sanity checks
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)
452 {
453 dwErrorCode = ERROR_INVALID_PARAMETER;
454 goto Cleanup;
455 }
456
457 *pcbOutputNeeded = 0;
458
459 // This action can only happen at SERVER_ACCESS_ADMINISTER access level.
460 if (!(pXcv->GrantedAccess & SERVER_ACCESS_ADMINISTER))
461 {
462 dwErrorCode = ERROR_ACCESS_DENIED;
463 goto Cleanup;
464 }
465
466 // SetDefaultCommConfigW needs the port name without colon.
467 dwErrorCode = GetPortNameWithoutColon(pXcv->pwszObject, &pwszPortNameWithoutColon);
468 if (dwErrorCode != ERROR_SUCCESS)
469 goto Cleanup;
470
471 // Switch to the SYSTEM context for setting the port configuration.
472 hToken = RevertToPrinterSelf();
473 if (!hToken)
474 {
475 dwErrorCode = GetLastError();
476 ERR("RevertToPrinterSelf failed with error %lu!\n", dwErrorCode);
477 goto Cleanup;
478 }
479
480 // Finally pass the parameters to SetDefaultCommConfigW.
481 pCommConfig = (LPCOMMCONFIG)pInputData;
482 if (!SetDefaultCommConfigW(pwszPortNameWithoutColon, pCommConfig, pCommConfig->dwSize))
483 {
484 dwErrorCode = GetLastError();
485 ERR("SetDefaultCommConfigW failed with error %lu!\n", dwErrorCode);
486 goto Cleanup;
487 }
488
489 dwErrorCode = ERROR_SUCCESS;
490
491 Cleanup:
492 if (hToken)
493 ImpersonatePrinterClient(hToken);
494
495 if (pwszPortNameWithoutColon)
496 DllFreeSplMem(pwszPortNameWithoutColon);
497
498 return dwErrorCode;
499 }
500
501 BOOL WINAPI
502 LocalmonXcvClosePort(HANDLE hXcv)
503 {
504 PLOCALMON_XCV pXcv = (PLOCALMON_XCV)hXcv;
505
506 TRACE("LocalmonXcvClosePort(%p)\n", hXcv);
507
508 // Sanity checks
509 if (!pXcv)
510 {
511 SetLastError(ERROR_INVALID_PARAMETER);
512 return FALSE;
513 }
514
515 // Remove it from the list and free the memory.
516 EnterCriticalSection(&pXcv->pLocalmon->Section);
517 RemoveEntryList(&pXcv->Entry);
518 LeaveCriticalSection(&pXcv->pLocalmon->Section);
519 DllFreeSplMem(pXcv);
520
521 SetLastError(ERROR_SUCCESS);
522 return TRUE;
523 }
524
525 DWORD WINAPI
526 LocalmonXcvDataPort(HANDLE hXcv, PCWSTR pszDataName, PBYTE pInputData, DWORD cbInputData, PBYTE pOutputData, DWORD cbOutputData, PDWORD pcbOutputNeeded)
527 {
528 FIXME("LocalmonXcvDataPort(%p, %S, %p, %lu, %p, %lu, %p)\n", hXcv, pszDataName, pInputData, cbInputData, pOutputData, cbOutputData, pcbOutputNeeded);
529
530 // Sanity checks
531 if (!pszDataName)
532 return ERROR_INVALID_PARAMETER;
533
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);
537
538 if (wcscmp(pszDataName, L"ConfigureLPTPortCommandOK") == 0)
539 return _HandleConfigureLPTPortCommandOK((PLOCALMON_XCV)hXcv, pInputData, pcbOutputNeeded);
540
541 if (wcscmp(pszDataName, L"DeletePort") == 0)
542 return _HandleDeletePort((PLOCALMON_XCV)hXcv, pInputData, pcbOutputNeeded);
543
544 if (wcscmp(pszDataName, L"GetDefaultCommConfig") == 0)
545 return _HandleGetDefaultCommConfig(pInputData, pOutputData, cbOutputData, pcbOutputNeeded);
546
547 if (wcscmp(pszDataName, L"GetTransmissionRetryTimeout") == 0)
548 return _HandleGetTransmissionRetryTimeout(pOutputData, cbOutputData, pcbOutputNeeded);
549
550 if (wcscmp(pszDataName, L"MonitorUI") == 0)
551 return _HandleMonitorUI(pOutputData, cbOutputData, pcbOutputNeeded);
552
553 if (wcscmp(pszDataName, L"PortExists") == 0)
554 return _HandlePortExists(pInputData, pOutputData, cbOutputData, pcbOutputNeeded);
555
556 if (wcscmp(pszDataName, L"PortIsValid") == 0)
557 return _HandlePortIsValid(pInputData, pOutputData, cbOutputData, pcbOutputNeeded);
558
559 if (wcscmp(pszDataName, L"SetDefaultCommConfig") == 0)
560 return _HandleSetDefaultCommConfig((PLOCALMON_XCV)hXcv, pInputData, pcbOutputNeeded);
561
562 return ERROR_INVALID_PARAMETER;
563 }
564
565 BOOL WINAPI
566 LocalmonXcvOpenPort(HANDLE hMonitor, PCWSTR pwszObject, ACCESS_MASK GrantedAccess, PHANDLE phXcv)
567 {
568 DWORD cbObject = 0;
569 DWORD dwErrorCode;
570 PLOCALMON_HANDLE pLocalmon = (PLOCALMON_HANDLE)hMonitor;
571 PLOCALMON_XCV pXcv;
572
573 FIXME("LocalmonXcvOpenPort(%p, %S, %lu, %p)\n", hMonitor, pwszObject, GrantedAccess, phXcv);
574
575 // Sanity checks
576 if (!pLocalmon || !phXcv)
577 {
578 dwErrorCode = ERROR_INVALID_PARAMETER;
579 goto Cleanup;
580 }
581
582 if (pwszObject)
583 cbObject = (wcslen(pwszObject) + 1) * sizeof(WCHAR);
584
585 // Create a new LOCALMON_XCV structure and fill the relevant fields.
586 pXcv = DllAllocSplMem(sizeof(LOCALMON_XCV) + cbObject);
587 if (!pXcv)
588 {
589 dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
590 ERR("DllAllocSplMem failed with error %lu!\n", GetLastError());
591 goto Cleanup;
592 }
593
594 pXcv->pLocalmon = pLocalmon;
595 pXcv->GrantedAccess = GrantedAccess;
596
597 if (cbObject)
598 {
599 pXcv->pwszObject = (PWSTR)((PBYTE)pXcv + sizeof(LOCALMON_XCV));
600 CopyMemory(pXcv->pwszObject, pwszObject, cbObject);
601 }
602
603 InsertTailList(&pLocalmon->XcvHandles, &pXcv->Entry);
604
605 // Return it as the Xcv handle.
606 *phXcv = (HANDLE)pXcv;
607 dwErrorCode = ERROR_SUCCESS;
608
609 Cleanup:
610 SetLastError(dwErrorCode);
611 return (dwErrorCode == ERROR_SUCCESS);
612 }