server: Add channel handling for location channel ([MS-RDPEL])

With the location channel, an RDP server can redirect the location of
the user from the client side to the server side.
The PDUs are almost the same as in the documentation, except for the
encoded types, these ones are here already parsed.
Optional values are accessed via pointers. If the pointer of a value is
NULL, then that value was not supplied by the client side.
This commit is contained in:
Pascal Nowack 2023-10-16 11:03:12 +02:00 committed by akallabeth
parent 5ec5be7ac4
commit aac8945fe9
8 changed files with 963 additions and 2 deletions

View File

@ -0,0 +1,22 @@
# FreeRDP: A Remote Desktop Protocol Implementation
# FreeRDP cmake build script
#
# Copyright 2023 Pascal Nowack <Pascal.Nowack@gmx.de>
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
define_channel("location")
if(WITH_SERVER_CHANNELS)
add_channel_server(${MODULE_PREFIX} ${CHANNEL_NAME})
endif()

View File

@ -0,0 +1,12 @@
set(OPTION_DEFAULT ON)
set(OPTION_CLIENT_DEFAULT OFF)
set(OPTION_SERVER_DEFAULT ON)
define_channel_options(NAME "location" TYPE "dynamic"
DESCRIPTION "Location Virtual Channel Extension"
SPECIFICATIONS "[MS-RDPEL]"
DEFAULT ${OPTION_DEFAULT})
define_channel_server_options(${OPTION_SERVER_DEFAULT})

View File

@ -0,0 +1,28 @@
# FreeRDP: A Remote Desktop Protocol Implementation
# FreeRDP cmake build script
#
# Copyright 2023 Pascal Nowack <Pascal.Nowack@gmx.de>
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
define_channel_server("location")
set(${MODULE_PREFIX}_SRCS
location_main.c
)
set(${MODULE_PREFIX}_LIBS
freerdp
)
add_channel_server_library(${MODULE_PREFIX} ${MODULE_NAME} ${CHANNEL_NAME} FALSE "DVCPluginEntry")

View File

