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;
80 HANDLE hEvent =
nullptr;
81 DWORD BytesReturned = 0;
82 PULONG pSessionId =
nullptr;
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 =
nullptr,
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,
251 .speedDelta =
nullptr,
252 .headingDelta =
nullptr };
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,
289 .speedDelta =
nullptr,
290 .headingDelta =
nullptr };
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_ResetPosition(s);
327 WTSVirtualChannelRead(location->location_channel, 0,
nullptr, 0, &BytesReturned);
331 if (BytesReturned < 1)
333 error = CHANNEL_RC_OK;
337 if (!Stream_EnsureRemainingCapacity(s, BytesReturned))
339 WLog_ERR(TAG,
"Stream_EnsureRemainingCapacity failed!");
340 error = CHANNEL_RC_NO_MEMORY;
344 if (WTSVirtualChannelRead(location->location_channel, 0, Stream_BufferAs(s,
char),
345 (ULONG)Stream_Capacity(s), &BytesReturned) == FALSE)
347 WLog_ERR(TAG,
"WTSVirtualChannelRead failed!");
351 if (!Stream_SetLength(s, BytesReturned))
354 if (!Stream_CheckAndLogRequiredLength(TAG, s, LOCATION_HEADER_SIZE))
355 return ERROR_NO_DATA;
358 const UINT16 pduType = Stream_Get_UINT16(s);
360 .pduLength = Stream_Get_UINT32(s) };
364 case PDUTYPE_CLIENT_READY:
365 error = location_server_recv_client_ready(&location->context, s, &header);
367 case PDUTYPE_BASE_LOCATION3D:
368 error = location_server_recv_base_location3d(&location->context, s, &header);
370 case PDUTYPE_LOCATION2D_DELTA:
371 error = location_server_recv_location2d_delta(&location->context, s, &header);
373 case PDUTYPE_LOCATION3D_DELTA:
374 error = location_server_recv_location3d_delta(&location->context, s, &header);
377 WLog_ERR(TAG,
"location_process_message: unknown or invalid pduType %" PRIu16
"",
385 WLog_ERR(TAG,
"Response failed with error %" PRIu32
"!", error);
390static UINT location_server_context_poll_int(LocationServerContext* context)
392 location_server* location = (location_server*)context;
393 UINT error = ERROR_INTERNAL_ERROR;
395 WINPR_ASSERT(location);
397 switch (location->state)
399 case LOCATION_INITIAL:
400 error = location_server_open_channel(location);
402 WLog_ERR(TAG,
"location_server_open_channel failed with error %" PRIu32
"!", error);
404 location->state = LOCATION_OPENED;
406 case LOCATION_OPENED:
407 error = location_process_message(location);
416static HANDLE location_server_get_channel_handle(location_server* location)
418 void* buffer =
nullptr;
419 DWORD BytesReturned = 0;
420 HANDLE ChannelEvent =
nullptr;
422 WINPR_ASSERT(location);
424 if (WTSVirtualChannelQuery(location->location_channel, WTSVirtualEventHandle, &buffer,
425 &BytesReturned) == TRUE)
427 if (BytesReturned ==
sizeof(HANDLE))
428 ChannelEvent = *(HANDLE*)buffer;
430 WTSFreeMemory(buffer);
436static DWORD WINAPI location_server_thread_func(LPVOID arg)
439 HANDLE events[2] = WINPR_C_ARRAY_INIT;
440 location_server* location = (location_server*)arg;
441 UINT error = CHANNEL_RC_OK;
444 WINPR_ASSERT(location);
447 events[nCount++] = location->stopEvent;
449 while ((error == CHANNEL_RC_OK) && (WaitForSingleObject(events[0], 0) != WAIT_OBJECT_0))
451 switch (location->state)
453 case LOCATION_INITIAL:
454 error = location_server_context_poll_int(&location->context);
455 if (error == CHANNEL_RC_OK)
457 events[1] = location_server_get_channel_handle(location);
461 case LOCATION_OPENED:
462 status = WaitForMultipleObjects(nCount, events, FALSE, INFINITE);
467 case WAIT_OBJECT_0 + 1:
469 error = location_server_context_poll_int(&location->context);
474 error = ERROR_INTERNAL_ERROR;
483 (void)WTSVirtualChannelClose(location->location_channel);
484 location->location_channel =
nullptr;
486 if (error && location->context.rdpcontext)
487 setChannelError(location->context.rdpcontext, error,
488 "location_server_thread_func reported an error");
494static UINT location_server_open(LocationServerContext* context)
496 location_server* location = (location_server*)context;
498 WINPR_ASSERT(location);
500 if (!location->externalThread && (location->thread ==
nullptr))
502 location->stopEvent = CreateEvent(
nullptr, TRUE, FALSE,
nullptr);
503 if (!location->stopEvent)
505 WLog_ERR(TAG,
"CreateEvent failed!");
506 return ERROR_INTERNAL_ERROR;
510 CreateThread(
nullptr, 0, location_server_thread_func, location, 0,
nullptr);
511 if (!location->thread)
513 WLog_ERR(TAG,
"CreateThread failed!");
514 (void)CloseHandle(location->stopEvent);
515 location->stopEvent =
nullptr;
516 return ERROR_INTERNAL_ERROR;
519 location->isOpened = TRUE;
521 return CHANNEL_RC_OK;
524static UINT location_server_close(LocationServerContext* context)
526 UINT error = CHANNEL_RC_OK;
527 location_server* location = (location_server*)context;
529 WINPR_ASSERT(location);
531 if (!location->externalThread && location->thread)
533 (void)SetEvent(location->stopEvent);
535 if (WaitForSingleObject(location->thread, INFINITE) == WAIT_FAILED)
537 error = GetLastError();
538 WLog_ERR(TAG,
"WaitForSingleObject failed with error %" PRIu32
"", error);
542 (void)CloseHandle(location->thread);
543 (void)CloseHandle(location->stopEvent);
544 location->thread =
nullptr;
545 location->stopEvent =
nullptr;
547 if (location->externalThread)
549 if (location->state != LOCATION_INITIAL)
551 (void)WTSVirtualChannelClose(location->location_channel);
552 location->location_channel =
nullptr;
553 location->state = LOCATION_INITIAL;
556 location->isOpened = FALSE;
561static UINT location_server_context_poll(LocationServerContext* context)
563 location_server* location = (location_server*)context;
565 WINPR_ASSERT(location);
567 if (!location->externalThread)
568 return ERROR_INTERNAL_ERROR;
570 return location_server_context_poll_int(context);
573static BOOL location_server_context_handle(LocationServerContext* context, HANDLE* handle)
575 location_server* location = (location_server*)context;
577 WINPR_ASSERT(location);
578 WINPR_ASSERT(handle);
580 if (!location->externalThread)
582 if (location->state == LOCATION_INITIAL)
585 *handle = location_server_get_channel_handle(location);
590static UINT location_server_packet_send(LocationServerContext* context,
wStream* s)
592 location_server* location = (location_server*)context;
593 UINT error = CHANNEL_RC_OK;
596 WINPR_ASSERT(location);
599 const size_t pos = Stream_GetPosition(s);
600 WINPR_ASSERT(pos <= UINT32_MAX);
601 if (!WTSVirtualChannelWrite(location->location_channel, Stream_BufferAs(s,
char), (ULONG)pos,
604 WLog_ERR(TAG,
"WTSVirtualChannelWrite failed!");
605 error = ERROR_INTERNAL_ERROR;
609 if (written < Stream_GetPosition(s))
611 WLog_WARN(TAG,
"Unexpected bytes written: %" PRIu32
"/%" PRIuz
"", written,
612 Stream_GetPosition(s));
616 Stream_Free(s, TRUE);
620static UINT location_server_send_server_ready(LocationServerContext* context,
624 UINT32 pduLength = 0;
625 UINT32 protocolVersion = 0;
627 WINPR_ASSERT(context);
628 WINPR_ASSERT(serverReady);
630 protocolVersion = serverReady->protocolVersion;
632 pduLength = LOCATION_HEADER_SIZE + 4 + 4;
634 s = Stream_New(
nullptr, pduLength);
637 WLog_ERR(TAG,
"Stream_New failed!");
638 return ERROR_NOT_ENOUGH_MEMORY;
642 Stream_Write_UINT16(s, PDUTYPE_SERVER_READY);
643 Stream_Write_UINT32(s, pduLength);
645 Stream_Write_UINT32(s, protocolVersion);
646 Stream_Write_UINT32(s, serverReady->flags);
648 return location_server_packet_send(context, s);
651LocationServerContext* location_server_context_new(HANDLE vcm)
653 location_server* location = (location_server*)calloc(1,
sizeof(location_server));
658 location->context.vcm = vcm;
659 location->context.Initialize = location_server_initialize;
660 location->context.Open = location_server_open;
661 location->context.Close = location_server_close;
662 location->context.Poll = location_server_context_poll;
663 location->context.ChannelHandle = location_server_context_handle;
665 location->context.ServerReady = location_server_send_server_ready;
667 location->buffer = Stream_New(
nullptr, 4096);
668 if (!location->buffer)
671 return &location->context;
673 WINPR_PRAGMA_DIAG_PUSH
674 WINPR_PRAGMA_DIAG_IGNORED_MISMATCHED_DEALLOC
675 location_server_context_free(&location->context);
676 WINPR_PRAGMA_DIAG_POP
680void location_server_context_free(LocationServerContext* context)
682 location_server* location = (location_server*)context;
686 location_server_close(context);
687 Stream_Free(location->buffer, TRUE);