FreeRDP
Loading...
Searching...
No Matches
server/ainput_main.c
1
21#include <freerdp/config.h>
22
23#include <stdio.h>
24#include <stdlib.h>
25#include <string.h>
26
27#include <winpr/crt.h>
28#include <winpr/assert.h>
29#include <winpr/synch.h>
30#include <winpr/thread.h>
31#include <winpr/stream.h>
32#include <winpr/sysinfo.h>
33
34#include <freerdp/freerdp.h>
35#include <freerdp/channels/ainput.h>
36#include <freerdp/server/ainput.h>
37#include <freerdp/channels/log.h>
38
39#include "../common/ainput_common.h"
40
41#define TAG CHANNELS_TAG("ainput.server")
42
43typedef enum
44{
45 AINPUT_INITIAL,
46 AINPUT_OPENED,
47 AINPUT_VERSION_SENT,
48} eAInputChannelState;
49
50typedef struct
51{
52 ainput_server_context context;
53
54 BOOL opened;
55
56 HANDLE stopEvent;
57
58 HANDLE thread;
59 void* ainput_channel;
60
61 DWORD SessionId;
62
63 BOOL isOpened;
64 BOOL externalThread;
65
66 /* Channel state */
67 eAInputChannelState state;
68
69 wStream* buffer;
70} ainput_server;
71
72static UINT ainput_server_context_poll(ainput_server_context* context);
73static BOOL ainput_server_context_handle(ainput_server_context* context, HANDLE* handle);
74static UINT ainput_server_context_poll_int(ainput_server_context* context);
75
76static BOOL ainput_server_is_open(ainput_server_context* context)
77{
78 ainput_server* ainput = (ainput_server*)context;
79
80 WINPR_ASSERT(ainput);
81 return ainput->isOpened;
82}
83
89static UINT ainput_server_open_channel(ainput_server* ainput)
90{
91 DWORD Error = 0;
92 HANDLE hEvent = NULL;
93 DWORD StartTick = 0;
94 DWORD BytesReturned = 0;
95 PULONG pSessionId = NULL;
96
97 WINPR_ASSERT(ainput);
98
99 if (WTSQuerySessionInformationA(ainput->context.vcm, WTS_CURRENT_SESSION, WTSSessionId,
100 (LPSTR*)&pSessionId, &BytesReturned) == FALSE)
101 {
102 WLog_ERR(TAG, "WTSQuerySessionInformationA failed!");
103 return ERROR_INTERNAL_ERROR;
104 }
105
106 ainput->SessionId = (DWORD)*pSessionId;
107 WTSFreeMemory(pSessionId);
108 hEvent = WTSVirtualChannelManagerGetEventHandle(ainput->context.vcm);
109 StartTick = GetTickCount();
110
111 while (ainput->ainput_channel == NULL)
112 {
113 if (WaitForSingleObject(hEvent, 1000) == WAIT_FAILED)
114 {
115 Error = GetLastError();
116 WLog_ERR(TAG, "WaitForSingleObject failed with error %" PRIu32 "!", Error);
117 return Error;
118 }
119
120 ainput->ainput_channel = WTSVirtualChannelOpenEx(ainput->SessionId, AINPUT_DVC_CHANNEL_NAME,
121 WTS_CHANNEL_OPTION_DYNAMIC);
122
123 Error = GetLastError();
124
125 if (Error == ERROR_NOT_FOUND)
126 {
127 WLog_DBG(TAG, "Channel %s not found", AINPUT_DVC_CHANNEL_NAME);
128 break;
129 }
130
131 if (ainput->ainput_channel)
132 {
133 UINT32 channelId = 0;
134 BOOL status = TRUE;
135
136 channelId = WTSChannelGetIdByHandle(ainput->ainput_channel);
137
138 IFCALLRET(ainput->context.ChannelIdAssigned, status, &ainput->context, channelId);
139 if (!status)
140 {
141 WLog_ERR(TAG, "context->ChannelIdAssigned failed!");
142 return ERROR_INTERNAL_ERROR;
143 }
144
145 break;
146 }
147
148 if (GetTickCount() - StartTick > 5000)
149 {
150 WLog_WARN(TAG, "Timeout opening channel %s", AINPUT_DVC_CHANNEL_NAME);
151 break;
152 }
153 }
154
155 return ainput->ainput_channel ? CHANNEL_RC_OK : ERROR_INTERNAL_ERROR;
156}
157
158static UINT ainput_server_send_version(ainput_server* ainput)
159{
160 ULONG written = 0;
161 wStream* s = NULL;
162
163 WINPR_ASSERT(ainput);
164
165 s = ainput->buffer;
166 WINPR_ASSERT(s);
167
168 Stream_SetPosition(s, 0);
169 if (!Stream_EnsureCapacity(s, 10))
170 {
171 WLog_WARN(TAG, "[%s] out of memory", AINPUT_DVC_CHANNEL_NAME);
172 return ERROR_OUTOFMEMORY;
173 }
174
175 Stream_Write_UINT16(s, MSG_AINPUT_VERSION);
176 Stream_Write_UINT32(s, AINPUT_VERSION_MAJOR); /* Version (4 bytes) */
177 Stream_Write_UINT32(s, AINPUT_VERSION_MINOR); /* Version (4 bytes) */
178
179 WINPR_ASSERT(Stream_GetPosition(s) <= UINT32_MAX);
180 if (!WTSVirtualChannelWrite(ainput->ainput_channel, Stream_BufferAs(s, char),
181 (ULONG)Stream_GetPosition(s), &written))
182 {
183 WLog_ERR(TAG, "WTSVirtualChannelWrite failed!");
184 return ERROR_INTERNAL_ERROR;
185 }
186
187 return CHANNEL_RC_OK;
188}
189
190static UINT ainput_server_recv_mouse_event(ainput_server* ainput, wStream* s)
191{
192 UINT error = CHANNEL_RC_OK;
193 UINT64 flags = 0;
194 UINT64 time = 0;
195 INT32 x = 0;
196 INT32 y = 0;
197 char buffer[128] = { 0 };
198
199 WINPR_ASSERT(ainput);
200 WINPR_ASSERT(s);
201
202 if (!Stream_CheckAndLogRequiredLength(TAG, s, 24))
203 return ERROR_NO_DATA;
204
205 Stream_Read_UINT64(s, time);
206 Stream_Read_UINT64(s, flags);
207 Stream_Read_INT32(s, x);
208 Stream_Read_INT32(s, y);
209
210 WLog_VRB(TAG, "received: time=0x%08" PRIx64 ", flags=%s, %" PRId32 "x%" PRId32, time,
211 ainput_flags_to_string(flags, buffer, sizeof(buffer)), x, y);
212 IFCALLRET(ainput->context.MouseEvent, error, &ainput->context, time, flags, x, y);
213
214 return error;
215}
216
217static HANDLE ainput_server_get_channel_handle(ainput_server* ainput)
218{
219 void* buffer = NULL;
220 DWORD BytesReturned = 0;
221 HANDLE ChannelEvent = NULL;
222
223 WINPR_ASSERT(ainput);
224
225 if (WTSVirtualChannelQuery(ainput->ainput_channel, WTSVirtualEventHandle, &buffer,
226 &BytesReturned) == TRUE)
227 {
228 if (BytesReturned == sizeof(HANDLE))
229 ChannelEvent = *(HANDLE*)buffer;
230
231 WTSFreeMemory(buffer);
232 }
233
234 return ChannelEvent;
235}
236
237static DWORD WINAPI ainput_server_thread_func(LPVOID arg)
238{
239 DWORD nCount = 0;
240 HANDLE events[2] = { 0 };
241 ainput_server* ainput = (ainput_server*)arg;
242 UINT error = CHANNEL_RC_OK;
243 DWORD status = 0;
244
245 WINPR_ASSERT(ainput);
246
247 nCount = 0;
248 events[nCount++] = ainput->stopEvent;
249
250 while ((error == CHANNEL_RC_OK) && (WaitForSingleObject(events[0], 0) != WAIT_OBJECT_0))
251 {
252 switch (ainput->state)
253 {
254 case AINPUT_OPENED:
255 events[1] = ainput_server_get_channel_handle(ainput);
256 nCount = 2;
257 status = WaitForMultipleObjects(nCount, events, FALSE, 100);
258 switch (status)
259 {
260 case WAIT_TIMEOUT:
261 case WAIT_OBJECT_0 + 1:
262 case WAIT_OBJECT_0:
263 error = ainput_server_context_poll_int(&ainput->context);
264 break;
265 case WAIT_FAILED:
266 default:
267 WLog_WARN(TAG, "[%s] Wait for open failed", AINPUT_DVC_CHANNEL_NAME);
268 error = ERROR_INTERNAL_ERROR;
269 break;
270 }
271 break;
272 case AINPUT_VERSION_SENT:
273 status = WaitForMultipleObjects(nCount, events, FALSE, INFINITE);
274 switch (status)
275 {
276 case WAIT_TIMEOUT:
277 case WAIT_OBJECT_0 + 1:
278 case WAIT_OBJECT_0:
279 error = ainput_server_context_poll_int(&ainput->context);
280 break;
281
282 case WAIT_FAILED:
283 default:
284 WLog_WARN(TAG, "[%s] Wait for version failed", AINPUT_DVC_CHANNEL_NAME);
285 error = ERROR_INTERNAL_ERROR;
286 break;
287 }
288 break;
289 default:
290 error = ainput_server_context_poll_int(&ainput->context);
291 break;
292 }
293 }
294
295 (void)WTSVirtualChannelClose(ainput->ainput_channel);
296 ainput->ainput_channel = NULL;
297
298 if (error && ainput->context.rdpcontext)
299 setChannelError(ainput->context.rdpcontext, error,
300 "ainput_server_thread_func reported an error");
301
302 ExitThread(error);
303 return error;
304}
305
311static UINT ainput_server_open(ainput_server_context* context)
312{
313 ainput_server* ainput = (ainput_server*)context;
314
315 WINPR_ASSERT(ainput);
316
317 if (!ainput->externalThread && (ainput->thread == NULL))
318 {
319 ainput->stopEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
320 if (!ainput->stopEvent)
321 {
322 WLog_ERR(TAG, "CreateEvent failed!");
323 return ERROR_INTERNAL_ERROR;
324 }
325
326 ainput->thread = CreateThread(NULL, 0, ainput_server_thread_func, ainput, 0, NULL);
327 if (!ainput->thread)
328 {
329 WLog_ERR(TAG, "CreateEvent failed!");
330 (void)CloseHandle(ainput->stopEvent);
331 ainput->stopEvent = NULL;
332 return ERROR_INTERNAL_ERROR;
333 }
334 }
335 ainput->isOpened = TRUE;
336
337 return CHANNEL_RC_OK;
338}
339
345static UINT ainput_server_close(ainput_server_context* context)
346{
347 UINT error = CHANNEL_RC_OK;
348 ainput_server* ainput = (ainput_server*)context;
349
350 WINPR_ASSERT(ainput);
351
352 if (!ainput->externalThread && ainput->thread)
353 {
354 (void)SetEvent(ainput->stopEvent);
355
356 if (WaitForSingleObject(ainput->thread, INFINITE) == WAIT_FAILED)
357 {
358 error = GetLastError();
359 WLog_ERR(TAG, "WaitForSingleObject failed with error %" PRIu32 "", error);
360 return error;
361 }
362
363 (void)CloseHandle(ainput->thread);
364 (void)CloseHandle(ainput->stopEvent);
365 ainput->thread = NULL;
366 ainput->stopEvent = NULL;
367 }
368 if (ainput->externalThread)
369 {
370 if (ainput->state != AINPUT_INITIAL)
371 {
372 (void)WTSVirtualChannelClose(ainput->ainput_channel);
373 ainput->ainput_channel = NULL;
374 ainput->state = AINPUT_INITIAL;
375 }
376 }
377 ainput->isOpened = FALSE;
378
379 return error;
380}
381
382static UINT ainput_server_initialize(ainput_server_context* context, BOOL externalThread)
383{
384 UINT error = CHANNEL_RC_OK;
385 ainput_server* ainput = (ainput_server*)context;
386
387 WINPR_ASSERT(ainput);
388
389 if (ainput->isOpened)
390 {
391 WLog_WARN(TAG, "Application error: AINPUT channel already initialized, calling in this "
392 "state is not possible!");
393 return ERROR_INVALID_STATE;
394 }
395 ainput->externalThread = externalThread;
396 return error;
397}
398
399ainput_server_context* ainput_server_context_new(HANDLE vcm)
400{
401 ainput_server* ainput = (ainput_server*)calloc(1, sizeof(ainput_server));
402
403 if (!ainput)
404 return NULL;
405
406 ainput->context.vcm = vcm;
407 ainput->context.Open = ainput_server_open;
408 ainput->context.IsOpen = ainput_server_is_open;
409 ainput->context.Close = ainput_server_close;
410 ainput->context.Initialize = ainput_server_initialize;
411 ainput->context.Poll = ainput_server_context_poll;
412 ainput->context.ChannelHandle = ainput_server_context_handle;
413
414 ainput->buffer = Stream_New(NULL, 4096);
415 if (!ainput->buffer)
416 goto fail;
417 return &ainput->context;
418fail:
419 WINPR_PRAGMA_DIAG_PUSH
420 WINPR_PRAGMA_DIAG_IGNORED_MISMATCHED_DEALLOC
421 ainput_server_context_free(&ainput->context);
422 WINPR_PRAGMA_DIAG_POP
423 return NULL;
424}
425
426void ainput_server_context_free(ainput_server_context* context)
427{
428 ainput_server* ainput = (ainput_server*)context;
429 if (ainput)
430 {
431 ainput_server_close(context);
432 Stream_Free(ainput->buffer, TRUE);
433 }
434 free(ainput);
435}
436
437static UINT ainput_process_message(ainput_server* ainput)
438{
439 BOOL rc = 0;
440 UINT error = ERROR_INTERNAL_ERROR;
441 ULONG BytesReturned = 0;
442 ULONG ActualBytesReturned = 0;
443 UINT16 MessageId = 0;
444 wStream* s = NULL;
445
446 WINPR_ASSERT(ainput);
447 WINPR_ASSERT(ainput->ainput_channel);
448
449 s = ainput->buffer;
450 WINPR_ASSERT(s);
451
452 Stream_SetPosition(s, 0);
453 rc = WTSVirtualChannelRead(ainput->ainput_channel, 0, NULL, 0, &BytesReturned);
454 if (!rc)
455 goto out;
456
457 if (BytesReturned < 2)
458 {
459 error = CHANNEL_RC_OK;
460 goto out;
461 }
462
463 if (!Stream_EnsureRemainingCapacity(s, BytesReturned))
464 {
465 WLog_ERR(TAG, "Stream_EnsureRemainingCapacity failed!");
466 error = CHANNEL_RC_NO_MEMORY;
467 goto out;
468 }
469
470 if (WTSVirtualChannelRead(ainput->ainput_channel, 0, Stream_BufferAs(s, char),
471 (ULONG)Stream_Capacity(s), &ActualBytesReturned) == FALSE)
472 {
473 WLog_ERR(TAG, "WTSVirtualChannelRead failed!");
474 goto out;
475 }
476
477 if (BytesReturned != ActualBytesReturned)
478 {
479 WLog_ERR(TAG, "WTSVirtualChannelRead size mismatch %" PRId32 ", expected %" PRId32,
480 ActualBytesReturned, BytesReturned);
481 goto out;
482 }
483
484 Stream_SetLength(s, ActualBytesReturned);
485 Stream_Read_UINT16(s, MessageId);
486
487 switch (MessageId)
488 {
489 case MSG_AINPUT_MOUSE:
490 error = ainput_server_recv_mouse_event(ainput, s);
491 break;
492
493 default:
494 WLog_ERR(TAG, "audin_server_thread_func: unknown MessageId %" PRIu8 "", MessageId);
495 break;
496 }
497
498out:
499 if (error)
500 WLog_ERR(TAG, "Response failed with error %" PRIu32 "!", error);
501
502 return error;
503}
504
505BOOL ainput_server_context_handle(ainput_server_context* context, HANDLE* handle)
506{
507 ainput_server* ainput = (ainput_server*)context;
508 WINPR_ASSERT(ainput);
509 WINPR_ASSERT(handle);
510
511 if (!ainput->externalThread)
512 {
513 WLog_WARN(TAG, "[%s] externalThread fail!", AINPUT_DVC_CHANNEL_NAME);
514 return FALSE;
515 }
516 if (ainput->state == AINPUT_INITIAL)
517 {
518 WLog_WARN(TAG, "[%s] state fail!", AINPUT_DVC_CHANNEL_NAME);
519 return FALSE;
520 }
521 *handle = ainput_server_get_channel_handle(ainput);
522 return TRUE;
523}
524
525UINT ainput_server_context_poll_int(ainput_server_context* context)
526{
527 ainput_server* ainput = (ainput_server*)context;
528 UINT error = ERROR_INTERNAL_ERROR;
529
530 WINPR_ASSERT(ainput);
531
532 switch (ainput->state)
533 {
534 case AINPUT_INITIAL:
535 error = ainput_server_open_channel(ainput);
536 if (error)
537 WLog_ERR(TAG, "ainput_server_open_channel failed with error %" PRIu32 "!", error);
538 else
539 ainput->state = AINPUT_OPENED;
540 break;
541 case AINPUT_OPENED:
542 {
543 union
544 {
545 BYTE* pb;
546 void* pv;
547 } buffer;
548 DWORD BytesReturned = 0;
549
550 buffer.pv = NULL;
551
552 if (WTSVirtualChannelQuery(ainput->ainput_channel, WTSVirtualChannelReady, &buffer.pv,
553 &BytesReturned) != TRUE)
554 {
555 WLog_ERR(TAG, "WTSVirtualChannelReady failed,");
556 }
557 else
558 {
559 if (*buffer.pb != 0)
560 {
561 error = ainput_server_send_version(ainput);
562 if (error)
563 WLog_ERR(TAG, "audin_server_send_version failed with error %" PRIu32 "!",
564 error);
565 else
566 ainput->state = AINPUT_VERSION_SENT;
567 }
568 else
569 error = CHANNEL_RC_OK;
570 }
571 WTSFreeMemory(buffer.pv);
572 }
573 break;
574 case AINPUT_VERSION_SENT:
575 error = ainput_process_message(ainput);
576 break;
577
578 default:
579 WLog_ERR(TAG, "AINPUT channel is in invalid state %d", ainput->state);
580 break;
581 }
582
583 return error;
584}
585
586UINT ainput_server_context_poll(ainput_server_context* context)
587{
588 ainput_server* ainput = (ainput_server*)context;
589
590 WINPR_ASSERT(ainput);
591 if (!ainput->externalThread)
592 {
593 WLog_WARN(TAG, "[%s] externalThread fail!", AINPUT_DVC_CHANNEL_NAME);
594 return ERROR_INTERNAL_ERROR;
595 }
596 return ainput_server_context_poll_int(context);
597}