FreeRDP
Loading...
Searching...
No Matches
camera_device_enumerator_main.c
1
20#include <winpr/cast.h>
21
22#include <freerdp/config.h>
23
24#include <freerdp/freerdp.h>
25#include <freerdp/channels/log.h>
26#include <freerdp/server/rdpecam-enumerator.h>
27
28#include "rdpecam-utils.h"
29
30#define TAG CHANNELS_TAG("rdpecam-enumerator.server")
31
32typedef enum
33{
34 ENUMERATOR_INITIAL,
35 ENUMERATOR_OPENED,
36} eEnumeratorChannelState;
37
38typedef struct
39{
40 CamDevEnumServerContext context;
41
42 HANDLE stopEvent;
43
44 HANDLE thread;
45 void* enumerator_channel;
46
47 DWORD SessionId;
48
49 BOOL isOpened;
50 BOOL externalThread;
51
52 /* Channel state */
53 eEnumeratorChannelState state;
54
55 wStream* buffer;
56} enumerator_server;
57
58static UINT enumerator_server_initialize(CamDevEnumServerContext* context, BOOL externalThread)
59{
60 UINT error = CHANNEL_RC_OK;
61 enumerator_server* enumerator = (enumerator_server*)context;
62
63 WINPR_ASSERT(enumerator);
64
65 if (enumerator->isOpened)
66 {
67 WLog_WARN(TAG, "Application error: Camera Device Enumerator channel already initialized, "
68 "calling in this state is not possible!");
69 return ERROR_INVALID_STATE;
70 }
71
72 enumerator->externalThread = externalThread;
73
74 return error;
75}
76
77static UINT enumerator_server_open_channel(enumerator_server* enumerator)
78{
79 CamDevEnumServerContext* context = &enumerator->context;
80 DWORD Error = ERROR_SUCCESS;
81 HANDLE hEvent = NULL;
82 DWORD BytesReturned = 0;
83 PULONG pSessionId = NULL;
84 UINT32 channelId = 0;
85 BOOL status = TRUE;
86
87 WINPR_ASSERT(enumerator);
88
89 if (WTSQuerySessionInformationA(enumerator->context.vcm, WTS_CURRENT_SESSION, WTSSessionId,
90 (LPSTR*)&pSessionId, &BytesReturned) == FALSE)
91 {
92 WLog_ERR(TAG, "WTSQuerySessionInformationA failed!");
93 return ERROR_INTERNAL_ERROR;
94 }
95
96 enumerator->SessionId = (DWORD)*pSessionId;
97 WTSFreeMemory(pSessionId);
98 hEvent = WTSVirtualChannelManagerGetEventHandle(enumerator->context.vcm);
99
100 if (WaitForSingleObject(hEvent, 1000) == WAIT_FAILED)
101 {
102 Error = GetLastError();
103 WLog_ERR(TAG, "WaitForSingleObject failed with error %" PRIu32 "!", Error);
104 return Error;
105 }
106
107 enumerator->enumerator_channel = WTSVirtualChannelOpenEx(
108 enumerator->SessionId, RDPECAM_CONTROL_DVC_CHANNEL_NAME, WTS_CHANNEL_OPTION_DYNAMIC);
109 if (!enumerator->enumerator_channel)
110 {
111 Error = GetLastError();
112 WLog_ERR(TAG, "WTSVirtualChannelOpenEx failed with error %" PRIu32 "!", Error);
113 return Error;
114 }
115
116 channelId = WTSChannelGetIdByHandle(enumerator->enumerator_channel);
117
118 IFCALLRET(context->ChannelIdAssigned, status, context, channelId);
119 if (!status)
120 {
121 WLog_ERR(TAG, "context->ChannelIdAssigned failed!");
122 return ERROR_INTERNAL_ERROR;
123 }
124
125 return Error;
126}
127
128static UINT enumerator_server_handle_select_version_request(CamDevEnumServerContext* context,
129 WINPR_ATTR_UNUSED wStream* s,
130 const CAM_SHARED_MSG_HEADER* header)
131{
132 CAM_SELECT_VERSION_REQUEST pdu = { 0 };
133 UINT error = CHANNEL_RC_OK;
134
135 WINPR_ASSERT(context);
136 WINPR_ASSERT(header);
137
138 pdu.Header = *header;
139
140 IFCALLRET(context->SelectVersionRequest, error, context, &pdu);
141 if (error)
142 WLog_ERR(TAG, "context->SelectVersionRequest failed with error %" PRIu32 "", error);
143
144 return error;
145}
146
147static UINT enumerator_server_recv_device_added_notification(CamDevEnumServerContext* context,
148 wStream* s,
149 const CAM_SHARED_MSG_HEADER* header)
150{
152 UINT error = CHANNEL_RC_OK;
153 size_t remaining_length = 0;
154 WCHAR* channel_name_start = 0;
155
156 WINPR_ASSERT(context);
157 WINPR_ASSERT(header);
158
159 pdu.Header = *header;
160
161 /*
162 * RequiredLength 4:
163 *
164 * Nullterminator DeviceName (2),
165 * VirtualChannelName (>= 1),
166 * Nullterminator VirtualChannelName (1)
167 */
168 if (!Stream_CheckAndLogRequiredLength(TAG, s, 4))
169 return ERROR_NO_DATA;
170
171 pdu.DeviceName = Stream_Pointer(s);
172
173 remaining_length = Stream_GetRemainingLength(s);
174 channel_name_start = Stream_Pointer(s);
175
176 /* Search for null terminator of DeviceName */
177 size_t i = 0;
178 for (; i < remaining_length; i += sizeof(WCHAR), ++channel_name_start)
179 {
180 if (*channel_name_start == L'\0')
181 break;
182 }
183
184 if (*channel_name_start != L'\0')
185 {
186 WLog_ERR(TAG, "enumerator_server_recv_device_added_notification: Invalid DeviceName!");
187 return ERROR_INVALID_DATA;
188 }
189
190 pdu.VirtualChannelName = (char*)++channel_name_start;
191 ++i;
192
193 if (i >= remaining_length || *pdu.VirtualChannelName == '\0')
194 {
195 WLog_ERR(TAG,
196 "enumerator_server_recv_device_added_notification: Invalid VirtualChannelName!");
197 return ERROR_INVALID_DATA;
198 }
199
200 char* tmp = pdu.VirtualChannelName;
201 for (; i < remaining_length; ++i, ++tmp)
202 {
203 if (*tmp == '\0')
204 break;
205 }
206
207 if (*tmp != '\0')
208 {
209 WLog_ERR(TAG,
210 "enumerator_server_recv_device_added_notification: Invalid VirtualChannelName!");
211 return ERROR_INVALID_DATA;
212 }
213
214 IFCALLRET(context->DeviceAddedNotification, error, context, &pdu);
215 if (error)
216 WLog_ERR(TAG, "context->DeviceAddedNotification failed with error %" PRIu32 "", error);
217
218 return error;
219}
220
221static UINT enumerator_server_recv_device_removed_notification(CamDevEnumServerContext* context,
222 wStream* s,
223 const CAM_SHARED_MSG_HEADER* header)
224{
226 UINT error = CHANNEL_RC_OK;
227 size_t remaining_length = 0;
228
229 WINPR_ASSERT(context);
230 WINPR_ASSERT(header);
231
232 pdu.Header = *header;
233
234 if (!Stream_CheckAndLogRequiredLength(TAG, s, 2))
235 return ERROR_NO_DATA;
236
237 pdu.VirtualChannelName = Stream_Pointer(s);
238
239 remaining_length = Stream_GetRemainingLength(s);
240 char* tmp = pdu.VirtualChannelName + 1;
241
242 for (size_t i = 1; i < remaining_length; ++i, ++tmp)
243 {
244 if (*tmp == '\0')
245 break;
246 }
247
248 if (*tmp != '\0')
249 {
250 WLog_ERR(TAG,
251 "enumerator_server_recv_device_removed_notification: Invalid VirtualChannelName!");
252 return ERROR_INVALID_DATA;
253 }
254
255 IFCALLRET(context->DeviceRemovedNotification, error, context, &pdu);
256 if (error)
257 WLog_ERR(TAG, "context->DeviceRemovedNotification failed with error %" PRIu32 "", error);
258
259 return error;
260}
261
262static UINT enumerator_process_message(enumerator_server* enumerator)
263{
264 BOOL rc = 0;
265 UINT error = ERROR_INTERNAL_ERROR;
266 ULONG BytesReturned = 0;
267 CAM_SHARED_MSG_HEADER header = { 0 };
268 wStream* s = NULL;
269
270 WINPR_ASSERT(enumerator);
271 WINPR_ASSERT(enumerator->enumerator_channel);
272
273 s = enumerator->buffer;
274 WINPR_ASSERT(s);
275
276 Stream_SetPosition(s, 0);
277 rc = WTSVirtualChannelRead(enumerator->enumerator_channel, 0, NULL, 0, &BytesReturned);
278 if (!rc)
279 goto out;
280
281 if (BytesReturned < 1)
282 {
283 error = CHANNEL_RC_OK;
284 goto out;
285 }
286
287 if (!Stream_EnsureRemainingCapacity(s, BytesReturned))
288 {
289 WLog_ERR(TAG, "Stream_EnsureRemainingCapacity failed!");
290 error = CHANNEL_RC_NO_MEMORY;
291 goto out;
292 }
293
294 if (WTSVirtualChannelRead(enumerator->enumerator_channel, 0, Stream_BufferAs(s, char),
295 (ULONG)Stream_Capacity(s), &BytesReturned) == FALSE)
296 {
297 WLog_ERR(TAG, "WTSVirtualChannelRead failed!");
298 goto out;
299 }
300
301 Stream_SetLength(s, BytesReturned);
302 if (!Stream_CheckAndLogRequiredLength(TAG, s, CAM_HEADER_SIZE))
303 return ERROR_NO_DATA;
304
305 Stream_Read_UINT8(s, header.Version);
306 {
307 const UINT8 id = Stream_Get_UINT8(s);
308 if (!rdpecam_valid_messageId(id))
309 return ERROR_INVALID_DATA;
310 header.MessageId = (CAM_MSG_ID)id;
311 }
312
313 switch (header.MessageId)
314 {
315 case CAM_MSG_ID_SelectVersionRequest:
316 error =
317 enumerator_server_handle_select_version_request(&enumerator->context, s, &header);
318 break;
319 case CAM_MSG_ID_DeviceAddedNotification:
320 error =
321 enumerator_server_recv_device_added_notification(&enumerator->context, s, &header);
322 break;
323 case CAM_MSG_ID_DeviceRemovedNotification:
324 error = enumerator_server_recv_device_removed_notification(&enumerator->context, s,
325 &header);
326 break;
327 default:
328 WLog_ERR(TAG, "enumerator_process_message: unknown or invalid MessageId %" PRIu8 "",
329 header.MessageId);
330 break;
331 }
332
333out:
334 if (error)
335 WLog_ERR(TAG, "Response failed with error %" PRIu32 "!", error);
336
337 return error;
338}
339
340static UINT enumerator_server_context_poll_int(CamDevEnumServerContext* context)
341{
342 enumerator_server* enumerator = (enumerator_server*)context;
343 UINT error = ERROR_INTERNAL_ERROR;
344
345 WINPR_ASSERT(enumerator);
346
347 switch (enumerator->state)
348 {
349 case ENUMERATOR_INITIAL:
350 error = enumerator_server_open_channel(enumerator);
351 if (error)
352 WLog_ERR(TAG, "enumerator_server_open_channel failed with error %" PRIu32 "!",
353 error);
354 else
355 enumerator->state = ENUMERATOR_OPENED;
356 break;
357 case ENUMERATOR_OPENED:
358 error = enumerator_process_message(enumerator);
359 break;
360 default:
361 break;
362 }
363
364 return error;
365}
366
367static HANDLE enumerator_server_get_channel_handle(enumerator_server* enumerator)
368{
369 void* buffer = NULL;
370 DWORD BytesReturned = 0;
371 HANDLE ChannelEvent = NULL;
372
373 WINPR_ASSERT(enumerator);
374
375 if (WTSVirtualChannelQuery(enumerator->enumerator_channel, WTSVirtualEventHandle, &buffer,
376 &BytesReturned) == TRUE)
377 {
378 if (BytesReturned == sizeof(HANDLE))
379 ChannelEvent = *(HANDLE*)buffer;
380
381 WTSFreeMemory(buffer);
382 }
383
384 return ChannelEvent;
385}
386
387static DWORD WINAPI enumerator_server_thread_func(LPVOID arg)
388{
389 DWORD nCount = 0;
390 HANDLE events[2] = { 0 };
391 enumerator_server* enumerator = (enumerator_server*)arg;
392 UINT error = CHANNEL_RC_OK;
393 DWORD status = 0;
394
395 WINPR_ASSERT(enumerator);
396
397 nCount = 0;
398 events[nCount++] = enumerator->stopEvent;
399
400 while ((error == CHANNEL_RC_OK) && (WaitForSingleObject(events[0], 0) != WAIT_OBJECT_0))
401 {
402 switch (enumerator->state)
403 {
404 case ENUMERATOR_INITIAL:
405 error = enumerator_server_context_poll_int(&enumerator->context);
406 if (error == CHANNEL_RC_OK)
407 {
408 events[1] = enumerator_server_get_channel_handle(enumerator);
409 nCount = 2;
410 }
411 break;
412 case ENUMERATOR_OPENED:
413 status = WaitForMultipleObjects(nCount, events, FALSE, INFINITE);
414 switch (status)
415 {
416 case WAIT_OBJECT_0:
417 break;
418 case WAIT_OBJECT_0 + 1:
419 case WAIT_TIMEOUT:
420 error = enumerator_server_context_poll_int(&enumerator->context);
421 break;
422
423 case WAIT_FAILED:
424 default:
425 error = ERROR_INTERNAL_ERROR;
426 break;
427 }
428 break;
429 default:
430 break;
431 }
432 }
433
434 (void)WTSVirtualChannelClose(enumerator->enumerator_channel);
435 enumerator->enumerator_channel = NULL;
436
437 if (error && enumerator->context.rdpcontext)
438 setChannelError(enumerator->context.rdpcontext, error,
439 "enumerator_server_thread_func reported an error");
440
441 ExitThread(error);
442 return error;
443}
444
445static UINT enumerator_server_open(CamDevEnumServerContext* context)
446{
447 enumerator_server* enumerator = (enumerator_server*)context;
448
449 WINPR_ASSERT(enumerator);
450
451 if (!enumerator->externalThread && (enumerator->thread == NULL))
452 {
453 enumerator->stopEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
454 if (!enumerator->stopEvent)
455 {
456 WLog_ERR(TAG, "CreateEvent failed!");
457 return ERROR_INTERNAL_ERROR;
458 }
459
460 enumerator->thread =
461 CreateThread(NULL, 0, enumerator_server_thread_func, enumerator, 0, NULL);
462 if (!enumerator->thread)
463 {
464 WLog_ERR(TAG, "CreateThread failed!");
465 (void)CloseHandle(enumerator->stopEvent);
466 enumerator->stopEvent = NULL;
467 return ERROR_INTERNAL_ERROR;
468 }
469 }
470 enumerator->isOpened = TRUE;
471
472 return CHANNEL_RC_OK;
473}
474
475static UINT enumerator_server_close(CamDevEnumServerContext* context)
476{
477 UINT error = CHANNEL_RC_OK;
478 enumerator_server* enumerator = (enumerator_server*)context;
479
480 WINPR_ASSERT(enumerator);
481
482 if (!enumerator->externalThread && enumerator->thread)
483 {
484 (void)SetEvent(enumerator->stopEvent);
485
486 if (WaitForSingleObject(enumerator->thread, INFINITE) == WAIT_FAILED)
487 {
488 error = GetLastError();
489 WLog_ERR(TAG, "WaitForSingleObject failed with error %" PRIu32 "", error);
490 return error;
491 }
492
493 (void)CloseHandle(enumerator->thread);
494 (void)CloseHandle(enumerator->stopEvent);
495 enumerator->thread = NULL;
496 enumerator->stopEvent = NULL;
497 }
498 if (enumerator->externalThread)
499 {
500 if (enumerator->state != ENUMERATOR_INITIAL)
501 {
502 (void)WTSVirtualChannelClose(enumerator->enumerator_channel);
503 enumerator->enumerator_channel = NULL;
504 enumerator->state = ENUMERATOR_INITIAL;
505 }
506 }
507 enumerator->isOpened = FALSE;
508
509 return error;
510}
511
512static UINT enumerator_server_context_poll(CamDevEnumServerContext* context)
513{
514 enumerator_server* enumerator = (enumerator_server*)context;
515
516 WINPR_ASSERT(enumerator);
517
518 if (!enumerator->externalThread)
519 return ERROR_INTERNAL_ERROR;
520
521 return enumerator_server_context_poll_int(context);
522}
523
524static BOOL enumerator_server_context_handle(CamDevEnumServerContext* context, HANDLE* handle)
525{
526 enumerator_server* enumerator = (enumerator_server*)context;
527
528 WINPR_ASSERT(enumerator);
529 WINPR_ASSERT(handle);
530
531 if (!enumerator->externalThread)
532 return FALSE;
533 if (enumerator->state == ENUMERATOR_INITIAL)
534 return FALSE;
535
536 *handle = enumerator_server_get_channel_handle(enumerator);
537
538 return TRUE;
539}
540
541static UINT enumerator_server_packet_send(CamDevEnumServerContext* context, wStream* s)
542{
543 enumerator_server* enumerator = (enumerator_server*)context;
544 UINT error = CHANNEL_RC_OK;
545 ULONG written = 0;
546
547 const size_t len = Stream_GetPosition(s);
548 WINPR_ASSERT(len <= UINT32_MAX);
549 if (!WTSVirtualChannelWrite(enumerator->enumerator_channel, Stream_BufferAs(s, char),
550 (UINT32)len, &written))
551 {
552 WLog_ERR(TAG, "WTSVirtualChannelWrite failed!");
553 error = ERROR_INTERNAL_ERROR;
554 goto out;
555 }
556
557 if (written < Stream_GetPosition(s))
558 {
559 WLog_WARN(TAG, "Unexpected bytes written: %" PRIu32 "/%" PRIuz "", written,
560 Stream_GetPosition(s));
561 }
562
563out:
564 Stream_Free(s, TRUE);
565 return error;
566}
567
568static UINT enumerator_send_select_version_response_pdu(
569 CamDevEnumServerContext* context, const CAM_SELECT_VERSION_RESPONSE* selectVersionResponse)
570{
571 wStream* s = NULL;
572
573 s = Stream_New(NULL, CAM_HEADER_SIZE);
574 if (!s)
575 {
576 WLog_ERR(TAG, "Stream_New failed!");
577 return ERROR_NOT_ENOUGH_MEMORY;
578 }
579
580 Stream_Write_UINT8(s, selectVersionResponse->Header.Version);
581 Stream_Write_UINT8(s,
582 WINPR_ASSERTING_INT_CAST(uint8_t, selectVersionResponse->Header.MessageId));
583
584 return enumerator_server_packet_send(context, s);
585}
586
587CamDevEnumServerContext* cam_dev_enum_server_context_new(HANDLE vcm)
588{
589 enumerator_server* enumerator = (enumerator_server*)calloc(1, sizeof(enumerator_server));
590
591 if (!enumerator)
592 return NULL;
593
594 enumerator->context.vcm = vcm;
595 enumerator->context.Initialize = enumerator_server_initialize;
596 enumerator->context.Open = enumerator_server_open;
597 enumerator->context.Close = enumerator_server_close;
598 enumerator->context.Poll = enumerator_server_context_poll;
599 enumerator->context.ChannelHandle = enumerator_server_context_handle;
600
601 enumerator->context.SelectVersionResponse = enumerator_send_select_version_response_pdu;
602
603 enumerator->buffer = Stream_New(NULL, 4096);
604 if (!enumerator->buffer)
605 goto fail;
606
607 return &enumerator->context;
608fail:
609 WINPR_PRAGMA_DIAG_PUSH
610 WINPR_PRAGMA_DIAG_IGNORED_MISMATCHED_DEALLOC
611 cam_dev_enum_server_context_free(&enumerator->context);
612 WINPR_PRAGMA_DIAG_POP
613 return NULL;
614}
615
616void cam_dev_enum_server_context_free(CamDevEnumServerContext* context)
617{
618 enumerator_server* enumerator = (enumerator_server*)context;
619
620 if (enumerator)
621 {
622 enumerator_server_close(context);
623 Stream_Free(enumerator->buffer, TRUE);
624 }
625
626 free(enumerator);
627}