@ -0,0 +1,631 @@
/**
* FreeRDP: A Remote Desktop Protocol Implementation
* Location Virtual Channel Extension
*
* Copyright 2023 Pascal Nowack <Pascal.Nowack@gmx.de>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <freerdp/config.h>
#include <freerdp/freerdp.h>
#include <freerdp/channels/log.h>
#include <freerdp/server/location.h>
#include <freerdp/utils/encoded_types.h>
#define TAG CHANNELS_TAG("location.server")
typedef enum
{
LOCATION_INITIAL,
LOCATION_OPENED,
} eLocationChannelState;
typedef struct
{
LocationServerContext context;
HANDLE stopEvent;
HANDLE thread;
void* location_channel;
DWORD SessionId;
BOOL isOpened;
BOOL externalThread;
/* Channel state */
eLocationChannelState state;
wStream* buffer;
} location_server;
static UINT location_server_initialize(LocationServerContext* context, BOOL externalThread)
{
UINT error = CHANNEL_RC_OK;
location_server* location = (location_server*)context;
WINPR_ASSERT(location);
if (location->isOpened)
{
WLog_WARN(TAG, "Application error: Location channel already initialized, "
"calling in this state is not possible!");
return ERROR_INVALID_STATE;
}
location->externalThread = externalThread;
return error;
}
static UINT location_server_open_channel(location_server* location)
{
LocationServerContext* context = &location->context;
DWORD Error = ERROR_SUCCESS;
HANDLE hEvent;
DWORD BytesReturned = 0;
PULONG pSessionId = NULL;
UINT32 channelId;
BOOL status = TRUE;
WINPR_ASSERT(location);
if (WTSQuerySessionInformationA(location->context.vcm, WTS_CURRENT_SESSION, WTSSessionId,
(LPSTR*)&pSessionId, &BytesReturned) == FALSE)
{
WLog_ERR(TAG, "WTSQuerySessionInformationA failed!");
return ERROR_INTERNAL_ERROR;
}
location->SessionId = (DWORD)*pSessionId;
WTSFreeMemory(pSessionId);
hEvent = WTSVirtualChannelManagerGetEventHandle(location->context.vcm);
if (WaitForSingleObject(hEvent, 1000) == WAIT_FAILED)
{
Error = GetLastError();
WLog_ERR(TAG, "WaitForSingleObject failed with error %" PRIu32 "!", Error);
return Error;
}
location->location_channel = WTSVirtualChannelOpenEx(
location->SessionId, LOCATION_DVC_CHANNEL_NAME, WTS_CHANNEL_OPTION_DYNAMIC);
if (!location->location_channel)
{
Error = GetLastError();
WLog_ERR(TAG, "WTSVirtualChannelOpenEx failed with error %" PRIu32 "!", Error);
return Error;
}
channelId = WTSChannelGetIdByHandle(location->location_channel);
IFCALLRET(context->ChannelIdAssigned, status, context, channelId);
if (!status)
{
WLog_ERR(TAG, "context->ChannelIdAssigned failed!");
return ERROR_INTERNAL_ERROR;
}
return Error;
}
static UINT location_server_recv_client_ready(LocationServerContext* context, wStream* s,
const RDPLOCATION_HEADER* header)
{
RDPLOCATION_CLIENT_READY_PDU pdu = { 0 };
UINT error = CHANNEL_RC_OK;
WINPR_ASSERT(context);
WINPR_ASSERT(s);
WINPR_ASSERT(header);
pdu.header = *header;
if (!Stream_CheckAndLogRequiredLength(TAG, s, 4))
return ERROR_NO_DATA;
Stream_Read_UINT32(s, pdu.protocolVersion);
if (Stream_GetRemainingLength(s) >= 4)
Stream_Read_UINT32(s, pdu.flags);
IFCALLRET(context->ClientReady, error, context, &pdu);
if (error)
WLog_ERR(TAG, "context->ClientReady failed with error %" PRIu32 "", error);
return error;
}
static UINT location_server_recv_base_location3d(LocationServerContext* context, wStream* s,
const RDPLOCATION_HEADER* header)
{
RDPLOCATION_BASE_LOCATION3D_PDU pdu = { 0 };
UINT error = CHANNEL_RC_OK;
double speed = 0.0;
double heading = 0.0;
double horizontalAccuracy = 0.0;
LOCATIONSOURCE source = 0;
WINPR_ASSERT(context);
WINPR_ASSERT(s);
WINPR_ASSERT(header);
pdu.header = *header;
if (!freerdp_read_four_byte_float(s, &pdu.latitude) ||
!freerdp_read_four_byte_float(s, &pdu.longitude) ||
!freerdp_read_four_byte_signed_integer(s, &pdu.altitude))
return FALSE;
if (Stream_GetRemainingLength(s) >= 1)
{
if (!freerdp_read_four_byte_float(s, &speed) ||
!freerdp_read_four_byte_float(s, &heading) ||
!freerdp_read_four_byte_float(s, &horizontalAccuracy) ||
!Stream_CheckAndLogRequiredLength(TAG, s, 1))
return FALSE;
Stream_Read_UINT8(s, source);
pdu.speed = &speed;
pdu.heading = &heading;
pdu.horizontalAccuracy = &horizontalAccuracy;
pdu.source = &source;
}
IFCALLRET(context->BaseLocation3D, error, context, &pdu);
if (error)
WLog_ERR(TAG, "context->BaseLocation3D failed with error %" PRIu32 "", error);
return error;
}
static UINT location_server_recv_location2d_delta(LocationServerContext* context, wStream* s,
const RDPLOCATION_HEADER* header)
{
RDPLOCATION_LOCATION2D_DELTA_PDU pdu = { 0 };
UINT error = CHANNEL_RC_OK;
double speedDelta = 0.0;
double headingDelta = 0.0;
WINPR_ASSERT(context);
WINPR_ASSERT(s);
WINPR_ASSERT(header);
pdu.header = *header;
if (!freerdp_read_four_byte_float(s, &pdu.latitudeDelta) ||
!freerdp_read_four_byte_float(s, &pdu.longitudeDelta))
return FALSE;
if (Stream_GetRemainingLength(s) >= 1)
{
if (!freerdp_read_four_byte_float(s, &speedDelta) ||
!freerdp_read_four_byte_float(s, &headingDelta))
return FALSE;
pdu.speedDelta = &speedDelta;
pdu.headingDelta = &headingDelta;
}
IFCALLRET(context->Location2DDelta, error, context, &pdu);
if (error)
WLog_ERR(TAG, "context->Location2DDelta failed with error %" PRIu32 "", error);
return error;
}
static UINT location_server_recv_location3d_delta(LocationServerContext* context, wStream* s,
const RDPLOCATION_HEADER* header)
{
RDPLOCATION_LOCATION3D_DELTA_PDU pdu = { 0 };
UINT error = CHANNEL_RC_OK;
double speedDelta = 0.0;
double headingDelta = 0.0;
WINPR_ASSERT(context);
WINPR_ASSERT(s);
WINPR_ASSERT(header);
pdu.header = *header;
if (!freerdp_read_four_byte_float(s, &pdu.latitudeDelta) ||
!freerdp_read_four_byte_float(s, &pdu.longitudeDelta) ||
!freerdp_read_four_byte_signed_integer(s, &pdu.altitudeDelta))
return FALSE;
if (Stream_GetRemainingLength(s) >= 1)
{
if (!freerdp_read_four_byte_float(s, &speedDelta) ||
!freerdp_read_four_byte_float(s, &headingDelta))
return FALSE;
pdu.speedDelta = &speedDelta;
pdu.headingDelta = &headingDelta;
}
IFCALLRET(context->Location3DDelta, error, context, &pdu);
if (error)
WLog_ERR(TAG, "context->Location3DDelta failed with error %" PRIu32 "", error);
return error;
}
static UINT location_process_message(location_server* location)
{
BOOL rc;
UINT error = ERROR_INTERNAL_ERROR;
ULONG BytesReturned;
RDPLOCATION_HEADER header = { 0 };
wStream* s;
WINPR_ASSERT(location);
WINPR_ASSERT(location->location_channel);
s = location->buffer;
WINPR_ASSERT(s);
Stream_SetPosition(s, 0);
rc = WTSVirtualChannelRead(location->location_channel, 0, NULL, 0, &BytesReturned);
if (!rc)
goto out;
if (BytesReturned < 1)
{
error = CHANNEL_RC_OK;
goto out;
}
if (!Stream_EnsureRemainingCapacity(s, BytesReturned))
{
WLog_ERR(TAG, "Stream_EnsureRemainingCapacity failed!");
error = CHANNEL_RC_NO_MEMORY;
goto out;
}
if (WTSVirtualChannelRead(location->location_channel, 0, (PCHAR)Stream_Buffer(s),
(ULONG)Stream_Capacity(s), &BytesReturned) == FALSE)
{
WLog_ERR(TAG, "WTSVirtualChannelRead failed!");
goto out;
}
Stream_SetLength(s, BytesReturned);
if (!Stream_CheckAndLogRequiredLength(TAG, s, LOCATION_HEADER_SIZE))
return ERROR_NO_DATA;
Stream_Read_UINT16(s, header.pduType);
Stream_Read_UINT32(s, header.pduLength);
switch (header.pduType)
{
case PDUTYPE_CLIENT_READY:
error = location_server_recv_client_ready(&location->context, s, &header);
break;
case PDUTYPE_BASE_LOCATION3D:
error = location_server_recv_base_location3d(&location->context, s, &header);
break;
case PDUTYPE_LOCATION2D_DELTA:
error = location_server_recv_location2d_delta(&location->context, s, &header);
break;
case PDUTYPE_LOCATION3D_DELTA:
error = location_server_recv_location3d_delta(&location->context, s, &header);
break;
default:
WLog_ERR(TAG, "location_process_message: unknown or invalid pduType %" PRIu8 "",
header.pduType);
break;
}
out:
if (error)
WLog_ERR(TAG, "Response failed with error %" PRIu32 "!", error);
return error;
}
static UINT location_server_context_poll_int(LocationServerContext* context)
{
location_server* location = (location_server*)context;
UINT error = ERROR_INTERNAL_ERROR;
WINPR_ASSERT(location);
switch (location->state)
{
case LOCATION_INITIAL:
error = location_server_open_channel(location);
if (error)
WLog_ERR(TAG, "location_server_open_channel failed with error %" PRIu32 "!", error);
else
location->state = LOCATION_OPENED;
break;
case LOCATION_OPENED:
error = location_process_message(location);
break;
}
return error;
}
static HANDLE location_server_get_channel_handle(location_server* location)
{
void* buffer = NULL;
DWORD BytesReturned = 0;
HANDLE ChannelEvent = NULL;
WINPR_ASSERT(location);
if (WTSVirtualChannelQuery(location->location_channel, WTSVirtualEventHandle, &buffer,
&BytesReturned) == TRUE)
{
if (BytesReturned == sizeof(HANDLE))
CopyMemory(&ChannelEvent, buffer, sizeof(HANDLE));
WTSFreeMemory(buffer);
}
return ChannelEvent;
}
static DWORD WINAPI location_server_thread_func(LPVOID arg)
{
DWORD nCount;
HANDLE events[2] = { 0 };
location_server* location = (location_server*)arg;
UINT error = CHANNEL_RC_OK;
DWORD status;
WINPR_ASSERT(location);
nCount = 0;
events[nCount++] = location->stopEvent;
while ((error == CHANNEL_RC_OK) && (WaitForSingleObject(events[0], 0) != WAIT_OBJECT_0))
{
switch (location->state)
{
case LOCATION_INITIAL:
error = location_server_context_poll_int(&location->context);
if (error == CHANNEL_RC_OK)
{
events[1] = location_server_get_channel_handle(location);
nCount = 2;
}
break;
case LOCATION_OPENED:
status = WaitForMultipleObjects(nCount, events, FALSE, INFINITE);
switch (status)
{
case WAIT_OBJECT_0:
break;
case WAIT_OBJECT_0 + 1:
case WAIT_TIMEOUT:
error = location_server_context_poll_int(&location->context);
break;
case WAIT_FAILED:
default:
error = ERROR_INTERNAL_ERROR;
break;
}
break;
}
}
WTSVirtualChannelClose(location->location_channel);
location->location_channel = NULL;
if (error && location->context.rdpcontext)
setChannelError(location->context.rdpcontext, error,
"location_server_thread_func reported an error");
ExitThread(error);
return error;
}
static UINT location_server_open(LocationServerContext* context)
{
location_server* location = (location_server*)context;
WINPR_ASSERT(location);
if (!location->externalThread && (location->thread == NULL))
{
location->stopEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
if (!location->stopEvent)
{
WLog_ERR(TAG, "CreateEvent failed!");
return ERROR_INTERNAL_ERROR;
}
location->thread = CreateThread(NULL, 0, location_server_thread_func, location, 0, NULL);
if (!location->thread)
{
WLog_ERR(TAG, "CreateThread failed!");
CloseHandle(location->stopEvent);
location->stopEvent = NULL;
return ERROR_INTERNAL_ERROR;
}
}
location->isOpened = TRUE;
return CHANNEL_RC_OK;
}
static UINT location_server_close(LocationServerContext* context)
{
UINT error = CHANNEL_RC_OK;
location_server* location = (location_server*)context;
WINPR_ASSERT(location);
if (!location->externalThread && location->thread)
{
SetEvent(location->stopEvent);
if (WaitForSingleObject(location->thread, INFINITE) == WAIT_FAILED)
{
error = GetLastError();
WLog_ERR(TAG, "WaitForSingleObject failed with error %" PRIu32 "", error);
return error;
}
CloseHandle(location->thread);
CloseHandle(location->stopEvent);
location->thread = NULL;
location->stopEvent = NULL;
}
if (location->externalThread)
{
if (location->state != LOCATION_INITIAL)
{
WTSVirtualChannelClose(location->location_channel);
location->location_channel = NULL;
location->state = LOCATION_INITIAL;
}
}
location->isOpened = FALSE;
return error;
}
static UINT location_server_context_poll(LocationServerContext* context)
{
location_server* location = (location_server*)context;
WINPR_ASSERT(location);
if (!location->externalThread)
return ERROR_INTERNAL_ERROR;
return location_server_context_poll_int(context);
}
static BOOL location_server_context_handle(LocationServerContext* context, HANDLE* handle)
{
location_server* location = (location_server*)context;
WINPR_ASSERT(location);
WINPR_ASSERT(handle);
if (!location->externalThread)
return FALSE;
if (location->state == LOCATION_INITIAL)
return FALSE;
*handle = location_server_get_channel_handle(location);
return TRUE;
}
static UINT location_server_packet_send(LocationServerContext* context, wStream* s)
{
location_server* location = (location_server*)context;
UINT error = CHANNEL_RC_OK;
ULONG written;
WINPR_ASSERT(location);
WINPR_ASSERT(s);
if (!WTSVirtualChannelWrite(location->location_channel, (PCHAR)Stream_Buffer(s),
Stream_GetPosition(s), &written))
{
WLog_ERR(TAG, "WTSVirtualChannelWrite failed!");
error = ERROR_INTERNAL_ERROR;
goto out;
}
if (written < Stream_GetPosition(s))
{
WLog_WARN(TAG, "Unexpected bytes written: %" PRIu32 "/%" PRIuz "", written,
Stream_GetPosition(s));
}
out:
Stream_Free(s, TRUE);
return error;
}
static UINT location_server_send_server_ready(LocationServerContext* context,
const RDPLOCATION_SERVER_READY_PDU* serverReady)
{
wStream* s;
UINT32 pduLength;
UINT32 protocolVersion;
WINPR_ASSERT(context);
WINPR_ASSERT(serverReady);
protocolVersion = serverReady->protocolVersion;
pduLength = LOCATION_HEADER_SIZE + 4 + 4;
s = Stream_New(NULL, pduLength);
if (!s)
{
WLog_ERR(TAG, "Stream_New failed!");
return ERROR_NOT_ENOUGH_MEMORY;
}
/* RDPLOCATION_HEADER */
Stream_Write_UINT16(s, PDUTYPE_SERVER_READY);
Stream_Write_UINT32(s, pduLength);
Stream_Write_UINT32(s, protocolVersion);
Stream_Write_UINT32(s, serverReady->flags);
return location_server_packet_send(context, s);
}
LocationServerContext* location_server_context_new(HANDLE vcm)
{
location_server* location = (location_server*)calloc(1, sizeof(location_server));
if (!location)
return NULL;
location->context.vcm = vcm;
location->context.Initialize = location_server_initialize;
location->context.Open = location_server_open;
location->context.Close = location_server_close;
location->context.Poll = location_server_context_poll;
location->context.ChannelHandle = location_server_context_handle;
location->context.ServerReady = location_server_send_server_ready;
location->buffer = Stream_New(NULL, 4096);
if (!location->buffer)
goto fail;
return &location->context;
fail:
location_server_context_free(&location->context);
return NULL;
}
void location_server_context_free(LocationServerContext* context)
{
location_server* location = (location_server*)context;
if (location)
{
location_server_close(context);
Stream_Free(location->buffer, TRUE);
}
free(location);
}

