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