22#include <freerdp/config.h>
24#include <freerdp/freerdp.h>
25#include <freerdp/channels/log.h>
26#include <freerdp/server/location.h>
27#include <freerdp/utils/encoded_types.h>
29#define TAG CHANNELS_TAG("location.server")
35} eLocationChannelState;
39 LocationServerContext context;
44 void* location_channel;
52 eLocationChannelState state;
57static UINT location_server_initialize(LocationServerContext* context, BOOL externalThread)
59 UINT error = CHANNEL_RC_OK;
60 location_server* location = (location_server*)context;
62 WINPR_ASSERT(location);
64 if (location->isOpened)
66 WLog_WARN(TAG,
"Application error: Location channel already initialized, "
67 "calling in this state is not possible!");
68 return ERROR_INVALID_STATE;
71 location->externalThread = externalThread;
76static UINT location_server_open_channel(location_server* location)
78 LocationServerContext* context = &location->context;
79 DWORD Error = ERROR_SUCCESS;
81 DWORD BytesReturned = 0;
82 PULONG pSessionId = NULL;
86 WINPR_ASSERT(location);
88 if (WTSQuerySessionInformationA(location->context.vcm, WTS_CURRENT_SESSION, WTSSessionId,
89 (LPSTR*)&pSessionId, &BytesReturned) == FALSE)
91 WLog_ERR(TAG,
"WTSQuerySessionInformationA failed!");
92 return ERROR_INTERNAL_ERROR;
95 location->SessionId = (DWORD)*pSessionId;
96 WTSFreeMemory(pSessionId);
97 hEvent = WTSVirtualChannelManagerGetEventHandle(location->context.vcm);
99 if (WaitForSingleObject(hEvent, 1000) == WAIT_FAILED)
101 Error = GetLastError();
102 WLog_ERR(TAG,
"WaitForSingleObject failed with error %" PRIu32
"!", Error);
106 location->location_channel = WTSVirtualChannelOpenEx(
107 location->SessionId, LOCATION_DVC_CHANNEL_NAME, WTS_CHANNEL_OPTION_DYNAMIC);
108 if (!location->location_channel)
110 Error = GetLastError();
111 WLog_ERR(TAG,
"WTSVirtualChannelOpenEx failed with error %" PRIu32
"!", Error);
115 channelId = WTSChannelGetIdByHandle(location->location_channel);
117 IFCALLRET(context->ChannelIdAssigned, status, context, channelId);
120 WLog_ERR(TAG,
"context->ChannelIdAssigned failed!");
121 return ERROR_INTERNAL_ERROR;
127static UINT location_server_recv_client_ready(LocationServerContext* context,
wStream* s,
130 UINT error = CHANNEL_RC_OK;
132 WINPR_ASSERT(context);
134 WINPR_ASSERT(header);
137 .protocolVersion = RDPLOCATION_PROTOCOL_VERSION_100,
140 if (!Stream_CheckAndLogRequiredLength(TAG, s, 4))
141 return ERROR_NO_DATA;
144 const UINT32 version = Stream_Get_UINT32(s);
147 case RDPLOCATION_PROTOCOL_VERSION_100:
148 pdu.protocolVersion = RDPLOCATION_PROTOCOL_VERSION_100;
150 case RDPLOCATION_PROTOCOL_VERSION_200:
151 pdu.protocolVersion = RDPLOCATION_PROTOCOL_VERSION_200;
154 pdu.protocolVersion = RDPLOCATION_PROTOCOL_VERSION_200;
156 "Received unsupported protocol version %" PRIu32
157 ", setting to highest supported %u",
158 version, pdu.protocolVersion);
163 if (Stream_GetRemainingLength(s) >= 4)
164 Stream_Read_UINT32(s, pdu.flags);
166 IFCALLRET(context->ClientReady, error, context, &pdu);
168 WLog_ERR(TAG,
"context->ClientReady failed with error %" PRIu32
"", error);
173static UINT location_server_recv_base_location3d(LocationServerContext* context,
wStream* s,
176 UINT error = CHANNEL_RC_OK;
178 double heading = 0.0;
179 double horizontalAccuracy = 0.0;
180 LOCATIONSOURCE source = LOCATIONSOURCE_IP;
182 WINPR_ASSERT(context);
184 WINPR_ASSERT(header);
192 .horizontalAccuracy = NULL,
195 if (!freerdp_read_four_byte_float(s, &pdu.latitude) ||
196 !freerdp_read_four_byte_float(s, &pdu.longitude) ||
197 !freerdp_read_four_byte_signed_integer(s, &pdu.altitude))
200 if (Stream_GetRemainingLength(s) >= 1)
202 if (!freerdp_read_four_byte_float(s, &speed) ||
203 !freerdp_read_four_byte_float(s, &heading) ||
204 !freerdp_read_four_byte_float(s, &horizontalAccuracy) ||
205 !Stream_CheckAndLogRequiredLength(TAG, s, 1))
209 const UINT8 src = Stream_Get_UINT8(s);
212 case LOCATIONSOURCE_IP:
213 case LOCATIONSOURCE_WIFI:
214 case LOCATIONSOURCE_CELL:
215 case LOCATIONSOURCE_GNSS:
218 WLog_ERR(TAG,
"Invalid LOCATIONSOURCE value %" PRIu8
"", src);
221 source = (LOCATIONSOURCE)src;
225 pdu.heading = &heading;
226 pdu.horizontalAccuracy = &horizontalAccuracy;
227 pdu.source = &source;
230 IFCALLRET(context->BaseLocation3D, error, context, &pdu);
232 WLog_ERR(TAG,
"context->BaseLocation3D failed with error %" PRIu32
"", error);
237static UINT location_server_recv_location2d_delta(LocationServerContext* context,
wStream* s,
240 UINT error = CHANNEL_RC_OK;
241 double speedDelta = 0.0;
242 double headingDelta = 0.0;
244 WINPR_ASSERT(context);
246 WINPR_ASSERT(header);
249 .latitudeDelta = FP_NAN,
250 .longitudeDelta = FP_NAN,
252 .headingDelta = NULL };
254 if (!freerdp_read_four_byte_float(s, &pdu.latitudeDelta) ||
255 !freerdp_read_four_byte_float(s, &pdu.longitudeDelta))
258 if (Stream_GetRemainingLength(s) >= 1)
260 if (!freerdp_read_four_byte_float(s, &speedDelta) ||
261 !freerdp_read_four_byte_float(s, &headingDelta))
264 pdu.speedDelta = &speedDelta;
265 pdu.headingDelta = &headingDelta;
268 IFCALLRET(context->Location2DDelta, error, context, &pdu);
270 WLog_ERR(TAG,
"context->Location2DDelta failed with error %" PRIu32
"", error);
275static UINT location_server_recv_location3d_delta(LocationServerContext* context,
wStream* s,
278 UINT error = CHANNEL_RC_OK;
279 double speedDelta = 0.0;
280 double headingDelta = 0.0;
282 WINPR_ASSERT(context);
284 WINPR_ASSERT(header);
287 .latitudeDelta = FP_NAN,
288 .longitudeDelta = FP_NAN,
290 .headingDelta = NULL };
292 if (!freerdp_read_four_byte_float(s, &pdu.latitudeDelta) ||
293 !freerdp_read_four_byte_float(s, &pdu.longitudeDelta) ||
294 !freerdp_read_four_byte_signed_integer(s, &pdu.altitudeDelta))
297 if (Stream_GetRemainingLength(s) >= 1)
299 if (!freerdp_read_four_byte_float(s, &speedDelta) ||
300 !freerdp_read_four_byte_float(s, &headingDelta))
303 pdu.speedDelta = &speedDelta;
304 pdu.headingDelta = &headingDelta;
307 IFCALLRET(context->Location3DDelta, error, context, &pdu);
309 WLog_ERR(TAG,
"context->Location3DDelta failed with error %" PRIu32
"", error);
314static UINT location_process_message(location_server* location)
316 UINT error = ERROR_INTERNAL_ERROR;
317 ULONG BytesReturned = 0;
319 WINPR_ASSERT(location);
320 WINPR_ASSERT(location->location_channel);
325 Stream_SetPosition(s, 0);
326 const BOOL rc = WTSVirtualChannelRead(location->location_channel, 0, NULL, 0, &BytesReturned);
330 if (BytesReturned < 1)
332 error = CHANNEL_RC_OK;
336 if (!Stream_EnsureRemainingCapacity(s, BytesReturned))
338 WLog_ERR(TAG,
"Stream_EnsureRemainingCapacity failed!");
339 error = CHANNEL_RC_NO_MEMORY;
343 if (WTSVirtualChannelRead(location->location_channel, 0, Stream_BufferAs(s,
char),
344 (ULONG)Stream_Capacity(s), &BytesReturned) == FALSE)
346 WLog_ERR(TAG,
"WTSVirtualChannelRead failed!");
350 Stream_SetLength(s, BytesReturned);
351 if (!Stream_CheckAndLogRequiredLength(TAG, s, LOCATION_HEADER_SIZE))
352 return ERROR_NO_DATA;
355 const UINT16 pduType = Stream_Get_UINT16(s);
357 .pduLength = Stream_Get_UINT32(s) };
361 case PDUTYPE_CLIENT_READY:
362 error = location_server_recv_client_ready(&location->context, s, &header);
364 case PDUTYPE_BASE_LOCATION3D:
365 error = location_server_recv_base_location3d(&location->context, s, &header);
367 case PDUTYPE_LOCATION2D_DELTA:
368 error = location_server_recv_location2d_delta(&location->context, s, &header);
370 case PDUTYPE_LOCATION3D_DELTA:
371 error = location_server_recv_location3d_delta(&location->context, s, &header);
374 WLog_ERR(TAG,
"location_process_message: unknown or invalid pduType %" PRIu16
"",
382 WLog_ERR(TAG,
"Response failed with error %" PRIu32
"!", error);
387static UINT location_server_context_poll_int(LocationServerContext* context)
389 location_server* location = (location_server*)context;
390 UINT error = ERROR_INTERNAL_ERROR;
392 WINPR_ASSERT(location);
394 switch (location->state)
396 case LOCATION_INITIAL:
397 error = location_server_open_channel(location);
399 WLog_ERR(TAG,
"location_server_open_channel failed with error %" PRIu32
"!", error);
401 location->state = LOCATION_OPENED;
403 case LOCATION_OPENED:
404 error = location_process_message(location);
413static HANDLE location_server_get_channel_handle(location_server* location)
416 DWORD BytesReturned = 0;
417 HANDLE ChannelEvent = NULL;
419 WINPR_ASSERT(location);
421 if (WTSVirtualChannelQuery(location->location_channel, WTSVirtualEventHandle, &buffer,
422 &BytesReturned) == TRUE)
424 if (BytesReturned ==
sizeof(HANDLE))
425 ChannelEvent = *(HANDLE*)buffer;
427 WTSFreeMemory(buffer);
433static DWORD WINAPI location_server_thread_func(LPVOID arg)
436 HANDLE events[2] = { 0 };
437 location_server* location = (location_server*)arg;
438 UINT error = CHANNEL_RC_OK;
441 WINPR_ASSERT(location);
444 events[nCount++] = location->stopEvent;
446 while ((error == CHANNEL_RC_OK) && (WaitForSingleObject(events[0], 0) != WAIT_OBJECT_0))
448 switch (location->state)
450 case LOCATION_INITIAL:
451 error = location_server_context_poll_int(&location->context);
452 if (error == CHANNEL_RC_OK)
454 events[1] = location_server_get_channel_handle(location);
458 case LOCATION_OPENED:
459 status = WaitForMultipleObjects(nCount, events, FALSE, INFINITE);
464 case WAIT_OBJECT_0 + 1:
466 error = location_server_context_poll_int(&location->context);
471 error = ERROR_INTERNAL_ERROR;
480 (void)WTSVirtualChannelClose(location->location_channel);
481 location->location_channel = NULL;
483 if (error && location->context.rdpcontext)
484 setChannelError(location->context.rdpcontext, error,
485 "location_server_thread_func reported an error");
491static UINT location_server_open(LocationServerContext* context)
493 location_server* location = (location_server*)context;
495 WINPR_ASSERT(location);
497 if (!location->externalThread && (location->thread == NULL))
499 location->stopEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
500 if (!location->stopEvent)
502 WLog_ERR(TAG,
"CreateEvent failed!");
503 return ERROR_INTERNAL_ERROR;
506 location->thread = CreateThread(NULL, 0, location_server_thread_func, location, 0, NULL);
507 if (!location->thread)
509 WLog_ERR(TAG,
"CreateThread failed!");
510 (void)CloseHandle(location->stopEvent);
511 location->stopEvent = NULL;
512 return ERROR_INTERNAL_ERROR;
515 location->isOpened = TRUE;
517 return CHANNEL_RC_OK;
520static UINT location_server_close(LocationServerContext* context)
522 UINT error = CHANNEL_RC_OK;
523 location_server* location = (location_server*)context;
525 WINPR_ASSERT(location);
527 if (!location->externalThread && location->thread)
529 (void)SetEvent(location->stopEvent);
531 if (WaitForSingleObject(location->thread, INFINITE) == WAIT_FAILED)
533 error = GetLastError();
534 WLog_ERR(TAG,
"WaitForSingleObject failed with error %" PRIu32
"", error);
538 (void)CloseHandle(location->thread);
539 (void)CloseHandle(location->stopEvent);
540 location->thread = NULL;
541 location->stopEvent = NULL;
543 if (location->externalThread)
545 if (location->state != LOCATION_INITIAL)
547 (void)WTSVirtualChannelClose(location->location_channel);
548 location->location_channel = NULL;
549 location->state = LOCATION_INITIAL;
552 location->isOpened = FALSE;
557static UINT location_server_context_poll(LocationServerContext* context)
559 location_server* location = (location_server*)context;
561 WINPR_ASSERT(location);
563 if (!location->externalThread)
564 return ERROR_INTERNAL_ERROR;
566 return location_server_context_poll_int(context);
569static BOOL location_server_context_handle(LocationServerContext* context, HANDLE* handle)
571 location_server* location = (location_server*)context;
573 WINPR_ASSERT(location);
574 WINPR_ASSERT(handle);
576 if (!location->externalThread)
578 if (location->state == LOCATION_INITIAL)
581 *handle = location_server_get_channel_handle(location);
586static UINT location_server_packet_send(LocationServerContext* context,
wStream* s)
588 location_server* location = (location_server*)context;
589 UINT error = CHANNEL_RC_OK;
592 WINPR_ASSERT(location);
595 const size_t pos = Stream_GetPosition(s);
596 WINPR_ASSERT(pos <= UINT32_MAX);
597 if (!WTSVirtualChannelWrite(location->location_channel, Stream_BufferAs(s,
char), (ULONG)pos,
600 WLog_ERR(TAG,
"WTSVirtualChannelWrite failed!");
601 error = ERROR_INTERNAL_ERROR;
605 if (written < Stream_GetPosition(s))
607 WLog_WARN(TAG,
"Unexpected bytes written: %" PRIu32
"/%" PRIuz
"", written,
608 Stream_GetPosition(s));
612 Stream_Free(s, TRUE);
616static UINT location_server_send_server_ready(LocationServerContext* context,
620 UINT32 pduLength = 0;
621 UINT32 protocolVersion = 0;
623 WINPR_ASSERT(context);
624 WINPR_ASSERT(serverReady);
626 protocolVersion = serverReady->protocolVersion;
628 pduLength = LOCATION_HEADER_SIZE + 4 + 4;
630 s = Stream_New(NULL, pduLength);
633 WLog_ERR(TAG,
"Stream_New failed!");
634 return ERROR_NOT_ENOUGH_MEMORY;
638 Stream_Write_UINT16(s, PDUTYPE_SERVER_READY);
639 Stream_Write_UINT32(s, pduLength);
641 Stream_Write_UINT32(s, protocolVersion);
642 Stream_Write_UINT32(s, serverReady->flags);
644 return location_server_packet_send(context, s);
647LocationServerContext* location_server_context_new(HANDLE vcm)
649 location_server* location = (location_server*)calloc(1,
sizeof(location_server));
654 location->context.vcm = vcm;
655 location->context.Initialize = location_server_initialize;
656 location->context.Open = location_server_open;
657 location->context.Close = location_server_close;
658 location->context.Poll = location_server_context_poll;
659 location->context.ChannelHandle = location_server_context_handle;
661 location->context.ServerReady = location_server_send_server_ready;
663 location->buffer = Stream_New(NULL, 4096);
664 if (!location->buffer)
667 return &location->context;
669 WINPR_PRAGMA_DIAG_PUSH
670 WINPR_PRAGMA_DIAG_IGNORED_MISMATCHED_DEALLOC
671 location_server_context_free(&location->context);
672 WINPR_PRAGMA_DIAG_POP
676void location_server_context_free(LocationServerContext* context)
678 location_server* location = (location_server*)context;
682 location_server_close(context);
683 Stream_Free(location->buffer, TRUE);