View File

@ -59,6 +59,10 @@
#include <freerdp/server/rdpecam.h>
#endif
#if defined(CHANNEL_LOCATION_SERVER)
#include <freerdp/server/location.h>
#endif /* CHANNEL_LOCATION_SERVER */
#ifdef WITH_CHANNEL_GFXREDIR
#include <freerdp/server/gfxredir.h>
#endif /* WITH_CHANNEL_GFXREDIR */
@ -86,10 +90,13 @@ void freerdp_channels_dummy(void)
TelemetryServerContext* telemetry;
RdpgfxServerContext* rdpgfx;
DispServerContext* disp;
#if defined (CHANNEL_RDPECAM_SERVER)
#if defined(CHANNEL_RDPECAM_SERVER)
CamDevEnumServerContext* camera_enumerator;
CameraDeviceServerContext* camera_device;
#endif
#if defined(CHANNEL_LOCATION_SERVER)
LocationServerContext* location;
#endif /* CHANNEL_LOCATION_SERVER */
#ifdef WITH_CHANNEL_GFXREDIR
GfxRedirServerContext* gfxredir;
#endif // WITH_CHANNEL_GFXREDIR
@ -124,13 +131,18 @@ void freerdp_channels_dummy(void)
disp = disp_server_context_new(NULL);
disp_server_context_free(disp);
#if defined (CHANNEL_RDPECAM_SERVER)
#if defined(CHANNEL_RDPECAM_SERVER)
camera_enumerator = cam_dev_enum_server_context_new(NULL);
cam_dev_enum_server_context_free(camera_enumerator);
camera_device = camera_device_server_context_new(NULL);
camera_device_server_context_free(camera_device);
#endif
#if defined(CHANNEL_LOCATION_SERVER)
location = location_server_context_new(NULL);
location_server_context_free(location);
#endif /* CHANNEL_LOCATION_SERVER */
#ifdef WITH_CHANNEL_GFXREDIR
gfxredir = gfxredir_server_context_new(NULL);
gfxredir_server_context_free(gfxredir);

