[LOCALUI] Sync with Wine Staging 3.3. CORE-14434
[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 return ERROR_CALL_NOT_IMPLEMENTED;
14 }
15
16 /**
17 * @name _HandleConfigureLPTPortCommandOK
18 *
19 * Writes the value for "TransmissionRetryTimeout" to the registry. Checks for granted SERVER_ACCESS_ADMINISTER access.
20 * Actually the opposite of _HandleGetTransmissionRetryTimeout, but name kept for compatibility.
21 *
22 * @param pXcv
23 * Pointer to the LOCALMON_XCV structure of the currently opened Xcv port.
24 *
25 * @param pInputData
26 * Pointer to a Unicode string containing the value to be written to the registry.
27 *
28 * @param pcbOutputNeeded
29 * Pointer to a DWORD that will be zeroed on return.
30 *
31 * @return
32 * An error code indicating success or failure.
33 */
34 static DWORD
35 _HandleConfigureLPTPortCommandOK(PLOCALMON_XCV pXcv, PBYTE pInputData, PDWORD pcbOutputNeeded)
36 {
37 DWORD cbBuffer;
38 DWORD dwErrorCode;
39 HKEY hKey = NULL;
40 HKEY hToken = NULL;
41
42 // Sanity checks
43 if (!pXcv || !pInputData || !pcbOutputNeeded)
44 {
45 dwErrorCode = ERROR_INVALID_PARAMETER;
46 goto Cleanup;
47 }
48
49 *pcbOutputNeeded = 0;
50
51 // This action can only happen at SERVER_ACCESS_ADMINISTER access level.
52 if (!(pXcv->GrantedAccess & SERVER_ACCESS_ADMINISTER))
53 {
54 dwErrorCode = ERROR_ACCESS_DENIED;
55 goto Cleanup;
56 }
57
58 // Switch to the SYSTEM context for modifying the registry.
59 hToken = RevertToPrinterSelf();
60 if (!hToken)
61 {
62 dwErrorCode = GetLastError();
63 ERR("RevertToPrinterSelf failed with error %lu!\n", dwErrorCode);
64 goto Cleanup;
65 }
66
67 // Open the key where our value is stored.
68 dwErrorCode = (DWORD)RegOpenKeyExW(HKEY_LOCAL_MACHINE, L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Windows", 0, KEY_SET_VALUE, &hKey);
69 if (dwErrorCode != ERROR_SUCCESS)
70 {
71 ERR("RegOpenKeyExW failed with status %lu!\n", dwErrorCode);
72 goto Cleanup;
73 }
74
75 // We don't use cbInputData here, because the buffer pInputData could be bigger than the data it contains.
76 cbBuffer = (wcslen((PWSTR)pInputData) + 1) * sizeof(WCHAR);
77
78 // Write the value to the registry.
79 dwErrorCode = (DWORD)RegSetValueExW(hKey, L"TransmissionRetryTimeout", 0, REG_SZ, pInputData, cbBuffer);
80 if (dwErrorCode != ERROR_SUCCESS)
81 {
82 ERR("RegSetValueExW failed with status %lu!\n", dwErrorCode);
83 goto Cleanup;
84 }
85
86 Cleanup:
87 if (hKey)
88 RegCloseKey(hKey);
89
90 if (hToken)
91 ImpersonatePrinterClient(hToken);
92
93 return dwErrorCode;
94 }
95
96 static DWORD
97 _HandleDeletePort(PLOCALMON_XCV pXcv, PBYTE pInputData, PDWORD pcbOutputNeeded)
98 {
99 return ERROR_CALL_NOT_IMPLEMENTED;
100 }
101
102 /**
103 * @name _HandleGetDefaultCommConfig
104 *
105 * Gets the default configuration of a legacy port.
106 * The opposite function is _HandleSetDefaultCommConfig.
107 *
108 * @param pInputData
109 * The port name (without colon!) whose default configuration you want to get.
110 *
111 * @param pOutputData
112 * Pointer to a COMMCONFIG structure that will receive the configuration information.
113 *
114 * @param cbOutputData
115 * Size of the variable pointed to by pOutputData.
116 *
117 * @param pcbOutputNeeded
118 * Pointer to a DWORD that contains the required size for pOutputData on return.
119 *
120 * @return
121 * An error code indicating success or failure.
122 */
123 static DWORD
124 _HandleGetDefaultCommConfig(PBYTE pInputData, PBYTE pOutputData, DWORD cbOutputData, PDWORD pcbOutputNeeded)
125 {
126 // Sanity checks
127 if (!pInputData || !pcbOutputNeeded)
128 return ERROR_INVALID_PARAMETER;
129
130 *pcbOutputNeeded = sizeof(COMMCONFIG);
131
132 // Check if the supplied buffer is large enough.
133 if (cbOutputData < *pcbOutputNeeded)
134 return ERROR_INSUFFICIENT_BUFFER;
135
136 // Finally get the port configuration.
137 if (!GetDefaultCommConfigW((PCWSTR)pInputData, (LPCOMMCONFIG)pOutputData, pcbOutputNeeded))
138 return GetLastError();
139
140 return ERROR_SUCCESS;
141 }
142
143 /**
144 * @name _HandleGetTransmissionRetryTimeout
145 *
146 * Reads the value for "TransmissionRetryTimeout" from the registry and converts it to a DWORD.
147 * The opposite function is _HandleConfigureLPTPortCommandOK.
148 *
149 * @param pOutputData
150 * Pointer to a DWORD that will receive the timeout value.
151 *
152 * @param cbOutputData
153 * Size of the variable pointed to by pOutputData.
154 *
155 * @param pcbOutputNeeded
156 * Pointer to a DWORD that contains the required size for pOutputData on return.
157 *
158 * @return
159 * An error code indicating success or failure.
160 */
161 static DWORD
162 _HandleGetTransmissionRetryTimeout(PBYTE pOutputData, DWORD cbOutputData, PDWORD pcbOutputNeeded)
163 {
164 DWORD dwTimeout;
165
166 // Sanity checks
167 if (!pOutputData || !pcbOutputNeeded)
168 return ERROR_INVALID_PARAMETER;
169
170 *pcbOutputNeeded = sizeof(DWORD);
171
172 // Check if the supplied buffer is large enough.
173 if (cbOutputData < *pcbOutputNeeded)
174 return ERROR_INSUFFICIENT_BUFFER;
175
176 // Retrieve and copy the number.
177 dwTimeout = GetLPTTransmissionRetryTimeout();
178 CopyMemory(pOutputData, &dwTimeout, sizeof(DWORD));
179 return ERROR_SUCCESS;
180 }
181
182 /**
183 * @name _HandleMonitorUI
184 *
185 * Returns the filename of the associated UI DLL for this Port Monitor.
186 *
187 * @param pOutputData
188 * Pointer to a Unicode string that will receive the DLL filename.
189 *
190 * @param cbOutputData
191 * Size of the variable pointed to by pOutputData.
192 *
193 * @param pcbOutputNeeded
194 * Pointer to a DWORD that contains the required size for pOutputData on return.
195 *
196 * @return
197 * An error code indicating success or failure.
198 */
199 static DWORD
200 _HandleMonitorUI(PBYTE pOutputData, DWORD cbOutputData, PDWORD pcbOutputNeeded)
201 {
202 const WCHAR wszMonitorUI[] = L"LocalUI.dll";
203
204 // Sanity checks
205 if (!pOutputData || !pcbOutputNeeded)
206 return ERROR_INVALID_PARAMETER;
207
208 *pcbOutputNeeded = sizeof(wszMonitorUI);
209
210 // Check if the supplied buffer is large enough.
211 if (cbOutputData < *pcbOutputNeeded)
212 return ERROR_INSUFFICIENT_BUFFER;
213
214 // Copy the string.
215 CopyMemory(pOutputData, wszMonitorUI, sizeof(wszMonitorUI));
216 return ERROR_SUCCESS;
217 }
218
219 /**
220 * @name _HandlePortExists
221 *
222 * Checks all Port Monitors installed on the local system to find out if a given port already exists.
223 *
224 * @param pInputData
225 * Pointer to a Unicode string specifying the port name to check.
226 *
227 * @param pOutputData
228 * Pointer to a BOOL that receives the result of the check.
229 *
230 * @param cbOutputData
231 * Size of the variable pointed to by pOutputData.
232 *
233 * @param pcbOutputNeeded
234 * Pointer to a DWORD that contains the required size for pOutputData on return.
235 *
236 * @return
237 * An error code indicating success or failure.
238 */
239 static DWORD
240 _HandlePortExists(PBYTE pInputData, PBYTE pOutputData, DWORD cbOutputData, PDWORD pcbOutputNeeded)
241 {
242 // Sanity checks
243 if (!pInputData || !pOutputData || !pcbOutputNeeded)
244 return ERROR_INVALID_PARAMETER;
245
246 *pcbOutputNeeded = sizeof(BOOL);
247
248 // Check if the supplied buffer is large enough.
249 if (cbOutputData < *pcbOutputNeeded)
250 return ERROR_INSUFFICIENT_BUFFER;
251
252 // Return the check result and error code.
253 *(PBOOL)pOutputData = DoesPortExist((PCWSTR)pInputData);
254 return GetLastError();
255 }
256
257 static DWORD
258 _HandlePortIsValid(PBYTE pInputData, PBYTE pOutputData, DWORD cbOutputData, PDWORD pcbOutputNeeded)
259 {
260 return ERROR_CALL_NOT_IMPLEMENTED;
261 }
262
263 /**
264 * @name _HandleSetDefaultCommConfig
265 *
266 * Sets the default configuration of a legacy port. Checks for granted SERVER_ACCESS_ADMINISTER access.
267 * You have to supply the port name (with colon!) in XcvOpenPort.
268 * The opposite function is _HandleGetDefaultCommConfig.
269 *
270 * @param pXcv
271 * Pointer to the LOCALMON_XCV structure of the currently opened Xcv port.
272 *
273 * @param pInputData
274 * Pointer to the COMMCONFIG structure that shall be passed to SetDefaultCommConfigW.
275 *
276 * @param pcbOutputNeeded
277 * Pointer to a DWORD that will be zeroed on return.
278 *
279 * @return
280 * An error code indicating success or failure.
281 */
282 static DWORD
283 _HandleSetDefaultCommConfig(PLOCALMON_XCV pXcv, PBYTE pInputData, PDWORD pcbOutputNeeded)
284 {
285 DWORD dwErrorCode;
286 HANDLE hToken = NULL;
287 LPCOMMCONFIG pCommConfig;
288 PWSTR pwszPortNameWithoutColon = NULL;
289
290 // Sanity checks
291 // pwszObject needs to be at least 2 characters long to be a port name with a trailing colon.
292 if (!pXcv || !pXcv->pwszObject || !pXcv->pwszObject[0] || !pXcv->pwszObject[1] || !pInputData || !pcbOutputNeeded)
293 {
294 dwErrorCode = ERROR_INVALID_PARAMETER;
295 goto Cleanup;
296 }
297
298 *pcbOutputNeeded = 0;
299
300 // This action can only happen at SERVER_ACCESS_ADMINISTER access level.
301 if (!(pXcv->GrantedAccess & SERVER_ACCESS_ADMINISTER))
302 {
303 dwErrorCode = ERROR_ACCESS_DENIED;
304 goto Cleanup;
305 }
306
307 // SetDefaultCommConfigW needs the port name without colon.
308 dwErrorCode = GetPortNameWithoutColon(pXcv->pwszObject, &pwszPortNameWithoutColon);
309 if (dwErrorCode != ERROR_SUCCESS)
310 goto Cleanup;
311
312 // Switch to the SYSTEM context for setting the port configuration.
313 hToken = RevertToPrinterSelf();
314 if (!hToken)
315 {
316 dwErrorCode = GetLastError();
317 ERR("RevertToPrinterSelf failed with error %lu!\n", dwErrorCode);
318 goto Cleanup;
319 }
320
321 // Finally pass the parameters to SetDefaultCommConfigW.
322 pCommConfig = (LPCOMMCONFIG)pInputData;
323 if (!SetDefaultCommConfigW(pwszPortNameWithoutColon, pCommConfig, pCommConfig->dwSize))
324 {
325 dwErrorCode = GetLastError();
326 ERR("SetDefaultCommConfigW failed with error %lu!\n", dwErrorCode);
327 goto Cleanup;
328 }
329
330 dwErrorCode = ERROR_SUCCESS;
331
332 Cleanup:
333 if (hToken)
334 ImpersonatePrinterClient(hToken);
335
336 if (pwszPortNameWithoutColon)
337 DllFreeSplMem(pwszPortNameWithoutColon);
338
339 return dwErrorCode;
340 }
341
342 BOOL WINAPI
343 LocalmonXcvClosePort(HANDLE hXcv)
344 {
345 PLOCALMON_XCV pXcv = (PLOCALMON_XCV)hXcv;
346
347 TRACE("LocalmonXcvClosePort(%p)\n", hXcv);
348
349 // Sanity checks
350 if (!pXcv)
351 {
352 SetLastError(ERROR_INVALID_PARAMETER);
353 return FALSE;
354 }
355
356 // Remove it from the list and free the memory.
357 RemoveEntryList(&pXcv->Entry);
358 DllFreeSplMem(pXcv);
359
360 SetLastError(ERROR_SUCCESS);
361 return TRUE;
362 }
363
364 DWORD WINAPI
365 LocalmonXcvDataPort(HANDLE hXcv, PCWSTR pszDataName, PBYTE pInputData, DWORD cbInputData, PBYTE pOutputData, DWORD cbOutputData, PDWORD pcbOutputNeeded)
366 {
367 TRACE("LocalmonXcvDataPort(%p, %S, %p, %lu, %p, %lu, %p)\n", hXcv, pszDataName, pInputData, cbInputData, pOutputData, cbOutputData, pcbOutputNeeded);
368
369 // Sanity checks
370 if (!pszDataName)
371 return ERROR_INVALID_PARAMETER;
372
373 // Call the appropriate handler for the requested data name.
374 if (wcscmp(pszDataName, L"AddPort") == 0)
375 return _HandleAddPort((PLOCALMON_XCV)hXcv, pInputData, pcbOutputNeeded);
376
377 if (wcscmp(pszDataName, L"ConfigureLPTPortCommandOK") == 0)
378 return _HandleConfigureLPTPortCommandOK((PLOCALMON_XCV)hXcv, pInputData, pcbOutputNeeded);
379
380 if (wcscmp(pszDataName, L"DeletePort") == 0)
381 return _HandleDeletePort((PLOCALMON_XCV)hXcv, pInputData, pcbOutputNeeded);
382
383 if (wcscmp(pszDataName, L"GetDefaultCommConfig") == 0)
384 return _HandleGetDefaultCommConfig(pInputData, pOutputData, cbOutputData, pcbOutputNeeded);
385
386 if (wcscmp(pszDataName, L"GetTransmissionRetryTimeout") == 0)
387 return _HandleGetTransmissionRetryTimeout(pOutputData, cbOutputData, pcbOutputNeeded);
388
389 if (wcscmp(pszDataName, L"MonitorUI") == 0)
390 return _HandleMonitorUI(pOutputData, cbOutputData, pcbOutputNeeded);
391
392 if (wcscmp(pszDataName, L"PortExists") == 0)
393 return _HandlePortExists(pInputData, pOutputData, cbOutputData, pcbOutputNeeded);
394
395 if (wcscmp(pszDataName, L"PortIsValid") == 0)
396 return _HandlePortIsValid(pInputData, pOutputData, cbOutputData, pcbOutputNeeded);
397
398 if (wcscmp(pszDataName, L"SetDefaultCommConfig") == 0)
399 return _HandleSetDefaultCommConfig((PLOCALMON_XCV)hXcv, pInputData, pcbOutputNeeded);
400
401 return ERROR_INVALID_PARAMETER;
402 }
403
404 BOOL WINAPI
405 LocalmonXcvOpenPort(HANDLE hMonitor, PCWSTR pwszObject, ACCESS_MASK GrantedAccess, PHANDLE phXcv)
406 {
407 DWORD cbObject = 0;
408 DWORD dwErrorCode;
409 PLOCALMON_HANDLE pLocalmon = (PLOCALMON_HANDLE)hMonitor;
410 PLOCALMON_XCV pXcv;
411
412 TRACE("LocalmonXcvOpenPort(%p, %S, %lu, %p)\n", hMonitor, pwszObject, GrantedAccess, phXcv);
413
414 // Sanity checks
415 if (!pLocalmon || !phXcv)
416 {
417 dwErrorCode = ERROR_INVALID_PARAMETER;
418 goto Cleanup;
419 }
420
421 if (pwszObject)
422 cbObject = (wcslen(pwszObject) + 1) * sizeof(WCHAR);
423
424 // Create a new LOCALMON_XCV structure and fill the relevant fields.
425 pXcv = DllAllocSplMem(sizeof(LOCALMON_XCV) + cbObject);
426 if (!pXcv)
427 {
428 dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
429 ERR("DllAllocSplMem failed with error %lu!\n", GetLastError());
430 goto Cleanup;
431 }
432
433 pXcv->pLocalmon = pLocalmon;
434 pXcv->GrantedAccess = GrantedAccess;
435
436 if (cbObject)
437 {
438 pXcv->pwszObject = (PWSTR)((PBYTE)pXcv + sizeof(LOCALMON_XCV));
439 CopyMemory(pXcv->pwszObject, pwszObject, cbObject);
440 }
441
442 InsertTailList(&pLocalmon->XcvHandles, &pXcv->Entry);
443
444 // Return it as the Xcv handle.
445 *phXcv = (HANDLE)pXcv;
446 dwErrorCode = ERROR_SUCCESS;
447
448 Cleanup:
449 SetLastError(dwErrorCode);
450 return (dwErrorCode == ERROR_SUCCESS);
451 }