20#include <freerdp/config.h>
22#include <freerdp/freerdp.h>
23#include <freerdp/channels/log.h>
24#include <freerdp/server/location.h>
25#include <freerdp/utils/encoded_types.h>
27#define TAG CHANNELS_TAG("location.server")
33} eLocationChannelState;
37 LocationServerContext context;
42 void* location_channel;
50 eLocationChannelState state;
55static UINT location_server_initialize(LocationServerContext* context, BOOL externalThread)
57 UINT error = CHANNEL_RC_OK;
58 location_server* location = (location_server*)context;
60 WINPR_ASSERT(location);
62 if (location->isOpened)
64 WLog_WARN(TAG,
"Application error: Location channel already initialized, "
65 "calling in this state is not possible!");
66 return ERROR_INVALID_STATE;
69 location->externalThread = externalThread;
74static UINT location_server_open_channel(location_server* location)
76 LocationServerContext* context = &location->context;
77 DWORD Error = ERROR_SUCCESS;
79 DWORD BytesReturned = 0;
80 PULONG pSessionId = NULL;
84 WINPR_ASSERT(location);
86 if (WTSQuerySessionInformationA(location->context.vcm, WTS_CURRENT_SESSION, WTSSessionId,
87 (LPSTR*)&pSessionId, &BytesReturned) == FALSE)
89 WLog_ERR(TAG,
"WTSQuerySessionInformationA failed!");
90 return ERROR_INTERNAL_ERROR;
93 location->SessionId = (DWORD)*pSessionId;
94 WTSFreeMemory(pSessionId);
95 hEvent = WTSVirtualChannelManagerGetEventHandle(location->context.vcm);
97 if (WaitForSingleObject(hEvent, 1000) == WAIT_FAILED)
99 Error = GetLastError();
100 WLog_ERR(TAG,
"WaitForSingleObject failed with error %" PRIu32
"!", Error);
104 location->location_channel = WTSVirtualChannelOpenEx(
105 location->SessionId, LOCATION_DVC_CHANNEL_NAME, WTS_CHANNEL_OPTION_DYNAMIC);
106 if (!location->location_channel)
108 Error = GetLastError();
109 WLog_ERR(TAG,
"WTSVirtualChannelOpenEx failed with error %" PRIu32
"!", Error);
113 channelId = WTSChannelGetIdByHandle(location->location_channel);
115 IFCALLRET(context->ChannelIdAssigned, status, context, channelId);
118 WLog_ERR(TAG,
"context->ChannelIdAssigned failed!");
119 return ERROR_INTERNAL_ERROR;
125static UINT location_server_recv_client_ready(LocationServerContext* context,
wStream* s,
129 UINT error = CHANNEL_RC_OK;
131 WINPR_ASSERT(context);
133 WINPR_ASSERT(header);
135 pdu.header = *header;
137 if (!Stream_CheckAndLogRequiredLength(TAG, s, 4))
138 return ERROR_NO_DATA;
141 const UINT32 version = Stream_Get_UINT32(s);
144 case RDPLOCATION_PROTOCOL_VERSION_100:
145 pdu.protocolVersion = RDPLOCATION_PROTOCOL_VERSION_100;
147 case RDPLOCATION_PROTOCOL_VERSION_200:
148 pdu.protocolVersion = RDPLOCATION_PROTOCOL_VERSION_200;
151 pdu.protocolVersion = RDPLOCATION_PROTOCOL_VERSION_200;
153 "Received unsupported protocol version %" PRIu32
154 ", setting to highest supported %u",
155 version, pdu.protocolVersion);
160 if (Stream_GetRemainingLength(s) >= 4)
161 Stream_Read_UINT32(s, pdu.flags);
163 IFCALLRET(context->ClientReady, error, context, &pdu);
165 WLog_ERR(TAG,
"context->ClientReady failed with error %" PRIu32
"", error);
170static UINT location_server_recv_base_location3d(LocationServerContext* context,
wStream* s,
174 UINT error = CHANNEL_RC_OK;
176 double heading = 0.0;
177 double horizontalAccuracy = 0.0;
178 LOCATIONSOURCE source = LOCATIONSOURCE_IP;
180 WINPR_ASSERT(context);
182 WINPR_ASSERT(header);
184 pdu.header = *header;
186 if (!freerdp_read_four_byte_float(s, &pdu.latitude) ||
187 !freerdp_read_four_byte_float(s, &pdu.longitude) ||
188 !freerdp_read_four_byte_signed_integer(s, &pdu.altitude))
191 if (Stream_GetRemainingLength(s) >= 1)
193 if (!freerdp_read_four_byte_float(s, &speed) ||
194 !freerdp_read_four_byte_float(s, &heading) ||
195 !freerdp_read_four_byte_float(s, &horizontalAccuracy) ||
196 !Stream_CheckAndLogRequiredLength(TAG, s, 1))
200 const UINT8 src = Stream_Get_UINT8(s);
203 case LOCATIONSOURCE_IP:
204 case LOCATIONSOURCE_WIFI:
205 case LOCATIONSOURCE_CELL:
206 case LOCATIONSOURCE_GNSS:
209 WLog_ERR(TAG,
"Invalid LOCATIONSOURCE value %" PRIu8
"", src);
212 source = (LOCATIONSOURCE)src;
216 pdu.heading = &heading;
217 pdu.horizontalAccuracy = &horizontalAccuracy;
218 pdu.source = &source;
221 IFCALLRET(context->BaseLocation3D, error, context, &pdu);
223 WLog_ERR(TAG,
"context->BaseLocation3D failed with error %" PRIu32
"", error);
228static UINT location_server_recv_location2d_delta(LocationServerContext* context,
wStream* s,
232 UINT error = CHANNEL_RC_OK;
233 double speedDelta = 0.0;
234 double headingDelta = 0.0;
236 WINPR_ASSERT(context);
238 WINPR_ASSERT(header);
240 pdu.header = *header;
242 if (!freerdp_read_four_byte_float(s, &pdu.latitudeDelta) ||
243 !freerdp_read_four_byte_float(s, &pdu.longitudeDelta))
246 if (Stream_GetRemainingLength(s) >= 1)
248 if (!freerdp_read_four_byte_float(s, &speedDelta) ||
249 !freerdp_read_four_byte_float(s, &headingDelta))
252 pdu.speedDelta = &speedDelta;
253 pdu.headingDelta = &headingDelta;
256 IFCALLRET(context->Location2DDelta, error, context, &pdu);
258 WLog_ERR(TAG,
"context->Location2DDelta failed with error %" PRIu32
"", error);
263static UINT location_server_recv_location3d_delta(LocationServerContext* context,
wStream* s,
267 UINT error = CHANNEL_RC_OK;
268 double speedDelta = 0.0;
269 double headingDelta = 0.0;
271 WINPR_ASSERT(context);
273 WINPR_ASSERT(header);
275 pdu.header = *header;
277 if (!freerdp_read_four_byte_float(s, &pdu.latitudeDelta) ||
278 !freerdp_read_four_byte_float(s, &pdu.longitudeDelta) ||
279 !freerdp_read_four_byte_signed_integer(s, &pdu.altitudeDelta))
282 if (Stream_GetRemainingLength(s) >= 1)
284 if (!freerdp_read_four_byte_float(s, &speedDelta) ||
285 !freerdp_read_four_byte_float(s, &headingDelta))
288 pdu.speedDelta = &speedDelta;
289 pdu.headingDelta = &headingDelta;
292 IFCALLRET(context->Location3DDelta, error, context, &pdu);
294 WLog_ERR(TAG,
"context->Location3DDelta failed with error %" PRIu32
"", error);
299static UINT location_process_message(location_server* location)
302 UINT error = ERROR_INTERNAL_ERROR;
303 ULONG BytesReturned = 0;
307 WINPR_ASSERT(location);
308 WINPR_ASSERT(location->location_channel);
310 s = location->buffer;
313 Stream_SetPosition(s, 0);
314 rc = WTSVirtualChannelRead(location->location_channel, 0, NULL, 0, &BytesReturned);
318 if (BytesReturned < 1)
320 error = CHANNEL_RC_OK;
324 if (!Stream_EnsureRemainingCapacity(s, BytesReturned))
326 WLog_ERR(TAG,
"Stream_EnsureRemainingCapacity failed!");
327 error = CHANNEL_RC_NO_MEMORY;
331 if (WTSVirtualChannelRead(location->location_channel, 0, Stream_BufferAs(s,
char),
332 (ULONG)Stream_Capacity(s), &BytesReturned) == FALSE)
334 WLog_ERR(TAG,
"WTSVirtualChannelRead failed!");
338 Stream_SetLength(s, BytesReturned);
339 if (!Stream_CheckAndLogRequiredLength(TAG, s, LOCATION_HEADER_SIZE))
340 return ERROR_NO_DATA;
343 const UINT16 pduType = Stream_Get_UINT16(s);
344 header.pduType = (LOCATION_PDUTYPE)pduType;
345 header.pduLength = Stream_Get_UINT32(s);
349 case PDUTYPE_CLIENT_READY:
350 error = location_server_recv_client_ready(&location->context, s, &header);
352 case PDUTYPE_BASE_LOCATION3D:
353 error = location_server_recv_base_location3d(&location->context, s, &header);
355 case PDUTYPE_LOCATION2D_DELTA:
356 error = location_server_recv_location2d_delta(&location->context, s, &header);
358 case PDUTYPE_LOCATION3D_DELTA:
359 error = location_server_recv_location3d_delta(&location->context, s, &header);
362 WLog_ERR(TAG,
"location_process_message: unknown or invalid pduType %" PRIu16
"",
370 WLog_ERR(TAG,
"Response failed with error %" PRIu32
"!", error);
375static UINT location_server_context_poll_int(LocationServerContext* context)
377 location_server* location = (location_server*)context;
378 UINT error = ERROR_INTERNAL_ERROR;
380 WINPR_ASSERT(location);
382 switch (location->state)
384 case LOCATION_INITIAL:
385 error = location_server_open_channel(location);
387 WLog_ERR(TAG,
"location_server_open_channel failed with error %" PRIu32
"!", error);
389 location->state = LOCATION_OPENED;
391 case LOCATION_OPENED:
392 error = location_process_message(location);
401static HANDLE location_server_get_channel_handle(location_server* location)
404 DWORD BytesReturned = 0;
405 HANDLE ChannelEvent = NULL;
407 WINPR_ASSERT(location);
409 if (WTSVirtualChannelQuery(location->location_channel, WTSVirtualEventHandle, &buffer,
410 &BytesReturned) == TRUE)
412 if (BytesReturned ==
sizeof(HANDLE))
413 ChannelEvent = *(HANDLE*)buffer;
415 WTSFreeMemory(buffer);
421static DWORD WINAPI location_server_thread_func(LPVOID arg)
424 HANDLE events[2] = { 0 };
425 location_server* location = (location_server*)arg;
426 UINT error = CHANNEL_RC_OK;
429 WINPR_ASSERT(location);
432 events[nCount++] = location->stopEvent;
434 while ((error == CHANNEL_RC_OK) && (WaitForSingleObject(events[0], 0) != WAIT_OBJECT_0))
436 switch (location->state)
438 case LOCATION_INITIAL:
439 error = location_server_context_poll_int(&location->context);
440 if (error == CHANNEL_RC_OK)
442 events[1] = location_server_get_channel_handle(location);
446 case LOCATION_OPENED:
447 status = WaitForMultipleObjects(nCount, events, FALSE, INFINITE);
452 case WAIT_OBJECT_0 + 1:
454 error = location_server_context_poll_int(&location->context);
459 error = ERROR_INTERNAL_ERROR;
468 (void)WTSVirtualChannelClose(location->location_channel);
469 location->location_channel = NULL;
471 if (error && location->context.rdpcontext)
472 setChannelError(location->context.rdpcontext, error,
473 "location_server_thread_func reported an error");
479static UINT location_server_open(LocationServerContext* context)
481 location_server* location = (location_server*)context;
483 WINPR_ASSERT(location);
485 if (!location->externalThread && (location->thread == NULL))
487 location->stopEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
488 if (!location->stopEvent)
490 WLog_ERR(TAG,
"CreateEvent failed!");
491 return ERROR_INTERNAL_ERROR;
494 location->thread = CreateThread(NULL, 0, location_server_thread_func, location, 0, NULL);
495 if (!location->thread)
497 WLog_ERR(TAG,
"CreateThread failed!");
498 (void)CloseHandle(location->stopEvent);
499 location->stopEvent = NULL;
500 return ERROR_INTERNAL_ERROR;
503 location->isOpened = TRUE;
505 return CHANNEL_RC_OK;
508static UINT location_server_close(LocationServerContext* context)
510 UINT error = CHANNEL_RC_OK;
511 location_server* location = (location_server*)context;
513 WINPR_ASSERT(location);
515 if (!location->externalThread && location->thread)
517 (void)SetEvent(location->stopEvent);
519 if (WaitForSingleObject(location->thread, INFINITE) == WAIT_FAILED)
521 error = GetLastError();
522 WLog_ERR(TAG,
"WaitForSingleObject failed with error %" PRIu32
"", error);
526 (void)CloseHandle(location->thread);
527 (void)CloseHandle(location->stopEvent);
528 location->thread = NULL;
529 location->stopEvent = NULL;
531 if (location->externalThread)
533 if (location->state != LOCATION_INITIAL)
535 (void)WTSVirtualChannelClose(location->location_channel);
536 location->location_channel = NULL;
537 location->state = LOCATION_INITIAL;
540 location->isOpened = FALSE;
545static UINT location_server_context_poll(LocationServerContext* context)
547 location_server* location = (location_server*)context;
549 WINPR_ASSERT(location);
551 if (!location->externalThread)
552 return ERROR_INTERNAL_ERROR;
554 return location_server_context_poll_int(context);
557static BOOL location_server_context_handle(LocationServerContext* context, HANDLE* handle)
559 location_server* location = (location_server*)context;
561 WINPR_ASSERT(location);
562 WINPR_ASSERT(handle);
564 if (!location->externalThread)
566 if (location->state == LOCATION_INITIAL)
569 *handle = location_server_get_channel_handle(location);
574static UINT location_server_packet_send(LocationServerContext* context,
wStream* s)
576 location_server* location = (location_server*)context;
577 UINT error = CHANNEL_RC_OK;
580 WINPR_ASSERT(location);
583 const size_t pos = Stream_GetPosition(s);
584 WINPR_ASSERT(pos <= UINT32_MAX);
585 if (!WTSVirtualChannelWrite(location->location_channel, Stream_BufferAs(s,
char), (ULONG)pos,
588 WLog_ERR(TAG,
"WTSVirtualChannelWrite failed!");
589 error = ERROR_INTERNAL_ERROR;
593 if (written < Stream_GetPosition(s))
595 WLog_WARN(TAG,
"Unexpected bytes written: %" PRIu32
"/%" PRIuz
"", written,
596 Stream_GetPosition(s));
600 Stream_Free(s, TRUE);
604static UINT location_server_send_server_ready(LocationServerContext* context,
608 UINT32 pduLength = 0;
609 UINT32 protocolVersion = 0;
611 WINPR_ASSERT(context);
612 WINPR_ASSERT(serverReady);
614 protocolVersion = serverReady->protocolVersion;
616 pduLength = LOCATION_HEADER_SIZE + 4 + 4;
618 s = Stream_New(NULL, pduLength);
621 WLog_ERR(TAG,
"Stream_New failed!");
622 return ERROR_NOT_ENOUGH_MEMORY;
626 Stream_Write_UINT16(s, PDUTYPE_SERVER_READY);
627 Stream_Write_UINT32(s, pduLength);
629 Stream_Write_UINT32(s, protocolVersion);
630 Stream_Write_UINT32(s, serverReady->flags);
632 return location_server_packet_send(context, s);
635LocationServerContext* location_server_context_new(HANDLE vcm)
637 location_server* location = (location_server*)calloc(1,
sizeof(location_server));
642 location->context.vcm = vcm;
643 location->context.Initialize = location_server_initialize;
644 location->context.Open = location_server_open;
645 location->context.Close = location_server_close;
646 location->context.Poll = location_server_context_poll;
647 location->context.ChannelHandle = location_server_context_handle;
649 location->context.ServerReady = location_server_send_server_ready;
651 location->buffer = Stream_New(NULL, 4096);
652 if (!location->buffer)
655 return &location->context;
657 WINPR_PRAGMA_DIAG_PUSH
658 WINPR_PRAGMA_DIAG_IGNORED_MISMATCHED_DEALLOC
659 location_server_context_free(&location->context);
660 WINPR_PRAGMA_DIAG_POP
664void location_server_context_free(LocationServerContext* context)
666 location_server* location = (location_server*)context;
670 location_server_close(context);
671 Stream_Free(location->buffer, TRUE);