View File

@ -90,6 +90,9 @@
#cmakedefine CHANNEL_GFXREDIR
#cmakedefine CHANNEL_GFXREDIR_CLIENT
#cmakedefine CHANNEL_GFXREDIR_SERVER
#cmakedefine CHANNEL_LOCATION
#cmakedefine CHANNEL_LOCATION_CLIENT
#cmakedefine CHANNEL_LOCATION_SERVER
#cmakedefine CHANNEL_PARALLEL
#cmakedefine CHANNEL_PARALLEL_CLIENT
#cmakedefine CHANNEL_PARALLEL_SERVER

View File

@ -0,0 +1,113 @@
/**
* FreeRDP: A Remote Desktop Protocol Implementation
* Location Virtual Channel Extension
*
* Copyright 2022 Pascal Nowack <Pascal.Nowack@gmx.de>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef FREERDP_CHANNEL_LOCATION_H
#define FREERDP_CHANNEL_LOCATION_H
#include <freerdp/api.h>
#include <freerdp/dvc.h>
#include <freerdp/types.h>
#define LOCATION_DVC_CHANNEL_NAME "Microsoft::Windows::RDS::Location"
#ifdef __cplusplus
extern "C"
{
#endif
typedef enum
{
PDUTYPE_SERVER_READY = 0x0001,
PDUTYPE_CLIENT_READY = 0x0002,
PDUTYPE_BASE_LOCATION3D = 0x0003,
PDUTYPE_LOCATION2D_DELTA = 0x0004,
PDUTYPE_LOCATION3D_DELTA = 0x0005,
} LOCATION_PDUTYPE;
#define LOCATION_HEADER_SIZE 6
typedef struct
{
LOCATION_PDUTYPE pduType;
UINT32 pduLength;
} RDPLOCATION_HEADER;
typedef enum
{
RDPLOCATION_PROTOCOL_VERSION_100 = 0x00010000,
RDPLOCATION_PROTOCOL_VERSION_200 = 0x00020000,
} RDPLOCATION_PROTOCOL_VERSION;
typedef struct
{
RDPLOCATION_HEADER header;
RDPLOCATION_PROTOCOL_VERSION protocolVersion;
UINT32 flags;
} RDPLOCATION_SERVER_READY_PDU;
typedef struct
{
RDPLOCATION_HEADER header;
RDPLOCATION_PROTOCOL_VERSION protocolVersion;
UINT32 flags;
} RDPLOCATION_CLIENT_READY_PDU;
typedef enum
{
LOCATIONSOURCE_IP = 0x00,
LOCATIONSOURCE_WIFI = 0x01,
LOCATIONSOURCE_CELL = 0x02,
LOCATIONSOURCE_GNSS = 0x03,
} LOCATIONSOURCE;
typedef struct
{
RDPLOCATION_HEADER header;
double latitude;
double longitude;
INT32 altitude;
double* speed;
double* heading;
double* horizontalAccuracy;
LOCATIONSOURCE* source;
} RDPLOCATION_BASE_LOCATION3D_PDU;
typedef struct
{
RDPLOCATION_HEADER header;
double latitudeDelta;
double longitudeDelta;
double* speedDelta;
double* headingDelta;
} RDPLOCATION_LOCATION2D_DELTA_PDU;
typedef struct
{
RDPLOCATION_HEADER header;
double latitudeDelta;
double longitudeDelta;
INT32 altitudeDelta;
double* speedDelta;
double* headingDelta;
} RDPLOCATION_LOCATION3D_DELTA_PDU;
#ifdef __cplusplus
}
#endif
#endif /* FREERDP_CHANNEL_LOCATION_H */

