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;
55 static 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;
74 static 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;
125 static 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;
140 Stream_Read_UINT32(s, pdu.protocolVersion);
142 if (Stream_GetRemainingLength(s) >= 4)
143 Stream_Read_UINT32(s, pdu.flags);
145 IFCALLRET(context->ClientReady, error, context, &pdu);
147 WLog_ERR(TAG,
"context->ClientReady failed with error %" PRIu32
"", error);
152 static UINT location_server_recv_base_location3d(LocationServerContext* context,
wStream* s,
156 UINT error = CHANNEL_RC_OK;
158 double heading = 0.0;
159 double horizontalAccuracy = 0.0;
160 LOCATIONSOURCE source = 0;
162 WINPR_ASSERT(context);
164 WINPR_ASSERT(header);
166 pdu.header = *header;
168 if (!freerdp_read_four_byte_float(s, &pdu.latitude) ||
169 !freerdp_read_four_byte_float(s, &pdu.longitude) ||
170 !freerdp_read_four_byte_signed_integer(s, &pdu.altitude))
173 if (Stream_GetRemainingLength(s) >= 1)
175 if (!freerdp_read_four_byte_float(s, &speed) ||
176 !freerdp_read_four_byte_float(s, &heading) ||
177 !freerdp_read_four_byte_float(s, &horizontalAccuracy) ||
178 !Stream_CheckAndLogRequiredLength(TAG, s, 1))
181 Stream_Read_UINT8(s, source);
184 pdu.heading = &heading;
185 pdu.horizontalAccuracy = &horizontalAccuracy;
186 pdu.source = &source;
189 IFCALLRET(context->BaseLocation3D, error, context, &pdu);
191 WLog_ERR(TAG,
"context->BaseLocation3D failed with error %" PRIu32
"", error);
196 static UINT location_server_recv_location2d_delta(LocationServerContext* context,
wStream* s,
200 UINT error = CHANNEL_RC_OK;
201 double speedDelta = 0.0;
202 double headingDelta = 0.0;
204 WINPR_ASSERT(context);
206 WINPR_ASSERT(header);
208 pdu.header = *header;
210 if (!freerdp_read_four_byte_float(s, &pdu.latitudeDelta) ||
211 !freerdp_read_four_byte_float(s, &pdu.longitudeDelta))
214 if (Stream_GetRemainingLength(s) >= 1)
216 if (!freerdp_read_four_byte_float(s, &speedDelta) ||
217 !freerdp_read_four_byte_float(s, &headingDelta))
220 pdu.speedDelta = &speedDelta;
221 pdu.headingDelta = &headingDelta;
224 IFCALLRET(context->Location2DDelta, error, context, &pdu);
226 WLog_ERR(TAG,
"context->Location2DDelta failed with error %" PRIu32
"", error);
231 static UINT location_server_recv_location3d_delta(LocationServerContext* context,
wStream* s,
235 UINT error = CHANNEL_RC_OK;
236 double speedDelta = 0.0;
237 double headingDelta = 0.0;
239 WINPR_ASSERT(context);
241 WINPR_ASSERT(header);
243 pdu.header = *header;
245 if (!freerdp_read_four_byte_float(s, &pdu.latitudeDelta) ||
246 !freerdp_read_four_byte_float(s, &pdu.longitudeDelta) ||
247 !freerdp_read_four_byte_signed_integer(s, &pdu.altitudeDelta))
250 if (Stream_GetRemainingLength(s) >= 1)
252 if (!freerdp_read_four_byte_float(s, &speedDelta) ||
253 !freerdp_read_four_byte_float(s, &headingDelta))
256 pdu.speedDelta = &speedDelta;
257 pdu.headingDelta = &headingDelta;
260 IFCALLRET(context->Location3DDelta, error, context, &pdu);
262 WLog_ERR(TAG,
"context->Location3DDelta failed with error %" PRIu32
"", error);
267 static UINT location_process_message(location_server* location)
270 UINT error = ERROR_INTERNAL_ERROR;
271 ULONG BytesReturned = 0;
275 WINPR_ASSERT(location);
276 WINPR_ASSERT(location->location_channel);
278 s = location->buffer;
281 Stream_SetPosition(s, 0);
282 rc = WTSVirtualChannelRead(location->location_channel, 0, NULL, 0, &BytesReturned);
286 if (BytesReturned < 1)
288 error = CHANNEL_RC_OK;
292 if (!Stream_EnsureRemainingCapacity(s, BytesReturned))
294 WLog_ERR(TAG,
"Stream_EnsureRemainingCapacity failed!");
295 error = CHANNEL_RC_NO_MEMORY;
299 if (WTSVirtualChannelRead(location->location_channel, 0, Stream_BufferAs(s,
char),
300 (ULONG)Stream_Capacity(s), &BytesReturned) == FALSE)
302 WLog_ERR(TAG,
"WTSVirtualChannelRead failed!");
306 Stream_SetLength(s, BytesReturned);
307 if (!Stream_CheckAndLogRequiredLength(TAG, s, LOCATION_HEADER_SIZE))
308 return ERROR_NO_DATA;
310 Stream_Read_UINT16(s, header.pduType);
311 Stream_Read_UINT32(s, header.pduLength);
313 switch (header.pduType)
315 case PDUTYPE_CLIENT_READY:
316 error = location_server_recv_client_ready(&location->context, s, &header);
318 case PDUTYPE_BASE_LOCATION3D:
319 error = location_server_recv_base_location3d(&location->context, s, &header);
321 case PDUTYPE_LOCATION2D_DELTA:
322 error = location_server_recv_location2d_delta(&location->context, s, &header);
324 case PDUTYPE_LOCATION3D_DELTA:
325 error = location_server_recv_location3d_delta(&location->context, s, &header);
328 WLog_ERR(TAG,
"location_process_message: unknown or invalid pduType %" PRIu8
"",
335 WLog_ERR(TAG,
"Response failed with error %" PRIu32
"!", error);
340 static UINT location_server_context_poll_int(LocationServerContext* context)
342 location_server* location = (location_server*)context;
343 UINT error = ERROR_INTERNAL_ERROR;
345 WINPR_ASSERT(location);
347 switch (location->state)
349 case LOCATION_INITIAL:
350 error = location_server_open_channel(location);
352 WLog_ERR(TAG,
"location_server_open_channel failed with error %" PRIu32
"!", error);
354 location->state = LOCATION_OPENED;
356 case LOCATION_OPENED:
357 error = location_process_message(location);
366 static HANDLE location_server_get_channel_handle(location_server* location)
369 DWORD BytesReturned = 0;
370 HANDLE ChannelEvent = NULL;
372 WINPR_ASSERT(location);
374 if (WTSVirtualChannelQuery(location->location_channel, WTSVirtualEventHandle, &buffer,
375 &BytesReturned) == TRUE)
377 if (BytesReturned ==
sizeof(HANDLE))
378 CopyMemory(&ChannelEvent, buffer,
sizeof(HANDLE));
380 WTSFreeMemory(buffer);
386 static DWORD WINAPI location_server_thread_func(LPVOID arg)
389 HANDLE events[2] = { 0 };
390 location_server* location = (location_server*)arg;
391 UINT error = CHANNEL_RC_OK;
394 WINPR_ASSERT(location);
397 events[nCount++] = location->stopEvent;
399 while ((error == CHANNEL_RC_OK) && (WaitForSingleObject(events[0], 0) != WAIT_OBJECT_0))
401 switch (location->state)
403 case LOCATION_INITIAL:
404 error = location_server_context_poll_int(&location->context);
405 if (error == CHANNEL_RC_OK)
407 events[1] = location_server_get_channel_handle(location);
411 case LOCATION_OPENED:
412 status = WaitForMultipleObjects(nCount, events, FALSE, INFINITE);
417 case WAIT_OBJECT_0 + 1:
419 error = location_server_context_poll_int(&location->context);
424 error = ERROR_INTERNAL_ERROR;
433 (void)WTSVirtualChannelClose(location->location_channel);
434 location->location_channel = NULL;
436 if (error && location->context.rdpcontext)
437 setChannelError(location->context.rdpcontext, error,
438 "location_server_thread_func reported an error");
444 static UINT location_server_open(LocationServerContext* context)
446 location_server* location = (location_server*)context;
448 WINPR_ASSERT(location);
450 if (!location->externalThread && (location->thread == NULL))
452 location->stopEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
453 if (!location->stopEvent)
455 WLog_ERR(TAG,
"CreateEvent failed!");
456 return ERROR_INTERNAL_ERROR;
459 location->thread = CreateThread(NULL, 0, location_server_thread_func, location, 0, NULL);
460 if (!location->thread)
462 WLog_ERR(TAG,
"CreateThread failed!");
463 (void)CloseHandle(location->stopEvent);
464 location->stopEvent = NULL;
465 return ERROR_INTERNAL_ERROR;
468 location->isOpened = TRUE;
470 return CHANNEL_RC_OK;
473 static UINT location_server_close(LocationServerContext* context)
475 UINT error = CHANNEL_RC_OK;
476 location_server* location = (location_server*)context;
478 WINPR_ASSERT(location);
480 if (!location->externalThread && location->thread)
482 (void)SetEvent(location->stopEvent);
484 if (WaitForSingleObject(location->thread, INFINITE) == WAIT_FAILED)
486 error = GetLastError();
487 WLog_ERR(TAG,
"WaitForSingleObject failed with error %" PRIu32
"", error);
491 (void)CloseHandle(location->thread);
492 (void)CloseHandle(location->stopEvent);
493 location->thread = NULL;
494 location->stopEvent = NULL;
496 if (location->externalThread)
498 if (location->state != LOCATION_INITIAL)
500 (void)WTSVirtualChannelClose(location->location_channel);
501 location->location_channel = NULL;
502 location->state = LOCATION_INITIAL;
505 location->isOpened = FALSE;
510 static UINT location_server_context_poll(LocationServerContext* context)
512 location_server* location = (location_server*)context;
514 WINPR_ASSERT(location);
516 if (!location->externalThread)
517 return ERROR_INTERNAL_ERROR;
519 return location_server_context_poll_int(context);
522 static BOOL location_server_context_handle(LocationServerContext* context, HANDLE* handle)
524 location_server* location = (location_server*)context;
526 WINPR_ASSERT(location);
527 WINPR_ASSERT(handle);
529 if (!location->externalThread)
531 if (location->state == LOCATION_INITIAL)
534 *handle = location_server_get_channel_handle(location);
539 static UINT location_server_packet_send(LocationServerContext* context,
wStream* s)
541 location_server* location = (location_server*)context;
542 UINT error = CHANNEL_RC_OK;
545 WINPR_ASSERT(location);
548 const size_t pos = Stream_GetPosition(s);
549 WINPR_ASSERT(pos <= UINT32_MAX);
550 if (!WTSVirtualChannelWrite(location->location_channel, Stream_BufferAs(s,
char), (ULONG)pos,
553 WLog_ERR(TAG,
"WTSVirtualChannelWrite failed!");
554 error = ERROR_INTERNAL_ERROR;
558 if (written < Stream_GetPosition(s))
560 WLog_WARN(TAG,
"Unexpected bytes written: %" PRIu32
"/%" PRIuz
"", written,
561 Stream_GetPosition(s));
565 Stream_Free(s, TRUE);
569 static UINT location_server_send_server_ready(LocationServerContext* context,
573 UINT32 pduLength = 0;
574 UINT32 protocolVersion = 0;
576 WINPR_ASSERT(context);
577 WINPR_ASSERT(serverReady);
579 protocolVersion = serverReady->protocolVersion;
581 pduLength = LOCATION_HEADER_SIZE + 4 + 4;
583 s = Stream_New(NULL, pduLength);
586 WLog_ERR(TAG,
"Stream_New failed!");
587 return ERROR_NOT_ENOUGH_MEMORY;
591 Stream_Write_UINT16(s, PDUTYPE_SERVER_READY);
592 Stream_Write_UINT32(s, pduLength);
594 Stream_Write_UINT32(s, protocolVersion);
595 Stream_Write_UINT32(s, serverReady->flags);
597 return location_server_packet_send(context, s);
600 LocationServerContext* location_server_context_new(HANDLE vcm)
602 location_server* location = (location_server*)calloc(1,
sizeof(location_server));
607 location->context.vcm = vcm;
608 location->context.Initialize = location_server_initialize;
609 location->context.Open = location_server_open;
610 location->context.Close = location_server_close;
611 location->context.Poll = location_server_context_poll;
612 location->context.ChannelHandle = location_server_context_handle;
614 location->context.ServerReady = location_server_send_server_ready;
616 location->buffer = Stream_New(NULL, 4096);
617 if (!location->buffer)
620 return &location->context;
622 WINPR_PRAGMA_DIAG_PUSH
623 WINPR_PRAGMA_DIAG_IGNORED_MISMATCHED_DEALLOC
624 location_server_context_free(&location->context);
625 WINPR_PRAGMA_DIAG_POP
629 void location_server_context_free(LocationServerContext* context)
631 location_server* location = (location_server*)context;
635 location_server_close(context);
636 Stream_Free(location->buffer, TRUE);