FreeRDP
gfxredir_main.c
1 
20 #include <freerdp/config.h>
21 
22 #include "gfxredir_main.h"
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 #include <freerdp/channels/wtsvc.h>
34 #include <freerdp/channels/log.h>
35 
36 #include <freerdp/server/gfxredir.h>
37 #include <gfxredir_common.h>
38 
39 #define TAG CHANNELS_TAG("gfxredir.server")
40 
46 static UINT gfxredir_recv_legacy_caps_pdu(wStream* s, GfxRedirServerContext* context)
47 {
48  UINT32 error = CHANNEL_RC_OK;
49  GFXREDIR_LEGACY_CAPS_PDU pdu = { 0 };
50 
51  WINPR_ASSERT(context);
52 
53  if (!Stream_CheckAndLogRequiredLength(TAG, s, 2))
54  return ERROR_INVALID_DATA;
55 
56  Stream_Read_UINT16(s, pdu.version); /* version (2 bytes) */
57 
58  if (context)
59  IFCALLRET(context->GraphicsRedirectionLegacyCaps, error, context, &pdu);
60 
61  return error;
62 }
63 
69 static UINT gfxredir_recv_caps_advertise_pdu(wStream* s, UINT32 length,
70  GfxRedirServerContext* context)
71 {
72  UINT32 error = CHANNEL_RC_OK;
73  GFXREDIR_CAPS_ADVERTISE_PDU pdu = { 0 };
74 
75  WINPR_ASSERT(context);
76 
77  if (!Stream_CheckAndLogRequiredLength(TAG, s, length))
78  return ERROR_INVALID_DATA;
79 
80  pdu.length = length;
81  Stream_GetPointer(s, pdu.caps);
82  Stream_Seek(s, length);
83 
84  if (!context->GraphicsRedirectionCapsAdvertise)
85  {
86  WLog_ERR(TAG, "server does not support CapsAdvertise PDU!");
87  return ERROR_NOT_SUPPORTED;
88  }
89  else if (context)
90  {
91  IFCALLRET(context->GraphicsRedirectionCapsAdvertise, error, context, &pdu);
92  }
93 
94  return error;
95 }
96 
102 static UINT gfxredir_recv_present_buffer_ack_pdu(wStream* s, GfxRedirServerContext* context)
103 {
104  UINT32 error = CHANNEL_RC_OK;
106 
107  WINPR_ASSERT(context);
108 
109  if (!Stream_CheckAndLogRequiredLength(TAG, s, 16))
110  return ERROR_INVALID_DATA;
111 
112  Stream_Read_UINT64(s, pdu.windowId); /* windowId (8 bytes) */
113  Stream_Read_UINT64(s, pdu.presentId); /* presentId (8 bytes) */
114 
115  if (context)
116  {
117  IFCALLRET(context->PresentBufferAck, error, context, &pdu);
118  }
119 
120  return error;
121 }
122 
128 static UINT gfxredir_server_receive_pdu(GfxRedirServerContext* context, wStream* s)
129 {
130  UINT error = CHANNEL_RC_OK;
131  GFXREDIR_HEADER header = { 0 };
132 
133  WINPR_ASSERT(context);
134 
135  const size_t beg = Stream_GetPosition(s);
136 
137  if ((error = gfxredir_read_header(s, &header)))
138  {
139  WLog_ERR(TAG, "gfxredir_read_header failed with error %" PRIu32 "!", error);
140  return error;
141  }
142 
143  switch (header.cmdId)
144  {
145  case GFXREDIR_CMDID_LEGACY_CAPS:
146  if ((error = gfxredir_recv_legacy_caps_pdu(s, context)))
147  WLog_ERR(TAG,
148  "gfxredir_recv_legacy_caps_pdu "
149  "failed with error %" PRIu32 "!",
150  error);
151 
152  break;
153 
154  case GFXREDIR_CMDID_CAPS_ADVERTISE:
155  if ((error = gfxredir_recv_caps_advertise_pdu(s, header.length - 8, context)))
156  WLog_ERR(TAG,
157  "gfxredir_recv_caps_advertise "
158  "failed with error %" PRIu32 "!",
159  error);
160  break;
161 
162  case GFXREDIR_CMDID_PRESENT_BUFFER_ACK:
163  if ((error = gfxredir_recv_present_buffer_ack_pdu(s, context)))
164  WLog_ERR(TAG,
165  "gfxredir_recv_present_buffer_ask_pdu "
166  "failed with error %" PRIu32 "!",
167  error);
168  break;
169 
170  default:
171  error = CHANNEL_RC_BAD_PROC;
172  WLog_WARN(TAG, "Received unknown PDU type: %" PRIu32 "", header.cmdId);
173  break;
174  }
175 
176  const size_t end = Stream_GetPosition(s);
177 
178  if (end != (beg + header.length))
179  {
180  WLog_ERR(TAG, "Unexpected GFXREDIR pdu end: Actual: %d, Expected: %" PRIu32 "", end,
181  (beg + header.length));
182  Stream_SetPosition(s, (beg + header.length));
183  }
184 
185  return error;
186 }
187 
193 static UINT gfxredir_server_handle_messages(GfxRedirServerContext* context)
194 {
195  UINT ret = CHANNEL_RC_OK;
196 
197  WINPR_ASSERT(context);
198 
199  GfxRedirServerPrivate* priv = context->priv;
200  WINPR_ASSERT(priv);
201 
202  wStream* s = priv->input_stream;
203 
204  /* Check whether the dynamic channel is ready */
205  if (!priv->isReady)
206  {
207  void* buffer = NULL;
208  DWORD BytesReturned = 0;
209  if (WTSVirtualChannelQuery(priv->gfxredir_channel, WTSVirtualChannelReady, &buffer,
210  &BytesReturned) == FALSE)
211  {
212  if (GetLastError() == ERROR_NO_DATA)
213  return ERROR_NO_DATA;
214 
215  WLog_ERR(TAG, "WTSVirtualChannelQuery failed");
216  return ERROR_INTERNAL_ERROR;
217  }
218 
219  priv->isReady = *((BOOL*)buffer);
220  WTSFreeMemory(buffer);
221  }
222 
223  /* Consume channel event only after the dynamic channel is ready */
224  if (priv->isReady)
225  {
226  Stream_SetPosition(s, 0);
227 
228  DWORD BytesReturned = 0;
229  if (!WTSVirtualChannelRead(priv->gfxredir_channel, 0, NULL, 0, &BytesReturned))
230  {
231  if (GetLastError() == ERROR_NO_DATA)
232  return ERROR_NO_DATA;
233 
234  WLog_ERR(TAG, "WTSVirtualChannelRead failed!");
235  return ERROR_INTERNAL_ERROR;
236  }
237 
238  if (BytesReturned < 1)
239  return CHANNEL_RC_OK;
240 
241  if (!Stream_EnsureRemainingCapacity(s, BytesReturned))
242  {
243  WLog_ERR(TAG, "Stream_EnsureRemainingCapacity failed!");
244  return CHANNEL_RC_NO_MEMORY;
245  }
246 
247  const ULONG cap = WINPR_ASSERTING_INT_CAST(ULONG, Stream_Capacity(s));
248  if (WTSVirtualChannelRead(priv->gfxredir_channel, 0, (PCHAR)Stream_Buffer(s), cap,
249  &BytesReturned) == FALSE)
250  {
251  WLog_ERR(TAG, "WTSVirtualChannelRead failed!");
252  return ERROR_INTERNAL_ERROR;
253  }
254 
255  Stream_SetLength(s, BytesReturned);
256  Stream_SetPosition(s, 0);
257 
258  while (Stream_GetPosition(s) < Stream_Length(s))
259  {
260  if ((ret = gfxredir_server_receive_pdu(context, s)))
261  {
262  WLog_ERR(TAG,
263  "gfxredir_server_receive_pdu "
264  "failed with error %" PRIu32 "!",
265  ret);
266  return ret;
267  }
268  }
269  }
270 
271  return ret;
272 }
273 
279 static DWORD WINAPI gfxredir_server_thread_func(LPVOID arg)
280 {
281  GfxRedirServerContext* context = (GfxRedirServerContext*)arg;
282  WINPR_ASSERT(context);
283 
284  GfxRedirServerPrivate* priv = context->priv;
285  WINPR_ASSERT(priv);
286 
287  HANDLE events[8] = { 0 };
288  UINT error = CHANNEL_RC_OK;
289  DWORD nCount = 0;
290  events[nCount++] = priv->stopEvent;
291  events[nCount++] = priv->channelEvent;
292 
293  while (TRUE)
294  {
295  const DWORD status = WaitForMultipleObjects(nCount, events, FALSE, INFINITE);
296 
297  if (status == WAIT_FAILED)
298  {
299  error = GetLastError();
300  WLog_ERR(TAG, "WaitForMultipleObjects failed with error %" PRIu32 "", error);
301  break;
302  }
303 
304  /* Stop Event */
305  if (status == WAIT_OBJECT_0)
306  break;
307 
308  if ((error = gfxredir_server_handle_messages(context)))
309  {
310  WLog_ERR(TAG, "gfxredir_server_handle_messages failed with error %" PRIu32 "", error);
311  break;
312  }
313  }
314 
315  ExitThread(error);
316  return error;
317 }
318 
330 static wStream* gfxredir_server_single_packet_new(UINT32 cmdId, size_t length)
331 {
332  GFXREDIR_HEADER header = { 0 };
333  wStream* s = Stream_New(NULL, GFXREDIR_HEADER_SIZE + length);
334 
335  if (!s)
336  {
337  WLog_ERR(TAG, "Stream_New failed!");
338  goto error;
339  }
340 
341  header.cmdId = cmdId;
342  header.length = WINPR_ASSERTING_INT_CAST(UINT32, GFXREDIR_HEADER_SIZE + length);
343 
344  const UINT error = gfxredir_write_header(s, &header);
345  if (error)
346  {
347  WLog_ERR(TAG, "Failed to write header with error %" PRIu32 "!", error);
348  goto error;
349  }
350 
351  return s;
352 error:
353  Stream_Free(s, TRUE);
354  return NULL;
355 }
356 
362 static UINT gfxredir_server_packet_send(GfxRedirServerContext* context, wStream* s)
363 {
364  UINT ret = ERROR_INTERNAL_ERROR;
365  ULONG written = 0;
366 
367  WINPR_ASSERT(context);
368 
369  const ULONG cap = WINPR_ASSERTING_INT_CAST(ULONG, Stream_GetPosition(s));
370  if (!WTSVirtualChannelWrite(context->priv->gfxredir_channel, (PCHAR)Stream_Buffer(s), cap,
371  &written))
372  {
373  WLog_ERR(TAG, "WTSVirtualChannelWrite failed!");
374  ret = ERROR_INTERNAL_ERROR;
375  goto out;
376  }
377 
378  if (written < Stream_GetPosition(s))
379  {
380  WLog_WARN(TAG, "Unexpected bytes written: %" PRIu32 "/%" PRIuz "", written,
381  Stream_GetPosition(s));
382  }
383 
384  ret = CHANNEL_RC_OK;
385 out:
386  Stream_Free(s, TRUE);
387  return ret;
388 }
389 
395 static UINT gfxredir_send_error(GfxRedirServerContext* context, const GFXREDIR_ERROR_PDU* error)
396 {
397  WINPR_ASSERT(context);
398  WINPR_ASSERT(error);
399 
400  wStream* s = gfxredir_server_single_packet_new(GFXREDIR_CMDID_ERROR, 4);
401 
402  if (!s)
403  {
404  WLog_ERR(TAG, "gfxredir_server_single_packet_new failed!");
405  return CHANNEL_RC_NO_MEMORY;
406  }
407 
408  Stream_Write_UINT32(s, error->errorCode);
409  return gfxredir_server_packet_send(context, s);
410 }
411 
417 static UINT gfxredir_send_caps_confirm(GfxRedirServerContext* context,
418  const GFXREDIR_CAPS_CONFIRM_PDU* graphicsCapsConfirm)
419 {
420  WINPR_ASSERT(context);
421  WINPR_ASSERT(graphicsCapsConfirm);
422 
423  if (graphicsCapsConfirm->length < GFXREDIR_CAPS_HEADER_SIZE)
424  {
425  WLog_ERR(TAG, "length must be greater than header size, failed!");
426  return ERROR_INVALID_DATA;
427  }
428 
429  wStream* s =
430  gfxredir_server_single_packet_new(GFXREDIR_CMDID_CAPS_CONFIRM, graphicsCapsConfirm->length);
431 
432  if (!s)
433  {
434  WLog_ERR(TAG, "gfxredir_server_single_packet_new failed!");
435  return CHANNEL_RC_NO_MEMORY;
436  }
437 
438  Stream_Write_UINT32(s, GFXREDIR_CAPS_SIGNATURE);
439  Stream_Write_UINT32(s, graphicsCapsConfirm->version);
440  Stream_Write_UINT32(s, graphicsCapsConfirm->length);
441  if (graphicsCapsConfirm->length > GFXREDIR_CAPS_HEADER_SIZE)
442  Stream_Write(s, graphicsCapsConfirm->capsData,
443  graphicsCapsConfirm->length - GFXREDIR_CAPS_HEADER_SIZE);
444  const UINT ret = gfxredir_server_packet_send(context, s);
445  if (ret == CHANNEL_RC_OK)
446  context->confirmedCapsVersion = graphicsCapsConfirm->version;
447  return ret;
448 }
449 
455 static UINT gfxredir_send_open_pool(GfxRedirServerContext* context,
456  const GFXREDIR_OPEN_POOL_PDU* openPool)
457 {
458  WINPR_ASSERT(context);
459  WINPR_ASSERT(openPool);
460 
461  if (context->confirmedCapsVersion != GFXREDIR_CAPS_VERSION2_0)
462  {
463  WLog_ERR(TAG, "open_pool is called with invalid version!");
464  return ERROR_INTERNAL_ERROR;
465  }
466 
467  if (openPool->sectionNameLength == 0 || openPool->sectionName == NULL)
468  {
469  WLog_ERR(TAG, "section name must be provided!");
470  return ERROR_INVALID_DATA;
471  }
472 
473  /* make sure null-terminate */
474  if (openPool->sectionName[openPool->sectionNameLength - 1] != 0)
475  {
476  WLog_ERR(TAG, "section name must be terminated with NULL!");
477  return ERROR_INVALID_DATA;
478  }
479 
480  wStream* s = gfxredir_server_single_packet_new(GFXREDIR_CMDID_OPEN_POOL,
481  20 + (openPool->sectionNameLength * 2));
482 
483  if (!s)
484  {
485  WLog_ERR(TAG, "gfxredir_server_single_packet_new failed!");
486  return CHANNEL_RC_NO_MEMORY;
487  }
488 
489  Stream_Write_UINT64(s, openPool->poolId);
490  Stream_Write_UINT64(s, openPool->poolSize);
491  Stream_Write_UINT32(s, openPool->sectionNameLength);
492  Stream_Write(s, openPool->sectionName, (2ULL * openPool->sectionNameLength));
493  return gfxredir_server_packet_send(context, s);
494 }
495 
501 static UINT gfxredir_send_close_pool(GfxRedirServerContext* context,
502  const GFXREDIR_CLOSE_POOL_PDU* closePool)
503 {
504  WINPR_ASSERT(context);
505  WINPR_ASSERT(closePool);
506 
507  if (context->confirmedCapsVersion != GFXREDIR_CAPS_VERSION2_0)
508  {
509  WLog_ERR(TAG, "close_pool is called with invalid version!");
510  return ERROR_INTERNAL_ERROR;
511  }
512 
513  wStream* s = gfxredir_server_single_packet_new(GFXREDIR_CMDID_CLOSE_POOL, 8);
514 
515  if (!s)
516  {
517  WLog_ERR(TAG, "gfxredir_server_single_packet_new failed!");
518  return CHANNEL_RC_NO_MEMORY;
519  }
520 
521  Stream_Write_UINT64(s, closePool->poolId);
522  return gfxredir_server_packet_send(context, s);
523 }
524 
530 static UINT gfxredir_send_create_buffer(GfxRedirServerContext* context,
531  const GFXREDIR_CREATE_BUFFER_PDU* createBuffer)
532 {
533  WINPR_ASSERT(context);
534  WINPR_ASSERT(createBuffer);
535 
536  if (context->confirmedCapsVersion != GFXREDIR_CAPS_VERSION2_0)
537  {
538  WLog_ERR(TAG, "create_buffer is called with invalid version!");
539  return ERROR_INTERNAL_ERROR;
540  }
541 
542  wStream* s = gfxredir_server_single_packet_new(GFXREDIR_CMDID_CREATE_BUFFER, 40);
543 
544  if (!s)
545  {
546  WLog_ERR(TAG, "gfxredir_server_single_packet_new failed!");
547  return CHANNEL_RC_NO_MEMORY;
548  }
549 
550  Stream_Write_UINT64(s, createBuffer->poolId);
551  Stream_Write_UINT64(s, createBuffer->bufferId);
552  Stream_Write_UINT64(s, createBuffer->offset);
553  Stream_Write_UINT32(s, createBuffer->stride);
554  Stream_Write_UINT32(s, createBuffer->width);
555  Stream_Write_UINT32(s, createBuffer->height);
556  Stream_Write_UINT32(s, createBuffer->format);
557  return gfxredir_server_packet_send(context, s);
558 }
559 
565 static UINT gfxredir_send_destroy_buffer(GfxRedirServerContext* context,
566  const GFXREDIR_DESTROY_BUFFER_PDU* destroyBuffer)
567 {
568  WINPR_ASSERT(context);
569  WINPR_ASSERT(destroyBuffer);
570 
571  if (context->confirmedCapsVersion != GFXREDIR_CAPS_VERSION2_0)
572  {
573  WLog_ERR(TAG, "destroy_buffer is called with invalid version!");
574  return ERROR_INTERNAL_ERROR;
575  }
576 
577  wStream* s = gfxredir_server_single_packet_new(GFXREDIR_CMDID_DESTROY_BUFFER, 8);
578 
579  if (!s)
580  {
581  WLog_ERR(TAG, "gfxredir_server_single_packet_new failed!");
582  return CHANNEL_RC_NO_MEMORY;
583  }
584 
585  Stream_Write_UINT64(s, destroyBuffer->bufferId);
586  return gfxredir_server_packet_send(context, s);
587 }
588 
594 static UINT gfxredir_send_present_buffer(GfxRedirServerContext* context,
595  const GFXREDIR_PRESENT_BUFFER_PDU* presentBuffer)
596 {
597  RECTANGLE_32 dummyRect = { 0, 0, 0, 0 };
598 
599  WINPR_ASSERT(context);
600  WINPR_ASSERT(presentBuffer);
601 
602  if (context->confirmedCapsVersion != GFXREDIR_CAPS_VERSION2_0)
603  {
604  WLog_ERR(TAG, "present_buffer is called with invalid version!");
605  return ERROR_INTERNAL_ERROR;
606  }
607 
608  if (presentBuffer->numOpaqueRects > GFXREDIR_MAX_OPAQUE_RECTS)
609  {
610  WLog_ERR(TAG, "numOpaqueRects is larger than its limit!");
611  return ERROR_INVALID_DATA;
612  }
613 
614  const size_t length =
615  64ULL + ((presentBuffer->numOpaqueRects ? presentBuffer->numOpaqueRects : 1) *
616  sizeof(RECTANGLE_32));
617 
618  wStream* s = gfxredir_server_single_packet_new(GFXREDIR_CMDID_PRESENT_BUFFER, length);
619 
620  if (!s)
621  {
622  WLog_ERR(TAG, "gfxredir_server_single_packet_new failed!");
623  return CHANNEL_RC_NO_MEMORY;
624  }
625 
626  Stream_Write_UINT64(s, presentBuffer->timestamp);
627  Stream_Write_UINT64(s, presentBuffer->presentId);
628  Stream_Write_UINT64(s, presentBuffer->windowId);
629  Stream_Write_UINT64(s, presentBuffer->bufferId);
630  Stream_Write_UINT32(s, presentBuffer->orientation);
631  Stream_Write_UINT32(s, presentBuffer->targetWidth);
632  Stream_Write_UINT32(s, presentBuffer->targetHeight);
633  Stream_Write_UINT32(s, presentBuffer->dirtyRect.left);
634  Stream_Write_UINT32(s, presentBuffer->dirtyRect.top);
635  Stream_Write_UINT32(s, presentBuffer->dirtyRect.width);
636  Stream_Write_UINT32(s, presentBuffer->dirtyRect.height);
637  Stream_Write_UINT32(s, presentBuffer->numOpaqueRects);
638  if (presentBuffer->numOpaqueRects)
639  Stream_Write(s, presentBuffer->opaqueRects,
640  (presentBuffer->numOpaqueRects * sizeof(RECTANGLE_32)));
641  else
642  Stream_Write(s, &dummyRect, sizeof(RECTANGLE_32));
643  return gfxredir_server_packet_send(context, s);
644 }
645 
651 static UINT gfxredir_server_open(GfxRedirServerContext* context)
652 {
653  UINT rc = ERROR_INTERNAL_ERROR;
654  WINPR_ASSERT(context);
655 
656  GfxRedirServerPrivate* priv = context->priv;
657  WINPR_ASSERT(priv);
658 
659  DWORD BytesReturned = 0;
660  PULONG pSessionId = NULL;
661  void* buffer = NULL;
662  priv->SessionId = WTS_CURRENT_SESSION;
663 
664  if (WTSQuerySessionInformationA(context->vcm, WTS_CURRENT_SESSION, WTSSessionId,
665  (LPSTR*)&pSessionId, &BytesReturned) == FALSE)
666  {
667  WLog_ERR(TAG, "WTSQuerySessionInformationA failed!");
668  rc = ERROR_INTERNAL_ERROR;
669  goto out_close;
670  }
671 
672  priv->SessionId = (DWORD)*pSessionId;
673  WTSFreeMemory(pSessionId);
674  priv->gfxredir_channel = WTSVirtualChannelOpenEx(priv->SessionId, GFXREDIR_DVC_CHANNEL_NAME,
675  WTS_CHANNEL_OPTION_DYNAMIC);
676 
677  if (!priv->gfxredir_channel)
678  {
679  WLog_ERR(TAG, "WTSVirtualChannelOpenEx failed!");
680  rc = GetLastError();
681  goto out_close;
682  }
683 
684  /* Query for channel event handle */
685  if (!WTSVirtualChannelQuery(priv->gfxredir_channel, WTSVirtualEventHandle, &buffer,
686  &BytesReturned) ||
687  (BytesReturned != sizeof(HANDLE)))
688  {
689  WLog_ERR(TAG,
690  "WTSVirtualChannelQuery failed "
691  "or invalid returned size(%" PRIu32 ")",
692  BytesReturned);
693 
694  if (buffer)
695  WTSFreeMemory(buffer);
696 
697  rc = ERROR_INTERNAL_ERROR;
698  goto out_close;
699  }
700 
701  priv->channelEvent = *(HANDLE*)buffer;
702  WTSFreeMemory(buffer);
703 
704  if (priv->thread == NULL)
705  {
706  if (!(priv->stopEvent = CreateEvent(NULL, TRUE, FALSE, NULL)))
707  {
708  WLog_ERR(TAG, "CreateEvent failed!");
709  goto out_close;
710  }
711 
712  if (!(priv->thread =
713  CreateThread(NULL, 0, gfxredir_server_thread_func, (void*)context, 0, NULL)))
714  {
715  WLog_ERR(TAG, "CreateEvent failed!");
716  (void)CloseHandle(priv->stopEvent);
717  priv->stopEvent = NULL;
718  goto out_close;
719  }
720  }
721 
722  return CHANNEL_RC_OK;
723 out_close:
724  WTSVirtualChannelClose(priv->gfxredir_channel);
725  priv->gfxredir_channel = NULL;
726  priv->channelEvent = NULL;
727  return rc;
728 }
729 
735 static UINT gfxredir_server_close(GfxRedirServerContext* context)
736 {
737  UINT error = CHANNEL_RC_OK;
738  WINPR_ASSERT(context);
739 
740  GfxRedirServerPrivate* priv = context->priv;
741  WINPR_ASSERT(priv);
742 
743  if (priv->thread)
744  {
745  (void)SetEvent(priv->stopEvent);
746 
747  if (WaitForSingleObject(priv->thread, INFINITE) == WAIT_FAILED)
748  {
749  error = GetLastError();
750  WLog_ERR(TAG, "WaitForSingleObject failed with error %" PRIu32 "", error);
751  return error;
752  }
753 
754  (void)CloseHandle(priv->thread);
755  (void)CloseHandle(priv->stopEvent);
756  priv->thread = NULL;
757  priv->stopEvent = NULL;
758  }
759 
760  if (priv->gfxredir_channel)
761  {
762  WTSVirtualChannelClose(priv->gfxredir_channel);
763  priv->gfxredir_channel = NULL;
764  }
765 
766  return error;
767 }
768 
769 GfxRedirServerContext* gfxredir_server_context_new(HANDLE vcm)
770 {
771  GfxRedirServerContext* context =
772  (GfxRedirServerContext*)calloc(1, sizeof(GfxRedirServerContext));
773 
774  if (!context)
775  {
776  WLog_ERR(TAG, "gfxredir_server_context_new(): calloc GfxRedirServerContext failed!");
777  return NULL;
778  }
779 
780  GfxRedirServerPrivate* priv = context->priv =
781  (GfxRedirServerPrivate*)calloc(1, sizeof(GfxRedirServerPrivate));
782 
783  if (!context->priv)
784  {
785  WLog_ERR(TAG, "gfxredir_server_context_new(): calloc GfxRedirServerPrivate failed!");
786  goto fail;
787  }
788 
789  priv->input_stream = Stream_New(NULL, 4);
790 
791  if (!priv->input_stream)
792  {
793  WLog_ERR(TAG, "Stream_New failed!");
794  goto fail;
795  }
796 
797  context->vcm = vcm;
798  context->Open = gfxredir_server_open;
799  context->Close = gfxredir_server_close;
800  context->Error = gfxredir_send_error;
801  context->GraphicsRedirectionCapsConfirm = gfxredir_send_caps_confirm;
802  context->OpenPool = gfxredir_send_open_pool;
803  context->ClosePool = gfxredir_send_close_pool;
804  context->CreateBuffer = gfxredir_send_create_buffer;
805  context->DestroyBuffer = gfxredir_send_destroy_buffer;
806  context->PresentBuffer = gfxredir_send_present_buffer;
807  context->confirmedCapsVersion = GFXREDIR_CAPS_VERSION1;
808  priv->isReady = FALSE;
809  return context;
810 fail:
811  gfxredir_server_context_free(context);
812  return NULL;
813 }
814 
815 void gfxredir_server_context_free(GfxRedirServerContext* context)
816 {
817  if (!context)
818  return;
819 
820  gfxredir_server_close(context);
821 
822  if (context->priv)
823  {
824  Stream_Free(context->priv->input_stream, TRUE);
825  free(context->priv);
826  }
827 
828  free(context);
829 }