View File

@ -0,0 +1,140 @@
/**
* FreeRDP: A Remote Desktop Protocol Implementation
* Location Virtual Channel Extension
*
* Copyright 2023 Pascal Nowack <Pascal.Nowack@gmx.de>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef FREERDP_CHANNEL_LOCATION_SERVER_LOCATION_H
#define FREERDP_CHANNEL_LOCATION_SERVER_LOCATION_H
#include <freerdp/channels/location.h>
#include <freerdp/channels/wtsvc.h>
#ifdef __cplusplus
extern "C"
{
#endif
typedef struct s_location_server_context LocationServerContext;
typedef UINT (*psLocationServerOpen)(LocationServerContext* context);
typedef UINT (*psLocationServerClose)(LocationServerContext* context);
typedef BOOL (*psLocationServerChannelIdAssigned)(LocationServerContext* context,
UINT32 channelId);
typedef UINT (*psLocationServerInitialize)(LocationServerContext* context, BOOL externalThread);
typedef UINT (*psLocationServerPoll)(LocationServerContext* context);
typedef BOOL (*psLocationServerChannelHandle)(LocationServerContext* context, HANDLE* handle);
typedef UINT (*psLocationServerServerReady)(LocationServerContext* context,
const RDPLOCATION_SERVER_READY_PDU* serverReady);
typedef UINT (*psLocationServerClientReady)(LocationServerContext* context,
const RDPLOCATION_CLIENT_READY_PDU* clientReady);
typedef UINT (*psLocationServerBaseLocation3D)(
LocationServerContext* context, const RDPLOCATION_BASE_LOCATION3D_PDU* baseLocation3D);
typedef UINT (*psLocationServerLocation2DDelta)(
LocationServerContext* context, const RDPLOCATION_LOCATION2D_DELTA_PDU* location2DDelta);
typedef UINT (*psLocationServerLocation3DDelta)(
LocationServerContext* context, const RDPLOCATION_LOCATION3D_DELTA_PDU* location3DDelta);
struct s_location_server_context
{
HANDLE vcm;
/* Server self-defined pointer. */
void* userdata;
/*** APIs called by the server. ***/
/**
* Optional: Set thread handling.
* When externalThread=TRUE, the application is responsible to call
* Poll() periodically to process channel events.
*
* Defaults to externalThread=FALSE
*/
psLocationServerInitialize Initialize;
/**
* Open the location channel.
*/
psLocationServerOpen Open;
/**
* Close the location channel.
*/
psLocationServerClose Close;
/**
* Poll
* When externalThread=TRUE, call Poll() periodically from your main loop.
* If externalThread=FALSE do not call.
*/
psLocationServerPoll Poll;
/**
* Retrieve the channel handle for use in conjunction with Poll().
* If externalThread=FALSE do not call.
*/
psLocationServerChannelHandle ChannelHandle;
/* All PDUs sent by the server don't require the header to be set */
/*
* Send a ServerReady PDU.
*/
psLocationServerServerReady ServerReady;
/*** Callbacks registered by the server. ***/
/**
* Callback, when the channel got its id assigned.
*/
psLocationServerChannelIdAssigned ChannelIdAssigned;
/**
* Callback for the ClientReady PDU.
*/
psLocationServerClientReady ClientReady;
/**
* Callback for the BaseLocation3D PDU.
*/
psLocationServerBaseLocation3D BaseLocation3D;
/**
* Callback for the Location2DDelta PDU.
*/
psLocationServerLocation2DDelta Location2DDelta;
/**
* Callback for the Location3DDelta PDU.
*/
psLocationServerLocation3DDelta Location3DDelta;
rdpContext* rdpcontext;
};
FREERDP_API LocationServerContext* location_server_context_new(HANDLE vcm);
FREERDP_API void location_server_context_free(LocationServerContext* context);
#ifdef __cplusplus
}
#endif
#endif /* FREERDP_CHANNEL_LOCATION_SERVER_LOCATION_H */