20#include <winpr/cast.h> 
   22#include <freerdp/config.h> 
   24#include <freerdp/freerdp.h> 
   25#include <freerdp/channels/log.h> 
   26#include <freerdp/server/rdpecam-enumerator.h> 
   28#define TAG CHANNELS_TAG("rdpecam-enumerator.server") 
   34} eEnumeratorChannelState;
 
   38  CamDevEnumServerContext context;
 
   43  void* enumerator_channel;
 
   51  eEnumeratorChannelState state;
 
   56static UINT enumerator_server_initialize(CamDevEnumServerContext* context, BOOL externalThread)
 
   58  UINT error = CHANNEL_RC_OK;
 
   59  enumerator_server* enumerator = (enumerator_server*)context;
 
   61  WINPR_ASSERT(enumerator);
 
   63  if (enumerator->isOpened)
 
   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;
 
   70  enumerator->externalThread = externalThread;
 
   75static UINT enumerator_server_open_channel(enumerator_server* enumerator)
 
   77  CamDevEnumServerContext* context = &enumerator->context;
 
   78  DWORD Error = ERROR_SUCCESS;
 
   80  DWORD BytesReturned = 0;
 
   81  PULONG pSessionId = NULL;
 
   85  WINPR_ASSERT(enumerator);
 
   87  if (WTSQuerySessionInformationA(enumerator->context.vcm, WTS_CURRENT_SESSION, WTSSessionId,
 
   88                                  (LPSTR*)&pSessionId, &BytesReturned) == FALSE)
 
   90    WLog_ERR(TAG, 
"WTSQuerySessionInformationA failed!");
 
   91    return ERROR_INTERNAL_ERROR;
 
   94  enumerator->SessionId = (DWORD)*pSessionId;
 
   95  WTSFreeMemory(pSessionId);
 
   96  hEvent = WTSVirtualChannelManagerGetEventHandle(enumerator->context.vcm);
 
   98  if (WaitForSingleObject(hEvent, 1000) == WAIT_FAILED)
 
  100    Error = GetLastError();
 
  101    WLog_ERR(TAG, 
"WaitForSingleObject failed with error %" PRIu32 
"!", Error);
 
  105  enumerator->enumerator_channel = WTSVirtualChannelOpenEx(
 
  106      enumerator->SessionId, RDPECAM_CONTROL_DVC_CHANNEL_NAME, WTS_CHANNEL_OPTION_DYNAMIC);
 
  107  if (!enumerator->enumerator_channel)
 
  109    Error = GetLastError();
 
  110    WLog_ERR(TAG, 
"WTSVirtualChannelOpenEx failed with error %" PRIu32 
"!", Error);
 
  114  channelId = WTSChannelGetIdByHandle(enumerator->enumerator_channel);
 
  116  IFCALLRET(context->ChannelIdAssigned, status, context, channelId);
 
  119    WLog_ERR(TAG, 
"context->ChannelIdAssigned failed!");
 
  120    return ERROR_INTERNAL_ERROR;
 
  126static UINT enumerator_server_handle_select_version_request(CamDevEnumServerContext* context,
 
  131  UINT error = CHANNEL_RC_OK;
 
  133  WINPR_ASSERT(context);
 
  134  WINPR_ASSERT(header);
 
  136  pdu.Header = *header;
 
  138  IFCALLRET(context->SelectVersionRequest, error, context, &pdu);
 
  140    WLog_ERR(TAG, 
"context->SelectVersionRequest failed with error %" PRIu32 
"", error);
 
  145static UINT enumerator_server_recv_device_added_notification(CamDevEnumServerContext* context,
 
  150  UINT error = CHANNEL_RC_OK;
 
  151  size_t remaining_length = 0;
 
  152  WCHAR* channel_name_start = 0;
 
  154  WINPR_ASSERT(context);
 
  155  WINPR_ASSERT(header);
 
  157  pdu.Header = *header;
 
  166  if (!Stream_CheckAndLogRequiredLength(TAG, s, 4))
 
  167    return ERROR_NO_DATA;
 
  169  pdu.DeviceName = Stream_Pointer(s);
 
  171  remaining_length = Stream_GetRemainingLength(s);
 
  172  channel_name_start = Stream_Pointer(s);
 
  176  for (; i < remaining_length; i += 
sizeof(WCHAR), ++channel_name_start)
 
  178    if (*channel_name_start == L
'\0')
 
  182  if (*channel_name_start != L
'\0')
 
  184    WLog_ERR(TAG, 
"enumerator_server_recv_device_added_notification: Invalid DeviceName!");
 
  185    return ERROR_INVALID_DATA;
 
  188  pdu.VirtualChannelName = (
char*)++channel_name_start;
 
  191  if (i >= remaining_length || *pdu.VirtualChannelName == 
'\0')
 
  194             "enumerator_server_recv_device_added_notification: Invalid VirtualChannelName!");
 
  195    return ERROR_INVALID_DATA;
 
  198  char* tmp = pdu.VirtualChannelName;
 
  199  for (; i < remaining_length; ++i, ++tmp)
 
  208             "enumerator_server_recv_device_added_notification: Invalid VirtualChannelName!");
 
  209    return ERROR_INVALID_DATA;
 
  212  IFCALLRET(context->DeviceAddedNotification, error, context, &pdu);
 
  214    WLog_ERR(TAG, 
"context->DeviceAddedNotification failed with error %" PRIu32 
"", error);
 
  219static UINT enumerator_server_recv_device_removed_notification(CamDevEnumServerContext* context,
 
  224  UINT error = CHANNEL_RC_OK;
 
  225  size_t remaining_length = 0;
 
  227  WINPR_ASSERT(context);
 
  228  WINPR_ASSERT(header);
 
  230  pdu.Header = *header;
 
  232  if (!Stream_CheckAndLogRequiredLength(TAG, s, 2))
 
  233    return ERROR_NO_DATA;
 
  235  pdu.VirtualChannelName = Stream_Pointer(s);
 
  237  remaining_length = Stream_GetRemainingLength(s);
 
  238  char* tmp = pdu.VirtualChannelName + 1;
 
  240  for (
size_t i = 1; i < remaining_length; ++i, ++tmp)
 
  249             "enumerator_server_recv_device_removed_notification: Invalid VirtualChannelName!");
 
  250    return ERROR_INVALID_DATA;
 
  253  IFCALLRET(context->DeviceRemovedNotification, error, context, &pdu);
 
  255    WLog_ERR(TAG, 
"context->DeviceRemovedNotification failed with error %" PRIu32 
"", error);
 
  260static UINT enumerator_process_message(enumerator_server* enumerator)
 
  263  UINT error = ERROR_INTERNAL_ERROR;
 
  264  ULONG BytesReturned = 0;
 
  268  WINPR_ASSERT(enumerator);
 
  269  WINPR_ASSERT(enumerator->enumerator_channel);
 
  271  s = enumerator->buffer;
 
  274  Stream_SetPosition(s, 0);
 
  275  rc = WTSVirtualChannelRead(enumerator->enumerator_channel, 0, NULL, 0, &BytesReturned);
 
  279  if (BytesReturned < 1)
 
  281    error = CHANNEL_RC_OK;
 
  285  if (!Stream_EnsureRemainingCapacity(s, BytesReturned))
 
  287    WLog_ERR(TAG, 
"Stream_EnsureRemainingCapacity failed!");
 
  288    error = CHANNEL_RC_NO_MEMORY;
 
  292  if (WTSVirtualChannelRead(enumerator->enumerator_channel, 0, Stream_BufferAs(s, 
char),
 
  293                            (ULONG)Stream_Capacity(s), &BytesReturned) == FALSE)
 
  295    WLog_ERR(TAG, 
"WTSVirtualChannelRead failed!");
 
  299  Stream_SetLength(s, BytesReturned);
 
  300  if (!Stream_CheckAndLogRequiredLength(TAG, s, CAM_HEADER_SIZE))
 
  301    return ERROR_NO_DATA;
 
  303  Stream_Read_UINT8(s, header.Version);
 
  304  Stream_Read_UINT8(s, header.MessageId);
 
  306  switch (header.MessageId)
 
  308    case CAM_MSG_ID_SelectVersionRequest:
 
  310          enumerator_server_handle_select_version_request(&enumerator->context, s, &header);
 
  312    case CAM_MSG_ID_DeviceAddedNotification:
 
  314          enumerator_server_recv_device_added_notification(&enumerator->context, s, &header);
 
  316    case CAM_MSG_ID_DeviceRemovedNotification:
 
  317      error = enumerator_server_recv_device_removed_notification(&enumerator->context, s,
 
  321      WLog_ERR(TAG, 
"enumerator_process_message: unknown or invalid MessageId %" PRIu8 
"",
 
  328    WLog_ERR(TAG, 
"Response failed with error %" PRIu32 
"!", error);
 
  333static UINT enumerator_server_context_poll_int(CamDevEnumServerContext* context)
 
  335  enumerator_server* enumerator = (enumerator_server*)context;
 
  336  UINT error = ERROR_INTERNAL_ERROR;
 
  338  WINPR_ASSERT(enumerator);
 
  340  switch (enumerator->state)
 
  342    case ENUMERATOR_INITIAL:
 
  343      error = enumerator_server_open_channel(enumerator);
 
  345        WLog_ERR(TAG, 
"enumerator_server_open_channel failed with error %" PRIu32 
"!",
 
  348        enumerator->state = ENUMERATOR_OPENED;
 
  350    case ENUMERATOR_OPENED:
 
  351      error = enumerator_process_message(enumerator);
 
  360static HANDLE enumerator_server_get_channel_handle(enumerator_server* enumerator)
 
  363  DWORD BytesReturned = 0;
 
  364  HANDLE ChannelEvent = NULL;
 
  366  WINPR_ASSERT(enumerator);
 
  368  if (WTSVirtualChannelQuery(enumerator->enumerator_channel, WTSVirtualEventHandle, &buffer,
 
  369                             &BytesReturned) == TRUE)
 
  371    if (BytesReturned == 
sizeof(HANDLE))
 
  372      ChannelEvent = *(HANDLE*)buffer;
 
  374    WTSFreeMemory(buffer);
 
  380static DWORD WINAPI enumerator_server_thread_func(LPVOID arg)
 
  383  HANDLE events[2] = { 0 };
 
  384  enumerator_server* enumerator = (enumerator_server*)arg;
 
  385  UINT error = CHANNEL_RC_OK;
 
  388  WINPR_ASSERT(enumerator);
 
  391  events[nCount++] = enumerator->stopEvent;
 
  393  while ((error == CHANNEL_RC_OK) && (WaitForSingleObject(events[0], 0) != WAIT_OBJECT_0))
 
  395    switch (enumerator->state)
 
  397      case ENUMERATOR_INITIAL:
 
  398        error = enumerator_server_context_poll_int(&enumerator->context);
 
  399        if (error == CHANNEL_RC_OK)
 
  401          events[1] = enumerator_server_get_channel_handle(enumerator);
 
  405      case ENUMERATOR_OPENED:
 
  406        status = WaitForMultipleObjects(nCount, events, FALSE, INFINITE);
 
  411          case WAIT_OBJECT_0 + 1:
 
  413            error = enumerator_server_context_poll_int(&enumerator->context);
 
  418            error = ERROR_INTERNAL_ERROR;
 
  427  (void)WTSVirtualChannelClose(enumerator->enumerator_channel);
 
  428  enumerator->enumerator_channel = NULL;
 
  430  if (error && enumerator->context.rdpcontext)
 
  431    setChannelError(enumerator->context.rdpcontext, error,
 
  432                    "enumerator_server_thread_func reported an error");
 
  438static UINT enumerator_server_open(CamDevEnumServerContext* context)
 
  440  enumerator_server* enumerator = (enumerator_server*)context;
 
  442  WINPR_ASSERT(enumerator);
 
  444  if (!enumerator->externalThread && (enumerator->thread == NULL))
 
  446    enumerator->stopEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
 
  447    if (!enumerator->stopEvent)
 
  449      WLog_ERR(TAG, 
"CreateEvent failed!");
 
  450      return ERROR_INTERNAL_ERROR;
 
  454        CreateThread(NULL, 0, enumerator_server_thread_func, enumerator, 0, NULL);
 
  455    if (!enumerator->thread)
 
  457      WLog_ERR(TAG, 
"CreateThread failed!");
 
  458      (void)CloseHandle(enumerator->stopEvent);
 
  459      enumerator->stopEvent = NULL;
 
  460      return ERROR_INTERNAL_ERROR;
 
  463  enumerator->isOpened = TRUE;
 
  465  return CHANNEL_RC_OK;
 
  468static UINT enumerator_server_close(CamDevEnumServerContext* context)
 
  470  UINT error = CHANNEL_RC_OK;
 
  471  enumerator_server* enumerator = (enumerator_server*)context;
 
  473  WINPR_ASSERT(enumerator);
 
  475  if (!enumerator->externalThread && enumerator->thread)
 
  477    (void)SetEvent(enumerator->stopEvent);
 
  479    if (WaitForSingleObject(enumerator->thread, INFINITE) == WAIT_FAILED)
 
  481      error = GetLastError();
 
  482      WLog_ERR(TAG, 
"WaitForSingleObject failed with error %" PRIu32 
"", error);
 
  486    (void)CloseHandle(enumerator->thread);
 
  487    (void)CloseHandle(enumerator->stopEvent);
 
  488    enumerator->thread = NULL;
 
  489    enumerator->stopEvent = NULL;
 
  491  if (enumerator->externalThread)
 
  493    if (enumerator->state != ENUMERATOR_INITIAL)
 
  495      (void)WTSVirtualChannelClose(enumerator->enumerator_channel);
 
  496      enumerator->enumerator_channel = NULL;
 
  497      enumerator->state = ENUMERATOR_INITIAL;
 
  500  enumerator->isOpened = FALSE;
 
  505static UINT enumerator_server_context_poll(CamDevEnumServerContext* context)
 
  507  enumerator_server* enumerator = (enumerator_server*)context;
 
  509  WINPR_ASSERT(enumerator);
 
  511  if (!enumerator->externalThread)
 
  512    return ERROR_INTERNAL_ERROR;
 
  514  return enumerator_server_context_poll_int(context);
 
  517static BOOL enumerator_server_context_handle(CamDevEnumServerContext* context, HANDLE* handle)
 
  519  enumerator_server* enumerator = (enumerator_server*)context;
 
  521  WINPR_ASSERT(enumerator);
 
  522  WINPR_ASSERT(handle);
 
  524  if (!enumerator->externalThread)
 
  526  if (enumerator->state == ENUMERATOR_INITIAL)
 
  529  *handle = enumerator_server_get_channel_handle(enumerator);
 
  534static UINT enumerator_server_packet_send(CamDevEnumServerContext* context, 
wStream* s)
 
  536  enumerator_server* enumerator = (enumerator_server*)context;
 
  537  UINT error = CHANNEL_RC_OK;
 
  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))
 
  545    WLog_ERR(TAG, 
"WTSVirtualChannelWrite failed!");
 
  546    error = ERROR_INTERNAL_ERROR;
 
  550  if (written < Stream_GetPosition(s))
 
  552    WLog_WARN(TAG, 
"Unexpected bytes written: %" PRIu32 
"/%" PRIuz 
"", written,
 
  553              Stream_GetPosition(s));
 
  557  Stream_Free(s, TRUE);
 
  561static UINT enumerator_send_select_version_response_pdu(
 
  566  s = Stream_New(NULL, CAM_HEADER_SIZE);
 
  569    WLog_ERR(TAG, 
"Stream_New failed!");
 
  570    return ERROR_NOT_ENOUGH_MEMORY;
 
  573  Stream_Write_UINT8(s, selectVersionResponse->Header.Version);
 
  574  Stream_Write_UINT8(s,
 
  575                     WINPR_ASSERTING_INT_CAST(uint8_t, selectVersionResponse->Header.MessageId));
 
  577  return enumerator_server_packet_send(context, s);
 
  580CamDevEnumServerContext* cam_dev_enum_server_context_new(HANDLE vcm)
 
  582  enumerator_server* enumerator = (enumerator_server*)calloc(1, 
sizeof(enumerator_server));
 
  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;
 
  594  enumerator->context.SelectVersionResponse = enumerator_send_select_version_response_pdu;
 
  596  enumerator->buffer = Stream_New(NULL, 4096);
 
  597  if (!enumerator->buffer)
 
  600  return &enumerator->context;
 
  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
 
  609void cam_dev_enum_server_context_free(CamDevEnumServerContext* context)
 
  611  enumerator_server* enumerator = (enumerator_server*)context;
 
  615    enumerator_server_close(context);
 
  616    Stream_Free(enumerator->buffer, TRUE);